diff --git a/.env.test b/.env.test index dba8427b..ccc1f4a7 100644 --- a/.env.test +++ b/.env.test @@ -5,3 +5,5 @@ OLLAMA_URL=http://ollama:11434 REDDIT_CLIENT_ID=client REDDIT_CLIENT_SECRET=secret REDDIT_USER_AGENT=newsletter-maker/test +CELERY_BROKER_URL=memory:// +CELERY_RESULT_BACKEND=cache+memory:// diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 2c047dab..8daa4515 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -45,6 +45,12 @@ You are working in Newsletter Maker, a Django + DRF + Celery + Qdrant backend wi - TypeScript and React code should use JSDoc for exported utilities, hooks, route handlers, and non-trivial components when behavior is not obvious from the type signature alone. - If architecture or workflow behavior changes, update the most relevant docs in `docs/`, especially `docs/DEVELOPER_GUIDE.md`, `docs/IMPLEMENTATION_OVERVIEW.md`, `docs/MODELS.md`, `docs/RELEVANCE_SCORING.md`, or `docs/LOGGING.md`. +## Prompt Skill Conventions + +- Application prompt skills live under `skills//SKILL.md` and are loaded by `core/llm.py` using the folder name rather than the frontmatter `name`. +- When adding or editing one of these prompt skills, always include a short frontmatter `description` so VS Code does not report incomplete skill metadata. +- If a frontmatter `name` is present, prefer lowercase letters, numbers, and hyphens there to satisfy the Copilot markdown validator, even when the runtime skill key elsewhere in the app still uses underscores. + ## Testing And Validation - Backend tests use `pytest`. diff --git a/.github/instructions/prompt-skills.instructions.md b/.github/instructions/prompt-skills.instructions.md new file mode 100644 index 00000000..c27b619a --- /dev/null +++ b/.github/instructions/prompt-skills.instructions.md @@ -0,0 +1,15 @@ +--- +name: "Prompt Skill Markdown Guidelines" +description: "Use when editing application prompt skills in skills/**/SKILL.md. Covers custom frontmatter, runtime lookup behavior, and keeping VS Code's skill validator quiet." +applyTo: + - "skills/**/SKILL.md" +--- + +# Prompt Skill Markdown Guidelines + +- Files under `skills/**/SKILL.md` are application prompt specs loaded by `core/llm.py`, not Copilot repo skills under `.github/skills/`. +- The runtime skill key is the folder name, so keep folder names and code constants aligned with the app's expected key. +- Always include a short frontmatter `description` to avoid VS Code skill-schema warnings. +- If a frontmatter `name` is present, prefer lowercase letters, numbers, and hyphens so the Copilot validator stays quiet, even when the runtime key elsewhere in the app uses underscores. +- Preserve the repo's `input` and `output` frontmatter fields because `core/llm.py` reads them to build prompts. +- Keep the body concise and instruction-focused. Return-shape requirements should stay explicit in the markdown body. diff --git a/.vscode/settings.json b/.vscode/settings.json index a3669184..cd72ea2d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,28 +1,45 @@ { "cSpell.words": [ + "abangser", "ASGI", "botocore", + "bsky", "buildx", "cbranch", "cfgv", "cstat", + "dabde", "dateutil", "djlint", "dnspython", + "doseq", + "exif", "falsey", + "Farcic", "FAVICONS", + "favourites", + "fbclid", "Feedly", "Fraunces", + "gclid", "gunicorn", + "Hashimoto", "healthz", "HNSW", "httpx", + "Hykes", "isready", "jsbeautifier", + "LANCZOS", "libipld", "libpq", "librt", + "lnkd", "mday", + "membe", + "mipsytipsy", + "mitchellh", + "newsle", "nodeenv", "noinput", "nomic", @@ -39,14 +56,24 @@ "readyz", "recomputations", "Referer", + "repost", + "reposts", + "rollup", "scaffolder", "simplejwt", + "skillr", + "solomonstre", "svix", + "topicv", "Unparseable", "unstub", "upserted", + "upserts", "upvote", "uritemplate", + "Vercel", + "vfarcic", + "Viktor", "xrpc", "xxhash" ] diff --git a/content/migrations/0002_content_newsletter_promotion.py b/content/migrations/0002_content_newsletter_promotion.py new file mode 100644 index 00000000..8d5ae735 --- /dev/null +++ b/content/migrations/0002_content_newsletter_promotion.py @@ -0,0 +1,41 @@ +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("content", "0001_initial"), + ("trends", "0003_theme_suggestion"), + ] + + operations = [ + migrations.AddField( + model_name="content", + name="newsletter_promotion_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="content", + name="newsletter_promotion_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="newsletter_promoted_content", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="content", + name="newsletter_promotion_theme", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="promoted_contents", + to="trends.themesuggestion", + ), + ), + ] diff --git a/content/models.py b/content/models.py index 34873a5c..cda19a9c 100644 --- a/content/models.py +++ b/content/models.py @@ -47,6 +47,21 @@ class Content(models.Model): duplicate_signal_count = models.IntegerField(default=0) is_reference = models.BooleanField(default=False) is_active = models.BooleanField(default=True) + newsletter_promotion_at = models.DateTimeField(null=True, blank=True) + newsletter_promotion_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="newsletter_promoted_content", + ) + newsletter_promotion_theme = models.ForeignKey( + "trends.ThemeSuggestion", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="promoted_contents", + ) class Meta: ordering = ["-published_date"] diff --git a/content/serializers.py b/content/serializers.py index d80b3d2a..b17cf7f4 100644 --- a/content/serializers.py +++ b/content/serializers.py @@ -32,6 +32,9 @@ class Meta: "duplicate_signal_count", "is_reference", "is_active", + "newsletter_promotion_at", + "newsletter_promotion_by", + "newsletter_promotion_theme", ] read_only_fields = [ "id", @@ -42,6 +45,9 @@ class Meta: "embedding_id", "duplicate_of", "duplicate_signal_count", + "newsletter_promotion_at", + "newsletter_promotion_by", + "newsletter_promotion_theme", ] def validate(self, attrs): diff --git a/core/api.py b/core/api.py index 92274493..08d8772e 100644 --- a/core/api.py +++ b/core/api.py @@ -6,7 +6,7 @@ """ import logging -from typing import Any +from typing import Any, cast from drf_spectacular.utils import ( OpenApiExample, @@ -589,6 +589,16 @@ class ProjectOwnedQuerysetMixin: queryset: Any = None + def _kwargs(self) -> dict[str, Any]: + """Return the DRF route kwargs for typed nested-project lookups.""" + + return cast(dict[str, Any], getattr(self, "kwargs")) + + def _request(self) -> Any: + """Return the DRF request object for typed access checks.""" + + return getattr(self, "request") + def get_project(self): """Return the project referenced by ``project_id`` after access checks. @@ -597,13 +607,15 @@ def get_project(self): NotFound: If the project does not exist or the user lacks access. """ - project_id = self.kwargs.get("project_id") + project_id = self._kwargs().get("project_id") if project_id is None: raise AssertionError( "project_id must be present in nested project-scoped routes" ) try: - return get_visible_projects_queryset(self.request.user).get(pk=project_id) + return get_visible_projects_queryset(self._request().user).get( + pk=project_id + ) except Project.DoesNotExist as exc: raise NotFound("Project not found.") from exc @@ -618,7 +630,7 @@ def get_queryset(self): def get_serializer_context(self): """Inject the resolved project into serializer context.""" - context = super().get_serializer_context() + context = cast(dict[str, Any], cast(Any, super()).get_serializer_context()) context["project"] = self.get_project() return context diff --git a/core/embeddings.py b/core/embeddings.py index 39fd198b..f495f6f2 100644 --- a/core/embeddings.py +++ b/core/embeddings.py @@ -15,6 +15,7 @@ import httpx from django.conf import settings as django_settings +from django.db.models import Model from django.utils.dateparse import parse_datetime from qdrant_client import QdrantClient from qdrant_client.models import ( @@ -34,6 +35,15 @@ settings = cast(CoreSettings, django_settings) +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed Qdrant payload construction.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + def get_sentence_transformer_class(): """Lazily import and cache the sentence-transformer class. @@ -90,7 +100,10 @@ def embed_text(self, text: str) -> list[float]: def get_embedding_dimension(self) -> int: """Return the model's native embedding dimension without probing text.""" - return int(self.model.get_sentence_embedding_dimension()) + dimension = self.model.get_sentence_embedding_dimension() + if dimension is None: + raise RuntimeError("Embedding model did not report a vector dimension.") + return int(dimension) class OllamaEmbeddingProvider(EmbeddingProvider): @@ -222,18 +235,20 @@ def upsert_content_embedding(content: Content) -> str: """ client = get_qdrant_client() - ensure_project_collection(content.project_id) + project_id = _require_pk(content.project) + content_id = _require_pk(content) + ensure_project_collection(project_id) embedding_id = content.embedding_id or str(uuid4()) vector = embed_text(build_content_embedding_text(content)) client.upsert( - collection_name=collection_name_for_project(content.project_id), + collection_name=collection_name_for_project(project_id), points=[ PointStruct( id=embedding_id, vector=vector, payload={ - "content_id": content.id, - "project_id": content.project_id, + "content_id": content_id, + "project_id": project_id, "url": content.url, "title": content.title, "published_date": serialize_published_date(content.published_date), @@ -254,18 +269,20 @@ def upsert_entity_embedding(entity: Entity) -> str: """Write or update an entity embedding in the project's entity collection.""" client = get_qdrant_client() - ensure_project_entity_collection(entity.project_id) + project_id = _require_pk(entity.project) + entity_id = _require_pk(entity) + ensure_project_entity_collection(project_id) vector = embed_text(build_entity_embedding_text(entity)) - embedding_id = f"entity-{entity.id}" + embedding_id = f"entity-{entity_id}" client.upsert( - collection_name=entity_collection_name_for_project(entity.project_id), + collection_name=entity_collection_name_for_project(project_id), points=[ PointStruct( id=embedding_id, vector=vector, payload={ - "entity_id": entity.id, - "project_id": entity.project_id, + "entity_id": entity_id, + "project_id": project_id, "name": entity.name, "type": entity.type, }, @@ -343,11 +360,11 @@ def search_similar_content( """Find content similar to an existing content row within the same project.""" return search_similar( - content.project_id, + _require_pk(content.project), embed_text(build_content_embedding_text(content)), limit=limit, is_reference=is_reference, - exclude_content_id=content.id, + exclude_content_id=_require_pk(content), ) @@ -370,9 +387,10 @@ def search_similar_entities( def search_similar_entities_for_content(content: Content, limit: int = 8): """Find tracked entities whose embeddings are close to a content item.""" - sync_project_entity_embeddings(content.project_id) + project_id = _require_pk(content.project) + sync_project_entity_embeddings(project_id) return search_similar_entities( - content.project_id, + project_id, embed_text(build_content_embedding_text(content)), limit=limit, ) diff --git a/core/entity_extraction.py b/core/entity_extraction.py index 80adee6b..1e2426c0 100644 --- a/core/entity_extraction.py +++ b/core/entity_extraction.py @@ -9,6 +9,7 @@ from django.conf import settings from django.db import transaction +from django.db.models import Model from django.utils import timezone from core.embeddings import search_similar_entities_for_content @@ -70,11 +71,26 @@ NEGATIVE_TOKENS = {"breach", "bug", "failed", "failure", "outage", "risk"} +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed entity-extraction operations.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _project_pk(content: Content) -> int: + """Return the content row's owning project primary key.""" + + return _require_pk(content.project) + + def run_entity_extraction(content: Content) -> dict[str, Any]: """Extract tracked-entity mentions and surface unknown candidates.""" tracked_entities = list( - Entity.objects.filter(project_id=content.project_id).order_by("name") + Entity.objects.filter(project_id=_project_pk(content)).order_by("name") ) extraction = _run_entity_extraction_with_fallback(content, tracked_entities) normalized_mentions, unresolved_names = _normalize_mentions( @@ -116,7 +132,7 @@ def run_entity_extraction(content: Content) -> dict[str, Any]: content, normalized_candidates, is_rerun=is_rerun ) primary_entity = _select_primary_entity(mentions) - if primary_entity is not None and content.entity_id is None: + if primary_entity is not None and content.entity is None: content.entity = primary_entity content.save(update_fields=["entity"]) @@ -126,7 +142,9 @@ def run_entity_extraction(content: Content) -> dict[str, Any]: "candidate_entities": [ _serialize_candidate(candidate) for candidate in candidates ], - "primary_entity_id": primary_entity.id if primary_entity is not None else None, + "primary_entity_id": ( + _require_pk(primary_entity) if primary_entity is not None else None + ), "confidence": confidence, "explanation": extraction.get( "explanation", @@ -186,7 +204,9 @@ def persist_entity_candidates( persisted: list[EntityCandidate] = [] tracked_names = { _normalize_name(entity.name) - for entity in Entity.objects.filter(project_id=content.project_id).only("name") + for entity in Entity.objects.filter(project_id=_project_pk(content)).only( + "name" + ) } seen_names: set[str] = set() for candidate_payload in candidate_payloads: @@ -221,7 +241,7 @@ def persist_entity_candidates( if candidate.suggested_type != suggested_type: candidate.suggested_type = suggested_type update_fields.append("suggested_type") - if candidate.first_seen_in_id is None: + if candidate.first_seen_in is None: candidate.first_seen_in = content update_fields.append("first_seen_in") if not is_rerun: @@ -287,10 +307,10 @@ def backfill_entity_mentions( mention_payloads = _heuristic_mentions_for_entities( content, [entity], - extra_labels={entity.id: labels}, + extra_labels={_require_pk(entity): labels}, ) mentions = upsert_entity_mentions(content, mention_payloads) - if content.entity_id is None and any( + if content.entity is None and any( mention.role in {EntityMentionRole.SUBJECT, EntityMentionRole.AUTHOR} for mention in mentions ): @@ -318,7 +338,7 @@ def _run_entity_extraction_with_fallback( { "title": content.title, "content_text": content.content_text[:5000], - "project_id": content.project_id, + "project_id": _project_pk(content), "tracked_entities": [ _serialize_tracked_entity(entity) for entity in candidate_entities @@ -377,7 +397,7 @@ def _retrieve_candidate_entities( if not tracked_entities: return [] - entities_by_id = {entity.id: entity for entity in tracked_entities} + entities_by_id = {_require_pk(entity): entity for entity in tracked_entities} ordered_ids: list[int] = [] try: matches = search_similar_entities_for_content( @@ -394,7 +414,7 @@ def _retrieve_candidate_entities( ordered_ids.append(entity_id) exact_match_ids = { - entity.id + _require_pk(entity) for entity in tracked_entities if _find_entity_span(content, entity, extra_labels=None) is not None } @@ -547,7 +567,8 @@ def _find_entity_span( ) -> str | None: """Return the first matched label for an entity inside the content.""" - labels = extra_labels.get(entity.id, []) if extra_labels is not None else [] + entity_id = _require_pk(entity) + labels = extra_labels.get(entity_id, []) if extra_labels is not None else [] labels = [*labels, *_entity_labels(entity)] haystacks = [content.author or "", content.title or "", content.content_text or ""] for label in labels: @@ -672,7 +693,7 @@ def _serialize_mention(mention: EntityMention) -> dict[str, Any]: """Serialize a persisted mention for the skill result payload.""" return { - "entity_id": mention.entity_id, + "entity_id": _require_pk(mention.entity), "entity_name": mention.entity.name, "role": mention.role, "sentiment": mention.sentiment, @@ -685,7 +706,7 @@ def _serialize_candidate(candidate: EntityCandidate) -> dict[str, Any]: """Serialize a persisted candidate for the skill result payload.""" return { - "id": candidate.id, + "id": _require_pk(candidate), "name": candidate.name, "suggested_type": candidate.suggested_type, "occurrence_count": candidate.occurrence_count, diff --git a/core/management/commands/seed_demo.py b/core/management/commands/seed_demo.py index 0d8fadb9..478077d1 100644 --- a/core/management/commands/seed_demo.py +++ b/core/management/commands/seed_demo.py @@ -861,7 +861,7 @@ def _seed_ingestion_runs(self, project: Project) -> int: def _sync_embeddings(self, contents: list[Content]) -> int: embedded_count = 0 - for content in sorted(contents, key=lambda item: item.id): + for content in sorted(contents, key=self._require_pk): try: upsert_content_embedding(content) except (HTTPError, ResponseHandlingException) as exc: @@ -875,6 +875,14 @@ def _sync_embeddings(self, contents: list[Content]) -> int: embedded_count += 1 return embedded_count + def _require_pk(self, content: Content) -> int: + """Return a saved content primary key for deterministic ordering.""" + + content_pk = content.pk + if content_pk is None: + raise ValueError("Demo content must be saved before embedding sync.") + return int(content_pk) + def _build_reference_articles(self) -> list[dict[str, Any]]: articles = list(LEGACY_REFERENCE_ARTICLES) for round_index in range(5): diff --git a/core/models.py b/core/models.py index dc7d9b50..1addf5c4 100644 --- a/core/models.py +++ b/core/models.py @@ -27,6 +27,8 @@ from pipeline.models import SkillStatus as _SkillStatus from projects.models import Project as _Project from trends.models import ContentClusterMembership as _ContentClusterMembership +from trends.models import ThemeSuggestion as _ThemeSuggestion +from trends.models import ThemeSuggestionStatus as _ThemeSuggestionStatus from trends.models import TopicCentroidSnapshot as _TopicCentroidSnapshot from trends.models import TopicCluster as _TopicCluster from trends.models import TopicVelocitySnapshot as _TopicVelocitySnapshot @@ -38,6 +40,8 @@ SkillResult = _SkillResult SkillStatus = _SkillStatus ContentClusterMembership = _ContentClusterMembership +ThemeSuggestion = _ThemeSuggestion +ThemeSuggestionStatus = _ThemeSuggestionStatus TopicCluster = _TopicCluster TopicCentroidSnapshot = _TopicCentroidSnapshot TopicVelocitySnapshot = _TopicVelocitySnapshot @@ -60,6 +64,8 @@ "NewsletterIntakeStatus", "Project", "RunStatus", + "ThemeSuggestion", + "ThemeSuggestionStatus", "TopicCluster", "TopicCentroidSnapshot", "TopicVelocitySnapshot", diff --git a/core/permissions.py b/core/permissions.py index 77757f13..2b321d70 100644 --- a/core/permissions.py +++ b/core/permissions.py @@ -2,10 +2,26 @@ from __future__ import annotations +from typing import TYPE_CHECKING + from rest_framework import permissions from projects.models import Project, ProjectMembership, ProjectRole +if TYPE_CHECKING: + + class PermissionBase: + """Typed shim for DRF permissions whose default methods return bool.""" + + def has_permission(self, request, view) -> bool: + pass + + def has_object_permission(self, request, view, obj) -> bool: + pass + +else: + PermissionBase = permissions.BasePermission + def get_visible_projects_queryset(user): """Return the projects visible to the given authenticated user.""" @@ -34,7 +50,9 @@ def _get_project_from_view(view) -> Project | None: get_project = getattr(view, "get_project", None) if callable(get_project): - return get_project() + project = get_project() + if isinstance(project, Project): + return project return None @@ -46,7 +64,7 @@ def _resolve_project(obj) -> Project: return obj.project -class IsProjectMember(permissions.BasePermission): +class IsProjectMember(PermissionBase): """Allow authenticated project members to read project-scoped resources.""" def has_permission(self, request, view) -> bool: @@ -64,7 +82,7 @@ def has_object_permission(self, request, view, obj) -> bool: return get_user_role(request.user, _resolve_project(obj)) is not None -class IsProjectContributor(permissions.BasePermission): +class IsProjectContributor(PermissionBase): """Allow only admins and members to access contributor-only resources.""" allowed_roles = {ProjectRole.ADMIN, ProjectRole.MEMBER} @@ -84,7 +102,7 @@ def has_object_permission(self, request, view, obj) -> bool: return get_user_role(request.user, _resolve_project(obj)) in self.allowed_roles -class IsProjectMemberWritable(permissions.BasePermission): +class IsProjectMemberWritable(PermissionBase): """Allow all members to read, but reserve writes for admins and members.""" writable_roles = {ProjectRole.ADMIN, ProjectRole.MEMBER} @@ -110,7 +128,7 @@ def has_object_permission(self, request, view, obj) -> bool: return role in self.writable_roles -class IsProjectAdmin(permissions.BasePermission): +class IsProjectAdmin(PermissionBase): """Restrict access to project admins.""" def has_permission(self, request, view) -> bool: @@ -128,7 +146,7 @@ def has_object_permission(self, request, view, obj) -> bool: return get_user_role(request.user, _resolve_project(obj)) == ProjectRole.ADMIN -class IsProjectFeedbackEditor(permissions.BasePermission): +class IsProjectFeedbackEditor(PermissionBase): """Allow feedback reads to any member and writes by owners or project admins.""" contributor_roles = {ProjectRole.ADMIN, ProjectRole.MEMBER} diff --git a/core/pipeline.py b/core/pipeline.py index 0d19cae7..97015fc0 100644 --- a/core/pipeline.py +++ b/core/pipeline.py @@ -11,10 +11,10 @@ import re from datetime import timedelta from functools import lru_cache -from typing import Any, Literal, TypedDict +from typing import Any, Literal, TypedDict, cast from django.conf import settings -from django.db.models import F +from django.db.models import F, Model from django.utils import timezone from langgraph.graph import END, StateGraph @@ -29,6 +29,7 @@ ) from core.entity_extraction import run_entity_extraction from core.llm import build_skill_user_prompt, get_skill_definition, openrouter_chat_json +from entities.models import EntityMention from pipeline.models import ReviewQueue, ReviewReason, SkillResult, SkillStatus logger = logging.getLogger(__name__) @@ -73,6 +74,30 @@ class PipelineState(TypedDict, total=False): status: str +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed pipeline operations.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _project_pk(content: Content) -> int: + """Return the content row's owning project primary key.""" + + return _require_pk(content.project) + + +def _content_id_from_state(state: PipelineState) -> int: + """Extract a required content id from pipeline state.""" + + content_id = state.get("content_id") + if content_id is None: + raise ValueError("Pipeline state is missing content_id.") + return content_id + + @lru_cache(maxsize=1) def get_ingestion_graph(): """Build and cache the LangGraph workflow used for content processing. @@ -128,11 +153,11 @@ def process_content_pipeline(content_id: int) -> PipelineState: content = Content.objects.select_related("project").get(pk=content_id) initial_state: PipelineState = { - "content_id": content.id, - "project_id": content.project_id, + "content_id": _require_pk(content), + "project_id": _project_pk(content), "status": "processing", } - return get_ingestion_graph().invoke(initial_state) + return cast(PipelineState, get_ingestion_graph().invoke(initial_state)) def deduplicate_node(state: PipelineState) -> PipelineState: @@ -332,7 +357,7 @@ def run_deduplication(content: Content) -> dict[str, Any]: return { "is_duplicate": True, "canonical_url": canonical_url, - "matched_content_id": exact_duplicate.id, + "matched_content_id": _require_pk(exact_duplicate), "matched_stage": "exact", "similarity_score": None, "used_llm": False, @@ -343,9 +368,9 @@ def run_deduplication(content: Content) -> dict[str, Any]: } recent_candidates = { - candidate.id: _root_duplicate_target(candidate) + _require_pk(candidate): _root_duplicate_target(candidate) for candidate in Content.objects.filter( - project_id=content.project_id, + project_id=_project_pk(content), is_reference=False, is_active=True, published_date__gte=timezone.now() @@ -381,7 +406,7 @@ def run_deduplication(content: Content) -> dict[str, Any]: return { "is_duplicate": True, "canonical_url": canonical_url, - "matched_content_id": duplicate_target.id, + "matched_content_id": _require_pk(duplicate_target), "matched_stage": "semantic", "similarity_score": similarity, "used_llm": False, @@ -403,7 +428,7 @@ def run_deduplication(content: Content) -> dict[str, Any]: return { "is_duplicate": True, "canonical_url": canonical_url, - "matched_content_id": duplicate_target.id, + "matched_content_id": _require_pk(duplicate_target), "matched_stage": "llm", "similarity_score": similarity, "used_llm": True, @@ -471,7 +496,7 @@ def run_content_classification(content: Content) -> dict[str, Any]: except Exception: logger.exception( "Classification model call failed; falling back to heuristic classifier", - extra={"content_id": content.id}, + extra={"content_id": _require_pk(content)}, ) return _heuristic_classification(content) @@ -481,7 +506,7 @@ def _find_exact_duplicate(content: Content, canonical_url: str) -> Content | Non exact_match = ( Content.objects.filter( - project_id=content.project_id, canonical_url=canonical_url + project_id=_project_pk(content), canonical_url=canonical_url ) .exclude(pk=content.pk) .select_related("duplicate_of") @@ -492,7 +517,7 @@ def _find_exact_duplicate(content: Content, canonical_url: str) -> Content | Non return _root_duplicate_target(exact_match) for candidate in ( - Content.objects.filter(project_id=content.project_id, canonical_url="") + Content.objects.filter(project_id=_project_pk(content), canonical_url="") .exclude(pk=content.pk) .select_related("duplicate_of") .order_by("ingested_at", "id") @@ -510,7 +535,7 @@ def _root_duplicate_target(content: Content) -> Content: """Resolve a duplicate chain to its retained canonical content row.""" current = content - while current.duplicate_of_id: + while True: duplicate_of = current.duplicate_of if duplicate_of is None: break @@ -558,7 +583,10 @@ def _run_deduplication_tiebreak( except Exception: logger.exception( "Deduplication tiebreak model call failed; treating the borderline pair as distinct", - extra={"content_id": content.id, "candidate_content_id": candidate.id}, + extra={ + "content_id": _require_pk(content), + "candidate_content_id": _require_pk(candidate), + }, ) return { "is_duplicate": False, @@ -613,9 +641,9 @@ def run_relevance_scoring(content: Content) -> dict[str, Any]: """ vector = embed_text(build_content_embedding_text(content)) - reference_similarity = float(get_reference_similarity(content.project_id, vector)) + reference_similarity = float(get_reference_similarity(_project_pk(content), vector)) centroid_similarity = float( - get_topic_centroid_similarity(content.project_id, vector) + get_topic_centroid_similarity(_project_pk(content), vector) ) similarity = max(reference_similarity, centroid_similarity) if ( @@ -669,7 +697,7 @@ def run_relevance_scoring(content: Content) -> dict[str, Any]: except Exception: logger.exception( "Relevance model call failed; falling back to heuristic relevance", - extra={"content_id": content.id}, + extra={"content_id": _require_pk(content)}, ) return { @@ -721,7 +749,7 @@ def run_summarization(content: Content) -> dict[str, Any]: except Exception: logger.exception( "Summarization model call failed; falling back to heuristic summary", - extra={"content_id": content.id}, + extra={"content_id": _require_pk(content)}, ) return { "summary": _heuristic_summary(content), @@ -800,7 +828,7 @@ def execute_background_skill_result( ).get(pk=skill_result_id) if skill_result.skill_name != skill_name: raise ValueError( - f"Skill result {skill_result.id} is for {skill_result.skill_name}, not {skill_name}." + f"Skill result {_require_pk(skill_result)} is for {skill_result.skill_name}, not {skill_name}." ) _update_skill_result(skill_result, status=SkillStatus.RUNNING, error_message="") @@ -1013,10 +1041,12 @@ def _apply_authority_adjustment( def _get_primary_authority_entity(content: Content): """Choose the best entity to use for authority-aware relevance bumping.""" - if content.entity_id: + if content.entity is not None: return content.entity - mentions = list(content.entity_mentions.select_related("entity").all()) + mentions = list( + EntityMention.objects.filter(content=content).select_related("entity") + ) if not mentions: return None @@ -1149,7 +1179,9 @@ def _clamp_score(value: Any) -> float: def _get_content(state: PipelineState) -> Content: - return Content.objects.select_related("project").get(pk=state["content_id"]) + return Content.objects.select_related("project").get( + pk=_content_id_from_state(state) + ) def _upsert_review_queue_item( diff --git a/core/serializer_mixins.py b/core/serializer_mixins.py index d3ab6317..a9209547 100644 --- a/core/serializer_mixins.py +++ b/core/serializer_mixins.py @@ -1,5 +1,9 @@ """Shared DRF serializer mixins used across app-owned serializer modules.""" +from typing import Any, cast + +from rest_framework import serializers + from core.models import Content, SkillResult from core.permissions import get_visible_projects_queryset from entities.models import Entity @@ -8,35 +12,42 @@ class ProjectScopedSerializerMixin: """Limit serializer relationship fields to objects the current user can access.""" + def _serializer(self) -> serializers.Serializer: + """Return ``self`` as a DRF serializer for typed mixin access.""" + + return cast(serializers.Serializer, self) + def _filter_related_queryset(self, request): """Constrain related-field querysets using the request user and project context.""" + serializer = self._serializer() + fields = cast(dict[str, Any], serializer.fields) user = request.user - project = self.context.get("project") - if "project" in self.fields: - self.fields["project"].queryset = get_visible_projects_queryset(user) - if "entity" in self.fields: + project = serializer.context.get("project") + if "project" in fields: + fields["project"].queryset = get_visible_projects_queryset(user) + if "entity" in fields: entity_queryset = ( Entity.objects.filter(project=project) if project else Entity.objects.filter(project__memberships__user=user).distinct() ) - self.fields["entity"].queryset = entity_queryset - if "merged_into" in self.fields: + fields["entity"].queryset = entity_queryset + if "merged_into" in fields: merged_into_queryset = ( Entity.objects.filter(project=project) if project else Entity.objects.filter(project__memberships__user=user).distinct() ) - self.fields["merged_into"].queryset = merged_into_queryset - if "content" in self.fields: + fields["merged_into"].queryset = merged_into_queryset + if "content" in fields: content_queryset = ( Content.objects.filter(project=project) if project else Content.objects.filter(project__memberships__user=user).distinct() ) - self.fields["content"].queryset = content_queryset - if "superseded_by" in self.fields: + fields["content"].queryset = content_queryset + if "superseded_by" in fields: skill_result_queryset = ( SkillResult.objects.filter(project=project) if project @@ -44,12 +55,12 @@ def _filter_related_queryset(self, request): project__memberships__user=user ).distinct() ) - self.fields["superseded_by"].queryset = skill_result_queryset + fields["superseded_by"].queryset = skill_result_queryset def __init__(self, *args, **kwargs): """Initialize the serializer and scope relation fields when authenticated.""" super().__init__(*args, **kwargs) - request = self.context.get("request") + request = self._serializer().context.get("request") if request and request.user.is_authenticated: self._filter_related_queryset(request) diff --git a/core/tasks.py b/core/tasks.py index f7f59b7b..cfde1cba 100644 --- a/core/tasks.py +++ b/core/tasks.py @@ -50,6 +50,7 @@ from trends.tasks import ( TOPIC_CENTROID_MIN_UPVOTES, assign_content_to_topic_cluster, + generate_theme_suggestions, queue_topic_centroid_recompute, recompute_topic_centroid, recompute_topic_clusters, @@ -69,6 +70,10 @@ "trends.tasks", "assign_content_to_topic_cluster", ), + "generate_theme_suggestions": ( + "trends.tasks", + "generate_theme_suggestions", + ), "TOPIC_CENTROID_MIN_UPVOTES": ( "trends.tasks", "TOPIC_CENTROID_MIN_UPVOTES", @@ -94,6 +99,7 @@ "process_newsletter_intake", "run_all_ingestions", "assign_content_to_topic_cluster", + "generate_theme_suggestions", "run_ingestion", "TOPIC_CENTROID_MIN_UPVOTES", "queue_topic_centroid_recompute", diff --git a/core/tests/test_admin.py b/core/tests/test_admin.py index cd85b1f8..595eeab1 100644 --- a/core/tests/test_admin.py +++ b/core/tests/test_admin.py @@ -1,10 +1,14 @@ from datetime import timedelta from types import SimpleNamespace -from unittest.mock import ANY +from typing import Any, cast +from unittest.mock import ANY, Mock import pytest from django.contrib import messages from django.contrib.admin.sites import AdminSite +from django.db.models import Model +from django.http import HttpRequest +from django.test import RequestFactory from django.utils import timezone from content.admin import ContentAdmin, UserFeedbackAdmin @@ -56,10 +60,63 @@ pytestmark = pytest.mark.django_db +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed admin test assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _create_user(user_model: Any, **kwargs: object): + """Create a user through the custom manager with a typed escape hatch.""" + + return cast(Any, user_model.objects).create_user(**kwargs) + + +def _request(query_params: dict[str, str] | None = None) -> HttpRequest: + """Build a typed request object for admin actions and filters.""" + + return RequestFactory().get("/admin/", data=query_params or {}) + + +def _params(**kwargs: str) -> dict[str, list[str]]: + """Build typed admin filter params.""" + + return {key: [value] for key, value in kwargs.items()} + + +def _message_user_mock(admin_instance: Any, mocker: Any) -> Mock: + """Install a mock for ModelAdmin.message_user and return it for assertions.""" + + message_mock = cast(Mock, mocker.Mock()) + admin_instance.message_user = message_mock + return message_mock + + +def _context(response: object) -> dict[str, Any]: + """Cast admin changelist extra_context payloads for typed assertions.""" + + return cast(dict[str, Any], response) + + +def _dashboard_stats(response: object) -> list[dict[str, Any]]: + """Return typed dashboard stats rows from a changelist extra_context payload.""" + + return cast(list[dict[str, Any]], _context(response)["dashboard_stats"]) + + +def _drilldowns(response: object) -> list[dict[str, Any]]: + """Return typed centroid drilldowns from a changelist extra_context payload.""" + + return cast(list[dict[str, Any]], _context(response)["centroid_project_drilldowns"]) + + @pytest.fixture def source_admin_context(django_user_model): - user = django_user_model.objects.create_user( - username="admin-owner", password="testpass123" + user = _create_user( + django_user_model, username="admin-owner", password="testpass123" ) project = Project.objects.create(name="Admin Project", topic_description="Infra") return SimpleNamespace(user=user, project=project) @@ -81,10 +138,10 @@ def test_test_source_connection_reports_success(source_admin_context, mocker): "projects.admin.get_plugin_for_source_config", return_value=plugin ) admin_instance = SourceConfigAdmin(SourceConfig, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.test_source_connection( - request=SimpleNamespace(), + request=_request(), queryset=SourceConfig.objects.filter(pk=source_config.pk), ) @@ -93,7 +150,7 @@ def test_test_source_connection_reports_success(source_admin_context, mocker): ) get_plugin_mock.assert_called_once() plugin.health_check.assert_called_once_with() - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Connectivity check passed for 1 source(s).", messages.SUCCESS, @@ -107,7 +164,7 @@ def test_project_config_admin_exposes_centroid_toggle_field(source_admin_context assert "recompute_topic_centroid_on_feedback_save" in admin_instance.list_display assert "recompute_topic_centroid_on_feedback_save" in admin_instance.list_filter assert "recompute_topic_centroid_on_feedback_save" in admin_instance.get_fields( - request=SimpleNamespace(), obj=config + request=_request(), obj=config ) @@ -172,28 +229,30 @@ def test_topic_centroid_snapshot_admin_changelist_view_builds_dashboard_stats( ) mocker.patch("trends.admin.timezone.now", return_value=fixed_now) - response = admin_instance.changelist_view(request=SimpleNamespace()) + response = admin_instance.changelist_view(request=_request()) + dashboard_stats = _dashboard_stats(response) + centroid_project_drilldowns = _drilldowns(response) super_changelist_view.assert_called_once() assert ( admin_instance.list_before_template == "admin/topic_centroid_snapshot_changelist_widget.html" ) - assert response["dashboard_stats"][0]["value"] == "1 / 2" - assert response["dashboard_stats"][0]["color"] == "warning" - assert response["dashboard_stats"][1]["value"] == "10.0%" - assert response["dashboard_stats"][1]["color"] == "success" - assert response["dashboard_stats"][2]["value"] == "20.0%" - assert response["dashboard_stats"][2]["color"] == "warning" - assert response["dashboard_stats"][3]["value"] == "6h ago" - assert response["dashboard_stats"][3]["color"] == "success" - assert len(response["centroid_project_drilldowns"]) == 2 - assert response["centroid_project_drilldowns"][0]["project_name"] == "Admin Project" - assert response["centroid_project_drilldowns"][0]["href"] == ( + assert dashboard_stats[0]["value"] == "1 / 2" + assert dashboard_stats[0]["color"] == "warning" + assert dashboard_stats[1]["value"] == "10.0%" + assert dashboard_stats[1]["color"] == "success" + assert dashboard_stats[2]["value"] == "20.0%" + assert dashboard_stats[2]["color"] == "warning" + assert dashboard_stats[3]["value"] == "6h ago" + assert dashboard_stats[3]["color"] == "success" + assert len(centroid_project_drilldowns) == 2 + assert centroid_project_drilldowns[0]["project_name"] == "Admin Project" + assert centroid_project_drilldowns[0]["href"] == ( "/admin/trends/topiccentroidsnapshot/?project__id__exact=" - f"{source_admin_context.project.id}" + f"{_require_pk(source_admin_context.project)}" ) - assert response["centroid_project_drilldowns"][0]["drift_from_previous"] == "10.0%" + assert centroid_project_drilldowns[0]["drift_from_previous"] == "10.0%" def test_test_source_connection_reports_failures(source_admin_context, mocker): @@ -207,14 +266,14 @@ def test_test_source_connection_reports_failures(source_admin_context, mocker): side_effect=ValueError("Missing required config field: feed_url"), ) admin_instance = SourceConfigAdmin(SourceConfig, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.test_source_connection( - request=SimpleNamespace(), + request=_request(), queryset=SourceConfig.objects.filter(pk=source_config.pk), ) - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Connectivity check failed for: rss source for Admin Project: Missing required config field: feed_url", messages.ERROR, @@ -266,11 +325,12 @@ def test_review_queue_changelist_view_builds_dashboard_stats( side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(request=SimpleNamespace()) + response = admin_instance.changelist_view(request=_request()) + dashboard_stats = _dashboard_stats(response) super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["value"] == 1 - assert response["dashboard_stats"][1]["value"] == "42%" + assert dashboard_stats[0]["value"] == 1 + assert dashboard_stats[1]["value"] == "42%" def test_review_queue_display_confidence_renders_without_django6_format_error( @@ -302,7 +362,7 @@ def test_review_queue_display_confidence_renders_without_django6_format_error( def test_bluesky_credentials_admin_form_encrypts_app_password(source_admin_context): form = BlueskyCredentialsAdminForm( data={ - "project": source_admin_context.project.id, + "project": _require_pk(source_admin_context.project), "handle": "@Alice.BSKY.social", "credential_input": "app-password", "pds_url": "https://pds.example.com/xrpc/", @@ -331,15 +391,15 @@ def test_verify_selected_bluesky_credentials_reports_success( "core.plugins.bluesky.BlueskySourcePlugin.verify_credentials" ) admin_instance = BlueskyCredentialsAdmin(BlueskyCredentials, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.verify_selected_credentials( - request=SimpleNamespace(), + request=_request(), queryset=BlueskyCredentials.objects.filter(pk=credentials.pk), ) verify_mock.assert_called_once_with(credentials) - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Credential verification passed for 1 account(s).", messages.SUCCESS, @@ -359,14 +419,14 @@ def test_verify_selected_bluesky_credentials_reports_failures( side_effect=RuntimeError("bad login"), ) admin_instance = BlueskyCredentialsAdmin(BlueskyCredentials, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.verify_selected_credentials( - request=SimpleNamespace(), + request=_request(), queryset=BlueskyCredentials.objects.filter(pk=credentials.pk), ) - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Credential verification failed for: Bluesky credentials for Admin Project: bad login", messages.ERROR, @@ -376,7 +436,7 @@ def test_verify_selected_bluesky_credentials_reports_failures( def test_mastodon_credentials_admin_form_encrypts_access_token(source_admin_context): form = MastodonCredentialsAdminForm( data={ - "project": source_admin_context.project.id, + "project": _require_pk(source_admin_context.project), "instance_url": "https://hachyderm.io/@alice/", "account_acct": "@Alice", "credential_input": "access-token", @@ -406,15 +466,15 @@ def test_verify_selected_mastodon_credentials_reports_success( "core.plugins.mastodon.MastodonSourcePlugin.verify_credentials" ) admin_instance = MastodonCredentialsAdmin(MastodonCredentials, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.verify_selected_credentials( - request=SimpleNamespace(), + request=_request(), queryset=MastodonCredentials.objects.filter(pk=credentials.pk), ) verify_mock.assert_called_once_with(credentials) - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Credential verification passed for 1 account(s).", messages.SUCCESS, @@ -435,14 +495,14 @@ def test_verify_selected_mastodon_credentials_reports_failures( side_effect=RuntimeError("bad token"), ) admin_instance = MastodonCredentialsAdmin(MastodonCredentials, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.verify_selected_credentials( - request=SimpleNamespace(), + request=_request(), queryset=MastodonCredentials.objects.filter(pk=credentials.pk), ) - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Credential verification failed for: Mastodon credentials for Admin Project: bad token", messages.ERROR, @@ -545,7 +605,7 @@ def test_content_view_trace_falls_back_to_skill_runs_changelist(source_admin_con rendered = admin_instance.view_trace(content) assert "🧠 Skill runs" in rendered - assert f"content__id__exact={content.id}" in rendered + assert f"content__id__exact={_require_pk(content)}" in rendered def test_content_changelist_view_builds_dashboard_stats(source_admin_context, mocker): @@ -580,12 +640,13 @@ def test_content_changelist_view_builds_dashboard_stats(source_admin_context, mo side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(request=SimpleNamespace()) + response = admin_instance.changelist_view(request=_request()) + dashboard_stats = _dashboard_stats(response) super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["value"] == "60.0%" - assert response["dashboard_stats"][1]["value"] == "65.0%" - assert response["dashboard_stats"][2]["value"] == 2 + assert dashboard_stats[0]["value"] == "60.0%" + assert dashboard_stats[1]["value"] == "65.0%" + assert dashboard_stats[2]["value"] == 2 def test_content_admin_score_columns_render_expected_values(source_admin_context): @@ -634,19 +695,19 @@ def test_generate_newsletter_ideas_queues_selected_content( ) delay_mock = mocker.patch("core.tasks.process_content.delay") admin_instance = ContentAdmin(Content, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.generate_newsletter_ideas( - request=SimpleNamespace(), + request=_request(), queryset=Content.objects.filter( - id__in=[first_content.id, second_content.id] + id__in=[_require_pk(first_content), _require_pk(second_content)] ).order_by("id"), ) - delay_mock.assert_any_call(first_content.id) - delay_mock.assert_any_call(second_content.id) + delay_mock.assert_any_call(_require_pk(first_content)) + delay_mock.assert_any_call(_require_pk(second_content)) assert delay_mock.call_count == 2 - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Successfully queued the pipeline for 2 items.", messages.SUCCESS, @@ -786,10 +847,10 @@ def test_accept_selected_entity_candidates_creates_entity_and_backfills_mentions occurrence_count=2, ) admin_instance = EntityCandidateAdmin(EntityCandidate, AdminSite()) - admin_instance.message_user = mocker.Mock() + _message_user_mock(admin_instance, mocker) admin_instance.accept_selected_candidates( - request=SimpleNamespace(), + request=_request(), queryset=EntityCandidate.objects.filter(pk=candidate.pk), ) @@ -802,9 +863,9 @@ def test_accept_selected_entity_candidates_creates_entity_and_backfills_mentions mention = EntityMention.objects.get(content=content, entity=entity) assert candidate.status == EntityCandidateStatus.ACCEPTED - assert candidate.merged_into_id == entity.id + assert candidate.merged_into == entity assert mention.role == "subject" - assert content.entity_id == entity.id + assert content.entity == entity def test_reject_selected_entity_candidates_marks_candidates_rejected( @@ -816,10 +877,10 @@ def test_reject_selected_entity_candidates_marks_candidates_rejected( suggested_type="vendor", ) admin_instance = EntityCandidateAdmin(EntityCandidate, AdminSite()) - admin_instance.message_user = mocker.Mock() + _message_user_mock(admin_instance, mocker) admin_instance.reject_selected_candidates( - request=SimpleNamespace(), + request=_request(), queryset=EntityCandidate.objects.filter(pk=candidate.pk), ) @@ -852,17 +913,17 @@ def test_merge_selected_entity_candidates_uses_existing_same_name_entity( first_seen_in=content, ) admin_instance = EntityCandidateAdmin(EntityCandidate, AdminSite()) - admin_instance.message_user = mocker.Mock() + _message_user_mock(admin_instance, mocker) admin_instance.merge_into_existing_entities( - request=SimpleNamespace(), + request=_request(), queryset=EntityCandidate.objects.filter(pk=candidate.pk), ) candidate.refresh_from_db() assert candidate.status == EntityCandidateStatus.MERGED - assert candidate.merged_into_id == entity.id + assert candidate.merged_into == entity def test_high_value_filter_only_returns_high_value_reference_content( @@ -891,14 +952,14 @@ def test_high_value_filter_only_returns_high_value_reference_content( is_reference=False, ) filter_instance = HighValueFilter( - request=SimpleNamespace(GET={}), - params={"value_tier": "high_value"}, + request=_request(), + params=_params(value_tier="high_value"), model=Content, model_admin=ContentAdmin(Content, AdminSite()), ) filter_instance.value = lambda: "high_value" - filtered = filter_instance.queryset(SimpleNamespace(), Content.objects.all()) + filtered = filter_instance.queryset(_request(), Content.objects.all()) assert list(filtered) == [high_value] @@ -928,14 +989,14 @@ def test_duplicate_state_filter_returns_canonical_rows_with_duplicate_signals( content_text="Plain content.", ) filter_instance = DuplicateStateFilter( - request=SimpleNamespace(GET={}), - params={"duplicate_state": "canonical_with_duplicates"}, + request=_request(), + params=_params(duplicate_state="canonical_with_duplicates"), model=Content, model_admin=ContentAdmin(Content, AdminSite()), ) filter_instance.value = lambda: "canonical_with_duplicates" - filtered = filter_instance.queryset(SimpleNamespace(), Content.objects.all()) + filtered = filter_instance.queryset(_request(), Content.objects.all()) assert list(filtered) == [canonical] @@ -966,14 +1027,14 @@ def test_duplicate_state_filter_returns_suppressed_duplicates( is_active=False, ) filter_instance = DuplicateStateFilter( - request=SimpleNamespace(GET={}), - params={"duplicate_state": "suppressed_duplicates"}, + request=_request(), + params=_params(duplicate_state="suppressed_duplicates"), model=Content, model_admin=ContentAdmin(Content, AdminSite()), ) filter_instance.value = lambda: "suppressed_duplicates" - filtered = filter_instance.queryset(SimpleNamespace(), Content.objects.all()) + filtered = filter_instance.queryset(_request(), Content.objects.all()) assert list(filtered) == [duplicate] @@ -1001,7 +1062,7 @@ def test_content_view_trace_builds_template_trace_url(source_admin_context, sett rendered = admin_instance.view_trace(content) assert ( - f"https://trace.example/{content.project_id}/summarization/{skill_result.id}/trace-123/{content.id}/trace-123" + f"https://trace.example/{_require_pk(source_admin_context.project)}/summarization/{_require_pk(skill_result)}/trace-123/{_require_pk(content)}/trace-123" in rendered ) @@ -1070,21 +1131,22 @@ def test_skill_result_admin_helpers_and_dashboard_stats(source_admin_context, mo superseded_by=current_result, ) admin_instance = SkillResultAdmin(SkillResult, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) super_changelist_view = mocker.patch( "core.admin.ModelAdmin.changelist_view", side_effect=lambda request, extra_context=None: extra_context, ) admin_instance.retry_selected_skills( - SimpleNamespace(), SkillResult.objects.filter(pk=current_result.pk) + _request(), SkillResult.objects.filter(pk=current_result.pk) ) current_result.refresh_from_db() - response = admin_instance.changelist_view(SimpleNamespace()) + response = admin_instance.changelist_view(_request()) + dashboard_stats = _dashboard_stats(response) assert current_result.status == "pending" assert current_result.error_message == "" - admin_instance.message_user.assert_called_once_with( + message_user_mock.assert_called_once_with( ANY, "Successfully reset 1 skills to PENDING for retry.", messages.SUCCESS, @@ -1102,16 +1164,16 @@ def test_skill_result_admin_helpers_and_dashboard_stats(source_admin_context, mo assert "Draft summary" in admin_instance.pretty_result_data(current_result) assert admin_instance.pretty_result_data(superseded_result) == "No data available" super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["value"] == "750ms" - assert response["dashboard_stats"][1]["value"] == "0.0%" + assert dashboard_stats[0]["value"] == "750ms" + assert dashboard_stats[1]["value"] == "0.0%" def test_user_feedback_admin_helpers_and_dashboard_stats( source_admin_context, django_user_model, mocker ): mocker.patch("core.signals.queue_topic_centroid_recompute") - user = django_user_model.objects.create_user( - username="feedback-user", password="testpass123" + user = _create_user( + django_user_model, username="feedback-user", password="testpass123" ) content = Content.objects.create( project=source_admin_context.project, @@ -1142,8 +1204,8 @@ def test_user_feedback_admin_helpers_and_dashboard_stats( UserFeedback.objects.create( content=other_content, project=source_admin_context.project, - user=django_user_model.objects.create_user( - username="feedback-user-2", password="testpass123" + user=_create_user( + django_user_model, username="feedback-user-2", password="testpass123" ), feedback_type="downvote", ) @@ -1153,7 +1215,8 @@ def test_user_feedback_admin_helpers_and_dashboard_stats( side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(SimpleNamespace()) + response = admin_instance.changelist_view(_request()) + dashboard_stats = _dashboard_stats(response) assert "👍" in admin_instance.display_feedback(upvote) assert admin_instance.get_content_title(upvote).endswith("...") @@ -1163,8 +1226,8 @@ def test_user_feedback_admin_helpers_and_dashboard_stats( downvote = UserFeedback.objects.get(content=other_content) assert admin_instance.get_ai_score(downvote) == "-" super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["value"] == "50.0%" - assert response["dashboard_stats"][1]["value"] == 2 + assert dashboard_stats[0]["value"] == "50.0%" + assert dashboard_stats[1]["value"] == 2 def test_ingestion_run_display_duration_handles_running_and_completed( @@ -1184,8 +1247,8 @@ def test_ingestion_run_display_duration_handles_running_and_completed( items_fetched=10, items_ingested=10, ) - completed_run.started_at = timezone.now() - timezone.timedelta(minutes=3, seconds=5) - completed_run.completed_at = completed_run.started_at + timezone.timedelta( + completed_run.started_at = timezone.now() - timedelta(minutes=3, seconds=5) + completed_run.completed_at = completed_run.started_at + timedelta( minutes=3, seconds=5 ) completed_run.save(update_fields=["started_at", "completed_at"]) @@ -1222,13 +1285,13 @@ def test_review_queue_actions_update_resolution_and_emit_message( resolved=False, ) admin_instance = ReviewQueueAdmin(ReviewQueue, AdminSite()) - admin_instance.message_user = mocker.Mock() + message_user_mock = _message_user_mock(admin_instance, mocker) admin_instance.mark_as_approved( - SimpleNamespace(), ReviewQueue.objects.filter(pk=approve_item.pk) + _request(), ReviewQueue.objects.filter(pk=approve_item.pk) ) admin_instance.mark_as_rejected( - SimpleNamespace(), ReviewQueue.objects.filter(pk=reject_item.pk) + _request(), ReviewQueue.objects.filter(pk=reject_item.pk) ) approve_item.refresh_from_db() @@ -1237,12 +1300,12 @@ def test_review_queue_actions_update_resolution_and_emit_message( assert approve_item.resolution == "APPROVED" assert reject_item.resolved is True assert reject_item.resolution == "REJECTED" - assert admin_instance.message_user.call_count == 2 + assert message_user_mock.call_count == 2 def test_high_value_filter_lookups_and_noop_queryset(source_admin_context): filter_instance = HighValueFilter( - request=SimpleNamespace(GET={}), + request=_request(), params={}, model=Content, model_admin=ContentAdmin(Content, AdminSite()), @@ -1258,10 +1321,10 @@ def test_high_value_filter_lookups_and_noop_queryset(source_admin_context): content_text="noop", ) - assert filter_instance.lookups(None, None) == ( + assert filter_instance.lookups(_request(), ContentAdmin(Content, AdminSite())) == ( ("high_value", "🔥 High Value (Score > 80 & Reference)"), ) - assert list(filter_instance.queryset(SimpleNamespace(), Content.objects.all())) == [ + assert list(filter_instance.queryset(_request(), Content.objects.all())) == [ content ] @@ -1334,11 +1397,12 @@ def test_skill_result_changelist_view_uses_warning_and_danger_colors( side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(SimpleNamespace()) + response = admin_instance.changelist_view(_request()) + dashboard_stats = _dashboard_stats(response) super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["color"] == "warning" - assert response["dashboard_stats"][1]["color"] == "danger" + assert dashboard_stats[0]["color"] == "warning" + assert dashboard_stats[1]["color"] == "danger" def test_user_feedback_admin_upvote_and_orange_score_branches( @@ -1400,8 +1464,8 @@ def test_user_feedback_changelist_view_uses_success_color_for_high_approval( UserFeedback.objects.create( content=second_content, project=source_admin_context.project, - user=django_user_model.objects.create_user( - username="feedback-success-2", password="testpass123" + user=_create_user( + django_user_model, username="feedback-success-2", password="testpass123" ), feedback_type="upvote", ) @@ -1411,11 +1475,12 @@ def test_user_feedback_changelist_view_uses_success_color_for_high_approval( side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(SimpleNamespace()) + response = admin_instance.changelist_view(_request()) + dashboard_stats = _dashboard_stats(response) super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["color"] == "success" - assert response["dashboard_stats"][0]["value"] == "100.0%" + assert dashboard_stats[0]["color"] == "success" + assert dashboard_stats[0]["value"] == "100.0%" def test_ingestion_run_admin_status_efficiency_and_dashboard_branches( @@ -1441,7 +1506,8 @@ def test_ingestion_run_admin_status_efficiency_and_dashboard_branches( side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(SimpleNamespace()) + response = admin_instance.changelist_view(_request()) + dashboard_stats = _dashboard_stats(response) assert "danger" in admin_instance.display_status( IngestionRun.objects.filter(status="failed").first() @@ -1454,8 +1520,8 @@ def test_ingestion_run_admin_status_efficiency_and_dashboard_branches( ) assert "info" in admin_instance.display_status(running_run) super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["value"] == "5" - assert response["dashboard_stats"][1]["color"] == "warning" + assert dashboard_stats[0]["value"] == "5" + assert dashboard_stats[1]["color"] == "warning" def test_source_config_admin_health_pretty_config_and_dashboard_branches( @@ -1466,7 +1532,7 @@ def test_source_config_admin_health_pretty_config_and_dashboard_branches( plugin_name=SourcePluginName.RSS, config={"feed_url": "https://example.com/stale.xml"}, is_active=True, - last_fetched_at=timezone.now() - timezone.timedelta(days=2), + last_fetched_at=timezone.now() - timedelta(days=2), ) paused_config = SourceConfig.objects.create( project=source_admin_context.project, @@ -1487,15 +1553,16 @@ def test_source_config_admin_health_pretty_config_and_dashboard_branches( side_effect=lambda request, extra_context=None: extra_context, ) - response = admin_instance.changelist_view(SimpleNamespace()) + response = admin_instance.changelist_view(_request()) + dashboard_stats = _dashboard_stats(response) assert "Stale" in admin_instance.display_health(stale_config) assert "Paused" in admin_instance.display_health(paused_config) assert "Never Run" in admin_instance.display_health(never_run_config) assert admin_instance.pretty_config(paused_config) == "Empty" super_changelist_view.assert_called_once() - assert response["dashboard_stats"][0]["color"] == "warning" - assert response["dashboard_stats"][1]["value"] == 2 + assert dashboard_stats[0]["color"] == "warning" + assert dashboard_stats[1]["value"] == 2 @pytest.mark.parametrize( diff --git a/core/tests/test_api.py b/core/tests/test_api.py index 03918b1a..fd4f155e 100644 --- a/core/tests/test_api.py +++ b/core/tests/test_api.py @@ -1,10 +1,12 @@ from types import SimpleNamespace +from typing import Any, cast from unittest.mock import patch from django.contrib.auth import get_user_model +from django.db.models import Model from django.urls import reverse from rest_framework import status -from rest_framework.test import APITestCase +from rest_framework.test import APIClient, APITestCase from core.models import ( Content, @@ -24,6 +26,8 @@ RunStatus, SkillResult, SkillStatus, + ThemeSuggestion, + ThemeSuggestionStatus, TopicCentroidSnapshot, TopicCluster, TopicVelocitySnapshot, @@ -41,14 +45,33 @@ ) +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed API test assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _typed_client(client: object) -> APIClient: + """Cast the DRF test client so Pylance sees APIClient helpers.""" + + return cast(APIClient, client) + + +def _create_user(user_model: type[Any], **kwargs: object): + """Create a user through the custom manager with a typed escape hatch.""" + + return cast(Any, user_model.objects).create_user(**kwargs) + + class ProjectScopedApiTests(APITestCase): def setUp(self): user_model = get_user_model() - self.owner = user_model.objects.create_user( - username="owner", password="testpass123" - ) - self.other_user = user_model.objects.create_user( - username="other", password="testpass123" + self.owner = _create_user(user_model, username="owner", password="testpass123") + self.other_user = _create_user( + user_model, username="other", password="testpass123" ) self.owner_project = Project.objects.create( name="Owner Project", @@ -157,14 +180,14 @@ def setUp(self): drift_from_previous=0.1, drift_from_week_ago=0.2, ) - self.client.force_authenticate(self.owner) + _typed_client(self.client).force_authenticate(self.owner) def assert_standardized_validation_error(self, payload, attr): self.assertEqual(payload["type"], "validation_error") self.assertTrue(any(error["attr"] == attr for error in payload["errors"])) def test_project_list_requires_authentication(self): - self.client.force_authenticate(user=None) + _typed_client(self.client).force_authenticate(user=None) response = self.client.get(reverse("v1:project-list"), HTTP_HOST="localhost") @@ -195,7 +218,7 @@ def test_project_list_is_scoped_to_request_user_memberships(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], self.owner_project.id) + self.assertEqual(response.json()[0]["id"], _require_pk(self.owner_project)) self.assertEqual(response.json()[0]["user_role"], ProjectRole.ADMIN) self.assertEqual( response.json()[0]["intake_token"], self.owner_project.intake_token @@ -214,7 +237,7 @@ def test_project_rotate_intake_token_returns_updated_project(self): response = self.client.post( reverse( "v1:project-rotate-intake-token", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -229,13 +252,14 @@ def test_project_rotate_intake_token_returns_updated_project(self): def test_entity_list_is_scoped_to_request_user_project(self): response = self.client.get( reverse( - "v1:project-entity-list", kwargs={"project_id": self.owner_project.id} + "v1:project-entity-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], self.owner_entity.id) + self.assertEqual(response.json()[0]["id"], _require_pk(self.owner_entity)) def test_entity_list_includes_recent_mentions(self): mention = EntityMention.objects.create( @@ -250,13 +274,16 @@ def test_entity_list_includes_recent_mentions(self): response = self.client.get( reverse( - "v1:project-entity-list", kwargs={"project_id": self.owner_project.id} + "v1:project-entity-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.json()[0]["mention_count"], 1) - self.assertEqual(response.json()[0]["latest_mentions"][0]["id"], mention.id) + self.assertEqual( + response.json()[0]["latest_mentions"][0]["id"], _require_pk(mention) + ) self.assertEqual( response.json()[0]["latest_mentions"][0]["content_title"], self.owner_content.title, @@ -296,16 +323,16 @@ def test_entity_mentions_action_returns_full_mention_history(self): reverse( "v1:project-entity-mentions", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_entity.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_entity), }, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 2) - self.assertEqual(response.json()[0]["id"], second_mention.id) - self.assertEqual(response.json()[1]["id"], first_mention.id) + self.assertEqual(response.json()[0]["id"], _require_pk(second_mention)) + self.assertEqual(response.json()[1]["id"], _require_pk(first_mention)) self.assertEqual(response.json()[0]["content_title"], second_content.title) def test_intake_allowlist_list_is_scoped_to_request_user_project(self): @@ -317,21 +344,23 @@ def test_intake_allowlist_list_is_scoped_to_request_user_project(self): response = self.client.get( reverse( "v1:project-intake-allowlist-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], self.owner_intake_allowlist.id) + self.assertEqual( + response.json()[0]["id"], _require_pk(self.owner_intake_allowlist) + ) self.assertFalse(response.json()[0]["is_confirmed"]) - self.assertNotEqual(response.json()[0]["id"], other_allowlist.id) + self.assertNotEqual(response.json()[0]["id"], _require_pk(other_allowlist)) def test_intake_allowlist_create_and_delete_manage_project_senders(self): create_response = self.client.post( reverse( "v1:project-intake-allowlist-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), {"sender_email": "new-sender@example.com"}, format="json", @@ -342,29 +371,31 @@ def test_intake_allowlist_create_and_delete_manage_project_senders(self): project=self.owner_project, sender_email="new-sender@example.com", ) - self.assertEqual(create_response.json()["project"], self.owner_project.id) + self.assertEqual( + create_response.json()["project"], _require_pk(self.owner_project) + ) self.assertFalse(create_response.json()["is_confirmed"]) delete_response = self.client.delete( reverse( "v1:project-intake-allowlist-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": created_allowlist.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(created_allowlist), }, ) ) self.assertEqual(delete_response.status_code, status.HTTP_204_NO_CONTENT) self.assertFalse( - IntakeAllowlist.objects.filter(pk=created_allowlist.id).exists() + IntakeAllowlist.objects.filter(pk=_require_pk(created_allowlist)).exists() ) def test_bluesky_credentials_list_create_and_update_hide_stored_password(self): list_response = self.client.get( reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) @@ -374,7 +405,7 @@ def test_bluesky_credentials_list_create_and_update_hide_stored_password(self): create_response = self.client.post( reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), { "handle": "@Owner.Project.BSKY.social", @@ -397,8 +428,8 @@ def test_bluesky_credentials_list_create_and_update_hide_stored_password(self): reverse( "v1:project-bluesky-credentials-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": credentials.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(credentials), }, ), { @@ -419,7 +450,7 @@ def test_bluesky_credentials_create_requires_app_password(self): response = self.client.post( reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), { "handle": "owner.bsky.social", @@ -445,19 +476,21 @@ def test_newsletter_intake_list_returns_recent_project_history(self): response = self.client.get( reverse( "v1:project-newsletter-intake-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], self.owner_newsletter_intake.id) + self.assertEqual( + response.json()[0]["id"], _require_pk(self.owner_newsletter_intake) + ) self.assertEqual(response.json()[0]["status"], NewsletterIntakeStatus.EXTRACTED) self.assertEqual( response.json()[0]["extraction_result"]["items"][0]["title"], "Example Post", ) - self.assertNotEqual(response.json()[0]["id"], other_intake.id) + self.assertNotEqual(response.json()[0]["id"], _require_pk(other_intake)) def test_entity_list_supports_authority_score_ordering(self): second_entity = Entity.objects.create( @@ -471,14 +504,15 @@ def test_entity_list_supports_authority_score_ordering(self): response = self.client.get( reverse( - "v1:project-entity-list", kwargs={"project_id": self.owner_project.id} + "v1:project-entity-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), {"ordering": "-authority_score"}, ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()[0]["id"], second_entity.id) - self.assertEqual(response.json()[1]["id"], self.owner_entity.id) + self.assertEqual(response.json()[0]["id"], _require_pk(second_entity)) + self.assertEqual(response.json()[1]["id"], _require_pk(self.owner_entity)) def test_entity_authority_history_action_returns_recent_snapshots(self): first_snapshot = EntityAuthoritySnapshot.objects.create( @@ -504,8 +538,8 @@ def test_entity_authority_history_action_returns_recent_snapshots(self): reverse( "v1:project-entity-authority-history", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_entity.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_entity), }, ), {"limit": 1}, @@ -513,8 +547,8 @@ def test_entity_authority_history_action_returns_recent_snapshots(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], second_snapshot.id) - self.assertNotEqual(response.json()[0]["id"], first_snapshot.id) + self.assertEqual(response.json()[0]["id"], _require_pk(second_snapshot)) + self.assertNotEqual(response.json()[0]["id"], _require_pk(first_snapshot)) def test_topic_centroid_summary_action_returns_latest_snapshot_and_averages(self): latest_snapshot = TopicCentroidSnapshot.objects.create( @@ -531,15 +565,17 @@ def test_topic_centroid_summary_action_returns_latest_snapshot_and_averages(self response = self.client.get( reverse( "v1:project-topic-centroid-snapshot-summary", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()["project"], self.owner_project.id) + self.assertEqual(response.json()["project"], _require_pk(self.owner_project)) self.assertEqual(response.json()["snapshot_count"], 2) self.assertEqual(response.json()["active_snapshot_count"], 2) - self.assertEqual(response.json()["latest_snapshot"]["id"], latest_snapshot.id) + self.assertEqual( + response.json()["latest_snapshot"]["id"], _require_pk(latest_snapshot) + ) self.assertAlmostEqual(response.json()["avg_drift_from_previous"], 0.2) self.assertAlmostEqual(response.json()["avg_drift_from_week_ago"], 0.3) @@ -565,17 +601,17 @@ def test_topic_cluster_list_returns_current_velocity_annotation(self): response = self.client.get( reverse( "v1:project-topic-cluster-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), {"ordering": "-velocity_score"}, ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], cluster.id) + self.assertEqual(response.json()[0]["id"], _require_pk(cluster)) self.assertEqual(response.json()[0]["member_count"], 3) self.assertEqual( - response.json()[0]["dominant_entity"]["id"], self.owner_entity.id + response.json()[0]["dominant_entity"]["id"], _require_pk(self.owner_entity) ) self.assertAlmostEqual(response.json()[0]["velocity_score"], 1.0) self.assertAlmostEqual(response.json()[0]["z_score"], 3.0) @@ -624,33 +660,230 @@ def test_topic_cluster_detail_and_velocity_history_action_return_memberships(sel detail_response = self.client.get( reverse( "v1:project-topic-cluster-detail", - kwargs={"project_id": self.owner_project.id, "pk": cluster.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(cluster), + }, ) ) history_response = self.client.get( reverse( "v1:project-topic-cluster-velocity-history", - kwargs={"project_id": self.owner_project.id, "pk": cluster.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(cluster), + }, ), {"limit": 1}, ) self.assertEqual(detail_response.status_code, status.HTTP_200_OK) - self.assertEqual(detail_response.json()["id"], cluster.id) + self.assertEqual(detail_response.json()["id"], _require_pk(cluster)) self.assertEqual(len(detail_response.json()["memberships"]), 1) self.assertEqual( detail_response.json()["memberships"][0]["content"]["id"], - self.owner_content.id, + _require_pk(self.owner_content), ) self.assertEqual(len(detail_response.json()["velocity_history"]), 2) self.assertEqual( detail_response.json()["velocity_history"][0]["id"], - second_snapshot.id, + _require_pk(second_snapshot), ) self.assertEqual(history_response.status_code, status.HTTP_200_OK) self.assertEqual(len(history_response.json()), 1) - self.assertEqual(history_response.json()[0]["id"], second_snapshot.id) + self.assertEqual(history_response.json()[0]["id"], _require_pk(second_snapshot)) + + def test_theme_suggestion_list_is_scoped_to_project(self): + cluster = TopicCluster.objects.create( + project=self.owner_project, + first_seen_at="2026-04-22T00:00:00Z", + last_seen_at="2026-04-24T00:00:00Z", + is_active=True, + member_count=3, + dominant_entity=self.owner_entity, + ) + suggestion = ThemeSuggestion.objects.create( + project=self.owner_project, + cluster=cluster, + title="Owner Theme", + pitch="Owner pitch", + why_it_matters="Owner why", + suggested_angle="Owner angle", + velocity_at_creation=0.9, + novelty_score=0.8, + ) + other_cluster = TopicCluster.objects.create( + project=self.other_project, + first_seen_at="2026-04-22T00:00:00Z", + last_seen_at="2026-04-24T00:00:00Z", + is_active=True, + member_count=3, + dominant_entity=self.other_entity, + ) + ThemeSuggestion.objects.create( + project=self.other_project, + cluster=other_cluster, + title="Other Theme", + pitch="Other pitch", + why_it_matters="Other why", + suggested_angle="Other angle", + velocity_at_creation=0.8, + novelty_score=0.7, + ) + + response = self.client.get( + reverse( + "v1:project-theme-suggestion-list", + kwargs={"project_id": _require_pk(self.owner_project)}, + ) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.json()), 1) + self.assertEqual(response.json()[0]["id"], _require_pk(suggestion)) + self.assertEqual(response.json()[0]["status"], ThemeSuggestionStatus.PENDING) + + def test_theme_suggestion_accept_and_dismiss_actions_update_workflow_fields(self): + cluster = TopicCluster.objects.create( + project=self.owner_project, + first_seen_at="2026-04-22T00:00:00Z", + last_seen_at="2026-04-24T00:00:00Z", + is_active=True, + member_count=3, + dominant_entity=self.owner_entity, + ) + promoted_content = Content.objects.create( + project=self.owner_project, + url="https://example.com/promoted-by-theme", + title="Promoted by Theme", + author="Owner Author", + entity=self.owner_entity, + source_plugin=SourcePluginName.RSS, + published_date="2026-04-24T00:00:00Z", + content_text="Promoted content text", + ) + ContentClusterMembership.objects.create( + content=promoted_content, + cluster=cluster, + project=self.owner_project, + similarity=0.94, + ) + accept_suggestion = ThemeSuggestion.objects.create( + project=self.owner_project, + cluster=cluster, + title="Accept Theme", + pitch="Pitch", + why_it_matters="Why", + suggested_angle="Angle", + velocity_at_creation=0.9, + novelty_score=0.8, + ) + dismiss_suggestion = ThemeSuggestion.objects.create( + project=self.owner_project, + cluster=cluster, + title="Dismiss Theme", + pitch="Pitch", + why_it_matters="Why", + suggested_angle="Angle", + velocity_at_creation=0.7, + novelty_score=0.75, + ) + + accept_response = self.client.post( + reverse( + "v1:project-theme-suggestion-accept", + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(accept_suggestion), + }, + ), + format="json", + ) + dismiss_response = self.client.post( + reverse( + "v1:project-theme-suggestion-dismiss", + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(dismiss_suggestion), + }, + ), + {"reason": "already covered"}, + format="json", + ) + + accept_suggestion.refresh_from_db() + dismiss_suggestion.refresh_from_db() + promoted_content.refresh_from_db() + self.assertEqual(accept_response.status_code, status.HTTP_200_OK) + self.assertEqual(accept_suggestion.status, ThemeSuggestionStatus.ACCEPTED) + self.assertEqual(accept_suggestion.decided_by, self.owner) + self.assertIsNotNone(accept_suggestion.decided_at) + self.assertEqual(promoted_content.newsletter_promotion_theme, accept_suggestion) + self.assertEqual(promoted_content.newsletter_promotion_by, self.owner) + self.assertIsNotNone(promoted_content.newsletter_promotion_at) + self.assertEqual(len(accept_response.json()["promoted_contents"]), 1) + self.assertEqual( + accept_response.json()["promoted_contents"][0]["id"], + _require_pk(promoted_content), + ) + + self.assertEqual(dismiss_response.status_code, status.HTTP_200_OK) + self.assertEqual(dismiss_suggestion.status, ThemeSuggestionStatus.DISMISSED) + self.assertEqual(dismiss_suggestion.dismissal_reason, "already covered") + self.assertEqual(dismiss_suggestion.decided_by, self.owner) + + def test_content_detail_includes_newsletter_promotion_state(self): + cluster = TopicCluster.objects.create( + project=self.owner_project, + first_seen_at="2026-04-22T00:00:00Z", + last_seen_at="2026-04-24T00:00:00Z", + is_active=True, + member_count=1, + dominant_entity=self.owner_entity, + ) + suggestion = ThemeSuggestion.objects.create( + project=self.owner_project, + cluster=cluster, + title="Promoted Theme", + pitch="Pitch", + why_it_matters="Why", + suggested_angle="Angle", + velocity_at_creation=0.9, + novelty_score=0.8, + status=ThemeSuggestionStatus.ACCEPTED, + decided_by=self.owner, + ) + self.owner_content.newsletter_promotion_theme = suggestion + self.owner_content.newsletter_promotion_by = self.owner + self.owner_content.newsletter_promotion_at = "2026-04-24T00:00:00Z" + self.owner_content.save( + update_fields=[ + "newsletter_promotion_theme", + "newsletter_promotion_by", + "newsletter_promotion_at", + ] + ) + + response = self.client.get( + reverse( + "v1:project-content-detail", + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_content), + }, + ) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.json()["newsletter_promotion_theme"], + _require_pk(suggestion), + ) + self.assertEqual( + response.json()["newsletter_promotion_by"], _require_pk(self.owner) + ) + self.assertIsNotNone(response.json()["newsletter_promotion_at"]) def test_content_detail_includes_duplicate_state(self): canonical = self.owner_content @@ -674,19 +907,23 @@ def test_content_detail_includes_duplicate_state(self): response = self.client.get( reverse( "v1:project-content-detail", - kwargs={"project_id": self.owner_project.id, "pk": duplicate.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(duplicate), + }, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.json()["canonical_url"], "https://example.com/owner") - self.assertEqual(response.json()["duplicate_of"], canonical.id) + self.assertEqual(response.json()["duplicate_of"], _require_pk(canonical)) self.assertEqual(response.json()["duplicate_signal_count"], 0) def test_nested_entity_list_rejects_other_users_project(self): response = self.client.get( reverse( - "v1:project-entity-list", kwargs={"project_id": self.other_project.id} + "v1:project-entity-list", + kwargs={"project_id": _require_pk(self.other_project)}, ) ) @@ -709,13 +946,13 @@ def test_entity_candidate_list_is_scoped_to_request_user_project(self): response = self.client.get( reverse( "v1:project-entity-candidate-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ) ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json()[0]["id"], owner_candidate.id) + self.assertEqual(response.json()[0]["id"], _require_pk(owner_candidate)) def test_entity_candidate_accept_action_returns_updated_candidate(self): candidate = EntityCandidate.objects.create( @@ -728,7 +965,10 @@ def test_entity_candidate_accept_action_returns_updated_candidate(self): response = self.client.post( reverse( "v1:project-entity-candidate-accept", - kwargs={"project_id": self.owner_project.id, "pk": candidate.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(candidate), + }, ), format="json", ) @@ -736,7 +976,7 @@ def test_entity_candidate_accept_action_returns_updated_candidate(self): candidate.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(candidate.status, EntityCandidateStatus.ACCEPTED) - self.assertIsNotNone(candidate.merged_into_id) + self.assertIsNotNone(candidate.merged_into) self.assertEqual(response.json()["status"], EntityCandidateStatus.ACCEPTED) def test_entity_candidate_reject_action_returns_updated_candidate(self): @@ -750,7 +990,10 @@ def test_entity_candidate_reject_action_returns_updated_candidate(self): response = self.client.post( reverse( "v1:project-entity-candidate-reject", - kwargs={"project_id": self.owner_project.id, "pk": candidate.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(candidate), + }, ), format="json", ) @@ -771,9 +1014,12 @@ def test_entity_candidate_merge_rejects_cross_project_entity(self): response = self.client.post( reverse( "v1:project-entity-candidate-merge", - kwargs={"project_id": self.owner_project.id, "pk": candidate.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(candidate), + }, ), - {"merged_into": self.other_entity.id}, + {"merged_into": _require_pk(self.other_entity)}, format="json", ) @@ -791,23 +1037,26 @@ def test_entity_candidate_merge_action_returns_updated_candidate(self): response = self.client.post( reverse( "v1:project-entity-candidate-merge", - kwargs={"project_id": self.owner_project.id, "pk": candidate.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(candidate), + }, ), - {"merged_into": self.owner_entity.id}, + {"merged_into": _require_pk(self.owner_entity)}, format="json", ) candidate.refresh_from_db() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(candidate.status, EntityCandidateStatus.MERGED) - self.assertEqual(candidate.merged_into_id, self.owner_entity.id) - self.assertEqual(response.json()["merged_into"], self.owner_entity.id) + self.assertEqual(candidate.merged_into, self.owner_entity) + self.assertEqual(response.json()["merged_into"], _require_pk(self.owner_entity)) def test_verify_bluesky_credentials_requires_project_credentials(self): response = self.client.post( reverse( "v1:project-verify-bluesky-credentials", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -828,7 +1077,7 @@ def test_verify_bluesky_credentials_verifies_project_account(self, verify_mock): response = self.client.post( reverse( "v1:project-verify-bluesky-credentials", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -836,7 +1085,7 @@ def test_verify_bluesky_credentials_verifies_project_account(self, verify_mock): self.assertEqual(response.status_code, status.HTTP_200_OK) verify_mock.assert_called_once() verified_credentials = verify_mock.call_args.args[0] - self.assertEqual(verified_credentials.id, credentials.id) + self.assertEqual(verified_credentials, credentials) self.assertEqual(response.json()["status"], "verified") self.assertEqual(response.json()["handle"], "project.bsky.social") self.assertEqual(response.json()["last_error"], "") @@ -858,7 +1107,7 @@ def test_verify_bluesky_credentials_surfaces_verification_errors( response = self.client.post( reverse( "v1:project-verify-bluesky-credentials", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -870,14 +1119,14 @@ def test_verify_bluesky_credentials_surfaces_verification_errors( self.assertNotIn("bad login", str(response.json())) logger_exception_mock.assert_called_once_with( "Bluesky credential verification failed for project id=%s", - self.owner_project.id, + _require_pk(self.owner_project), ) def test_verify_mastodon_credentials_requires_configured_project_credentials(self): response = self.client.post( reverse( "v1:project-verify-mastodon-credentials", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -900,7 +1149,7 @@ def test_verify_mastodon_credentials_verifies_project_account(self, verify_mock) response = self.client.post( reverse( "v1:project-verify-mastodon-credentials", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -908,7 +1157,7 @@ def test_verify_mastodon_credentials_verifies_project_account(self, verify_mock) self.assertEqual(response.status_code, status.HTTP_200_OK) verify_mock.assert_called_once() verified_credentials = verify_mock.call_args.args[0] - self.assertEqual(verified_credentials.id, credentials.id) + self.assertEqual(verified_credentials, credentials) self.assertEqual(response.json()["status"], "verified") self.assertEqual(response.json()["account_acct"], "alice@hachyderm.io") self.assertEqual(response.json()["instance_url"], "https://hachyderm.io") @@ -933,7 +1182,7 @@ def test_verify_mastodon_credentials_surfaces_verification_errors( response = self.client.post( reverse( "v1:project-verify-mastodon-credentials", - kwargs={"id": self.owner_project.id}, + kwargs={"id": _require_pk(self.owner_project)}, ), format="json", ) @@ -945,17 +1194,18 @@ def test_verify_mastodon_credentials_surfaces_verification_errors( self.assertNotIn("bad token", str(response.json())) logger_exception_mock.assert_called_once_with( "Mastodon credential verification failed for project id=%s", - self.owner_project.id, + _require_pk(self.owner_project), ) @patch("core.signals.queue_topic_centroid_recompute") def test_feedback_create_assigns_current_user(self, queue_centroid_mock): response = self.client.post( reverse( - "v1:project-feedback-list", kwargs={"project_id": self.owner_project.id} + "v1:project-feedback-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), { - "content": self.owner_content.id, + "content": _require_pk(self.owner_content), "feedback_type": FeedbackType.UPVOTE, }, format="json", @@ -965,15 +1215,16 @@ def test_feedback_create_assigns_current_user(self, queue_centroid_mock): feedback = UserFeedback.objects.get() self.assertEqual(feedback.user, self.owner) self.assertEqual(feedback.feedback_type, FeedbackType.UPVOTE) - queue_centroid_mock.assert_called_once_with(self.owner_project.id) + queue_centroid_mock.assert_called_once_with(_require_pk(self.owner_project)) def test_feedback_rejects_cross_project_content(self): response = self.client.post( reverse( - "v1:project-feedback-list", kwargs={"project_id": self.owner_project.id} + "v1:project-feedback-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), { - "content": self.other_content.id, + "content": _require_pk(self.other_content), "feedback_type": FeedbackType.DOWNVOTE, }, format="json", @@ -985,17 +1236,18 @@ def test_feedback_rejects_cross_project_content(self): def test_content_create_uses_project_from_url(self): response = self.client.post( reverse( - "v1:project-content-list", kwargs={"project_id": self.owner_project.id} + "v1:project-content-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), { "url": "https://example.com/new", "title": "New Content", "author": "Owner Author", - "entity": self.owner_entity.id, + "entity": _require_pk(self.owner_entity), "source_plugin": "rss", "published_date": "2026-04-22T00:00:00Z", "content_text": "Nested content text", - "project": self.other_project.id, + "project": _require_pk(self.other_project), }, format="json", ) @@ -1010,7 +1262,7 @@ def test_content_skill_action_queues_relevance_scoring( ): response = self.client.post( - f"/api/v1/projects/{self.owner_project.id}/contents/{self.owner_content.id}/skills/relevance_scoring/", + f"/api/v1/projects/{_require_pk(self.owner_project)}/contents/{_require_pk(self.owner_content)}/skills/relevance_scoring/", format="json", ) @@ -1020,7 +1272,9 @@ def test_content_skill_action_queues_relevance_scoring( skill_name="relevance_scoring", superseded_by__isnull=True, ) - run_relevance_scoring_delay_mock.assert_called_once_with(pending_result.id) + run_relevance_scoring_delay_mock.assert_called_once_with( + _require_pk(pending_result) + ) self.owner_content.refresh_from_db() self.assertIsNone(self.owner_content.relevance_score) self.assertEqual(response.json()["skill_name"], "relevance_scoring") @@ -1034,7 +1288,7 @@ def test_content_skill_action_queues_summarization( self.owner_content.save(update_fields=["relevance_score"]) response = self.client.post( - f"/api/v1/projects/{self.owner_project.id}/contents/{self.owner_content.id}/skills/summarization/", + f"/api/v1/projects/{_require_pk(self.owner_project)}/contents/{_require_pk(self.owner_content)}/skills/summarization/", format="json", ) @@ -1044,7 +1298,9 @@ def test_content_skill_action_queues_summarization( skill_name="summarization", superseded_by__isnull=True, ) - run_summarization_delay_mock.assert_called_once_with(pending_result.id) + run_summarization_delay_mock.assert_called_once_with( + _require_pk(pending_result) + ) self.assertEqual(response.json()["skill_name"], "summarization") self.assertEqual(response.json()["status"], SkillStatus.PENDING) @@ -1054,7 +1310,7 @@ def test_content_skill_action_runs_find_related(self, search_similar_content_moc SimpleNamespace( score=0.91, payload={ - "content_id": self.other_content.id, + "content_id": _require_pk(self.other_content), "title": self.other_content.title, "url": self.other_content.url, "published_date": self.other_content.published_date, @@ -1064,7 +1320,7 @@ def test_content_skill_action_runs_find_related(self, search_similar_content_moc ] response = self.client.post( - f"/api/v1/projects/{self.owner_project.id}/contents/{self.owner_content.id}/skills/find_related/", + f"/api/v1/projects/{_require_pk(self.owner_project)}/contents/{_require_pk(self.owner_content)}/skills/find_related/", format="json", ) @@ -1073,62 +1329,66 @@ def test_content_skill_action_runs_find_related(self, search_similar_content_moc self.assertEqual(response.json()["status"], SkillStatus.COMPLETED) self.assertEqual( response.json()["result_data"]["related_items"][0]["content_id"], - self.other_content.id, + _require_pk(self.other_content), ) def test_authenticated_nested_list_endpoints_smoke(self): list_endpoints = [ reverse( - "v1:project-config-list", kwargs={"project_id": self.owner_project.id} + "v1:project-config-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( - "v1:project-entity-list", kwargs={"project_id": self.owner_project.id} + "v1:project-entity-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-entity-candidate-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( - "v1:project-content-list", kwargs={"project_id": self.owner_project.id} + "v1:project-content-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-skill-result-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( - "v1:project-feedback-list", kwargs={"project_id": self.owner_project.id} + "v1:project-feedback-list", + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-ingestion-run-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-mastodon-credentials-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-intake-allowlist-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-newsletter-intake-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-source-config-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-topic-centroid-snapshot-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), reverse( "v1:project-review-queue-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), ] @@ -1143,71 +1403,71 @@ def test_authenticated_nested_detail_endpoints_smoke(self, queue_centroid_mock): reverse( "v1:project-config-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_config.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_config), }, ), reverse( "v1:project-entity-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_entity.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_entity), }, ), reverse( "v1:project-content-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_content.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_content), }, ), reverse( "v1:project-skill-result-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_skill_result.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_skill_result), }, ), reverse( "v1:project-ingestion-run-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_ingestion_run.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_ingestion_run), }, ), reverse( "v1:project-intake-allowlist-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_intake_allowlist.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_intake_allowlist), }, ), reverse( "v1:project-newsletter-intake-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_newsletter_intake.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_newsletter_intake), }, ), reverse( "v1:project-source-config-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_source_config.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_source_config), }, ), reverse( "v1:project-topic-centroid-snapshot-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_topic_centroid_snapshot.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_topic_centroid_snapshot), }, ), reverse( "v1:project-review-queue-detail", kwargs={ - "project_id": self.owner_project.id, - "pk": self.owner_review_queue.id, + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(self.owner_review_queue), }, ), ] @@ -1221,7 +1481,10 @@ def test_authenticated_nested_detail_endpoints_smoke(self, queue_centroid_mock): detail_endpoints.append( reverse( "v1:project-entity-candidate-detail", - kwargs={"project_id": self.owner_project.id, "pk": candidate.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(candidate), + }, ) ) @@ -1234,7 +1497,10 @@ def test_authenticated_nested_detail_endpoints_smoke(self, queue_centroid_mock): detail_endpoints.append( reverse( "v1:project-feedback-detail", - kwargs={"project_id": self.owner_project.id, "pk": feedback.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(feedback), + }, ) ) @@ -1245,7 +1511,10 @@ def test_authenticated_nested_detail_endpoints_smoke(self, queue_centroid_mock): detail_endpoints.append( reverse( "v1:project-bluesky-credentials-detail", - kwargs={"project_id": self.owner_project.id, "pk": credentials.id}, + kwargs={ + "project_id": _require_pk(self.owner_project), + "pk": _require_pk(credentials), + }, ) ) @@ -1258,7 +1527,7 @@ def test_source_config_create_validates_plugin_config(self): response = self.client.post( reverse( "v1:project-source-config-list", - kwargs={"project_id": self.owner_project.id}, + kwargs={"project_id": _require_pk(self.owner_project)}, ), {"plugin_name": SourcePluginName.RSS, "config": {}}, format="json", diff --git a/core/tests/test_bluesky.py b/core/tests/test_bluesky.py index 80443a72..f6d6c99e 100644 --- a/core/tests/test_bluesky.py +++ b/core/tests/test_bluesky.py @@ -7,6 +7,7 @@ Entity, ) from core.plugins.bluesky import BlueskySourcePlugin +from ingestion.plugins.base import ContentItem from projects.model_support import SourcePluginName from projects.models import BlueskyCredentials, Project, SourceConfig @@ -162,8 +163,13 @@ def test_bluesky_match_entity_for_item_uses_bluesky_handle(bluesky_context): plugin = BlueskySourcePlugin(bluesky_context.source_config) result = plugin.match_entity_for_item( - SimpleNamespace( + ContentItem( url="https://irrelevant.example.com/article", + title="Ignored title", + author="alice.bsky.social", + published_date=datetime.now(tz=UTC), + content_text="Ignored body", + source_plugin=SourcePluginName.BLUESKY, source_metadata={"author_handle": "Alice.BSky.social"}, ) ) diff --git a/core/tests/test_embeddings.py b/core/tests/test_embeddings.py index c79990b0..09c2a71c 100644 --- a/core/tests/test_embeddings.py +++ b/core/tests/test_embeddings.py @@ -1,5 +1,6 @@ from io import StringIO from types import SimpleNamespace +from typing import cast from unittest.mock import call import httpx @@ -7,6 +8,7 @@ from django.core.management import CommandError, call_command from django.db.models import Count from qdrant_client.http.exceptions import ResponseHandlingException +from qdrant_client.http.models import FieldCondition, Filter, MatchValue from core import embeddings from core.embeddings import ( @@ -340,12 +342,16 @@ def test_build_search_filter_returns_none_without_conditions(): def test_build_search_filter_supports_reference_and_exclusion_conditions(): - filter_value = build_search_filter(is_reference=True, exclude_content_id=42) + filter_value = cast( + Filter, build_search_filter(is_reference=True, exclude_content_id=42) + ) + must_conditions = cast(list[FieldCondition], filter_value.must) + must_not_conditions = cast(list[FieldCondition], filter_value.must_not) - assert filter_value.must[0].key == "is_reference" - assert filter_value.must[0].match.value is True - assert filter_value.must_not[0].key == "content_id" - assert filter_value.must_not[0].match.value == 42 + assert must_conditions[0].key == "is_reference" + assert cast(MatchValue, must_conditions[0].match).value is True + assert must_not_conditions[0].key == "content_id" + assert cast(MatchValue, must_not_conditions[0].match).value == 42 def test_embedding_smoke_command_prints_dimension(mocker, capsys): @@ -476,7 +482,7 @@ def test_sync_embeddings_scopes_to_requested_content_id(embedding_context, mocke ) upsert_mock.assert_called_once_with(embedding_context.content) - assert sibling_content.id != embedding_context.content.id + assert sibling_content.pk != embedding_context.content.pk assert "Synced embeddings for 1 content item(s)." in stdout.getvalue() diff --git a/core/tests/test_invitations.py b/core/tests/test_invitations.py index 855f7633..5b21354a 100644 --- a/core/tests/test_invitations.py +++ b/core/tests/test_invitations.py @@ -1,13 +1,37 @@ +from typing import Any, cast + from django.core import mail +from django.db.models import Model from django.test import override_settings from django.urls import reverse from rest_framework import status -from rest_framework.test import APITestCase +from rest_framework.test import APIClient, APITestCase from projects.models import Project, ProjectMembership, ProjectRole from users.models import MembershipInvitation +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed invitation test assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _typed_client(client: object) -> APIClient: + """Cast the DRF test client so Pylance sees APIClient helpers.""" + + return cast(APIClient, client) + + +def _create_user(user_model: type[Any], **kwargs: object): + """Create a user via the custom manager with a typed escape hatch.""" + + return cast(Any, user_model.objects).create_user(**kwargs) + + @override_settings( EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend", FRONTEND_BASE_URL="http://localhost:3000", @@ -15,28 +39,33 @@ class ProjectMembershipAndInvitationApiTests(APITestCase): def setUp(self): user_model = self.get_user_model() - self.admin_user = user_model.objects.create_user( + self.admin_user = _create_user( + user_model, username="project-admin", email="admin@example.com", password="testpass123", display_name="Project Admin", ) - self.second_admin = user_model.objects.create_user( + self.second_admin = _create_user( + user_model, username="second-admin", email="second-admin@example.com", password="testpass123", ) - self.member_user = user_model.objects.create_user( + self.member_user = _create_user( + user_model, username="project-member", email="member@example.com", password="testpass123", ) - self.reader_user = user_model.objects.create_user( + self.reader_user = _create_user( + user_model, username="project-reader", email="reader@example.com", password="testpass123", ) - self.invited_user = user_model.objects.create_user( + self.invited_user = _create_user( + user_model, username="invited-user", email="invitee@example.com", password="testpass123", @@ -74,12 +103,13 @@ def get_user_model(): return get_user_model() def test_project_create_assigns_creator_as_admin_membership(self): - creator = self.get_user_model().objects.create_user( + creator = _create_user( + self.get_user_model(), username="creator", email="creator@example.com", password="testpass123", ) - self.client.force_authenticate(creator) + _typed_client(self.client).force_authenticate(creator) response = self.client.post( reverse("v1:project-list"), @@ -98,12 +128,12 @@ def test_project_create_assigns_creator_as_admin_membership(self): self.assertEqual(response.json()["user_role"], ProjectRole.ADMIN) def test_project_admin_can_list_update_and_remove_memberships(self): - self.client.force_authenticate(self.admin_user) + _typed_client(self.client).force_authenticate(self.admin_user) list_response = self.client.get( reverse( "v1:project-membership-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ) ) self.assertEqual(list_response.status_code, status.HTTP_200_OK) @@ -113,8 +143,8 @@ def test_project_admin_can_list_update_and_remove_memberships(self): reverse( "v1:project-membership-detail", kwargs={ - "project_id": self.project.id, - "pk": self.member_membership.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.member_membership), }, ), {"role": ProjectRole.READER}, @@ -128,26 +158,28 @@ def test_project_admin_can_list_update_and_remove_memberships(self): reverse( "v1:project-membership-detail", kwargs={ - "project_id": self.project.id, - "pk": self.reader_membership.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.reader_membership), }, ) ) self.assertEqual(delete_response.status_code, status.HTTP_204_NO_CONTENT) self.assertFalse( - ProjectMembership.objects.filter(pk=self.reader_membership.id).exists() + ProjectMembership.objects.filter( + pk=_require_pk(self.reader_membership) + ).exists() ) def test_last_admin_cannot_be_demoted_or_removed(self): self.second_admin_membership.delete() - self.client.force_authenticate(self.admin_user) + _typed_client(self.client).force_authenticate(self.admin_user) demote_response = self.client.patch( reverse( "v1:project-membership-detail", kwargs={ - "project_id": self.project.id, - "pk": self.admin_membership.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.admin_membership), }, ), {"role": ProjectRole.MEMBER}, @@ -159,20 +191,20 @@ def test_last_admin_cannot_be_demoted_or_removed(self): reverse( "v1:project-membership-detail", kwargs={ - "project_id": self.project.id, - "pk": self.admin_membership.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.admin_membership), }, ) ) self.assertEqual(delete_response.status_code, status.HTTP_400_BAD_REQUEST) def test_project_admin_can_create_and_revoke_invitation(self): - self.client.force_authenticate(self.admin_user) + _typed_client(self.client).force_authenticate(self.admin_user) create_response = self.client.post( reverse( "v1:project-invitation-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ), {"email": "invitee@example.com", "role": ProjectRole.MEMBER}, format="json", @@ -182,12 +214,15 @@ def test_project_admin_can_create_and_revoke_invitation(self): invitation = MembershipInvitation.objects.get(project=self.project) self.assertEqual(invitation.invited_by, self.admin_user) self.assertEqual(len(mail.outbox), 1) - self.assertIn(invitation.token, mail.outbox[0].body) + self.assertIn(invitation.token, str(mail.outbox[0].body)) revoke_response = self.client.delete( reverse( "v1:project-invitation-detail", - kwargs={"project_id": self.project.id, "pk": invitation.id}, + kwargs={ + "project_id": _require_pk(self.project), + "pk": _require_pk(invitation), + }, ) ) self.assertEqual(revoke_response.status_code, status.HTTP_204_NO_CONTENT) @@ -211,7 +246,7 @@ def test_invited_user_can_view_and_accept_invitation_token(self): self.assertEqual(public_response.status_code, status.HTTP_200_OK) self.assertEqual(public_response.json()["project_name"], self.project.name) - self.client.force_authenticate(self.invited_user) + _typed_client(self.client).force_authenticate(self.invited_user) accept_response = self.client.post( reverse( "membership-invitation-token", @@ -236,7 +271,7 @@ def test_accept_requires_matching_email(self): role=ProjectRole.MEMBER, invited_by=self.admin_user, ) - self.client.force_authenticate(self.member_user) + _typed_client(self.client).force_authenticate(self.member_user) response = self.client.post( reverse( diff --git a/core/tests/test_mastodon.py b/core/tests/test_mastodon.py index df847aad..24857977 100644 --- a/core/tests/test_mastodon.py +++ b/core/tests/test_mastodon.py @@ -5,6 +5,7 @@ from core.models import Entity from core.plugins.mastodon import MastodonSourcePlugin +from ingestion.plugins.base import ContentItem from projects.model_support import SourcePluginName from projects.models import MastodonCredentials, Project, SourceConfig @@ -161,8 +162,13 @@ def test_mastodon_match_entity_for_item_uses_mastodon_handle(mastodon_context): plugin = MastodonSourcePlugin(mastodon_context.source_config) result = plugin.match_entity_for_item( - SimpleNamespace( + ContentItem( url="https://irrelevant.example.com/article", + title="Ignored title", + author="Alice Example", + published_date=datetime.now(tz=UTC), + content_text="Ignored body", + source_plugin=SourcePluginName.MASTODON, source_metadata={"author_acct": "Alice@Hachyderm.io"}, ) ) diff --git a/core/tests/test_newsletters.py b/core/tests/test_newsletters.py index d5a14a9b..396d4575 100644 --- a/core/tests/test_newsletters.py +++ b/core/tests/test_newsletters.py @@ -2,9 +2,12 @@ from base64 import b64encode from datetime import datetime, timezone from types import SimpleNamespace +from typing import cast import pytest from django.core import mail +from django.core.mail import EmailMultiAlternatives +from django.db.models import Model from django.urls import reverse from svix.webhooks import Webhook @@ -25,6 +28,15 @@ pytestmark = pytest.mark.django_db +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed newsletter test assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + @pytest.fixture def project(): return Project.objects.create( @@ -170,7 +182,7 @@ def test_handle_anymail_inbound_queues_confirmed_sender(settings, mocker, projec handle_anymail_inbound(sender=object(), event=event, esp_name="Resend") intake = NewsletterIntake.objects.get(message_id="msg-456") - delay_mock.assert_called_once_with(intake.id) + delay_mock.assert_called_once_with(_require_pk(intake)) send_mock.assert_not_called() @@ -311,7 +323,7 @@ def test_send_confirmation_email_uses_django_mail_backend(settings): ) assert len(mail.outbox) == 1 - message = mail.outbox[0] + message = cast(EmailMultiAlternatives, mail.outbox[0]) assert ( message.subject == "Confirm newsletter intake for Platform Engineering Weekly" ) @@ -319,7 +331,9 @@ def test_send_confirmation_email_uses_django_mail_backend(settings): assert message.to == ["newsletter@example.com"] assert "https://example.com/confirm/token" in message.body assert any( - mimetype == "text/html" and "Confirm sender" in content + mimetype == "text/html" + and isinstance(content, str) + and "Confirm sender" in content for content, mimetype in message.alternatives ) @@ -349,7 +363,7 @@ def test_confirm_newsletter_sender_confirms_allowlist_and_queues_pending_intakes assert response.status_code == 200 allowlist.refresh_from_db() assert allowlist.confirmed_at is not None - delay_mock.assert_called_once_with(intake.id) + delay_mock.assert_called_once_with(_require_pk(intake)) def test_process_newsletter_intake_creates_content_for_confirmed_sender( @@ -374,7 +388,7 @@ def test_process_newsletter_intake_creates_content_for_confirmed_sender( from core.tasks import process_newsletter_intake - result = process_newsletter_intake(intake.id) + result = process_newsletter_intake(_require_pk(intake)) assert result["items_ingested"] == 1 intake.refresh_from_db() @@ -382,6 +396,6 @@ def test_process_newsletter_intake_creates_content_for_confirmed_sender( assert intake.status == NewsletterIntakeStatus.EXTRACTED assert intake.extraction_result["method"] == "heuristic" assert content.source_plugin == "newsletter" - assert content.source_metadata["newsletter_intake_id"] == intake.id + assert content.source_metadata["newsletter_intake_id"] == _require_pk(intake) upsert_mock.assert_called_once_with(content) - delay_mock.assert_called_once_with(content.id) + delay_mock.assert_called_once_with(_require_pk(content)) diff --git a/core/tests/test_permissions.py b/core/tests/test_permissions.py index 39a543d6..37c28dd9 100644 --- a/core/tests/test_permissions.py +++ b/core/tests/test_permissions.py @@ -1,9 +1,11 @@ +from typing import Any, cast from unittest.mock import patch from django.contrib.auth import get_user_model +from django.db.models import Model from django.urls import reverse from rest_framework import status -from rest_framework.test import APITestCase +from rest_framework.test import APIClient, APITestCase from core.models import ( Content, @@ -12,13 +14,36 @@ FeedbackType, ReviewQueue, ReviewReason, + ThemeSuggestion, TopicCentroidSnapshot, + TopicCluster, UserFeedback, ) from projects.model_support import SourcePluginName from projects.models import BlueskyCredentials, Project, ProjectMembership, ProjectRole +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed permission test assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _typed_client(client: object) -> APIClient: + """Cast the DRF test client so Pylance sees APIClient methods.""" + + return cast(APIClient, client) + + +def _create_user(user_model: type[Any], **kwargs: object): + """Create a user through the custom manager with a typed escape hatch.""" + + return cast(Any, user_model.objects).create_user(**kwargs) + + class ProjectRolePermissionTests(APITestCase): def setUp(self): queue_centroid_patcher = patch("core.signals.queue_topic_centroid_recompute") @@ -26,19 +51,23 @@ def setUp(self): self.addCleanup(queue_centroid_patcher.stop) user_model = get_user_model() - self.admin_user = user_model.objects.create_user( + self.admin_user = _create_user( + user_model, username="project-admin", password="testpass123", ) - self.member_user = user_model.objects.create_user( + self.member_user = _create_user( + user_model, username="project-member", password="testpass123", ) - self.reader_user = user_model.objects.create_user( + self.reader_user = _create_user( + user_model, username="project-reader", password="testpass123", ) - self.outsider_user = user_model.objects.create_user( + self.outsider_user = _create_user( + user_model, username="outsider", password="testpass123", ) @@ -105,6 +134,24 @@ def setUp(self): drift_from_previous=0.1, drift_from_week_ago=0.2, ) + self.topic_cluster = TopicCluster.objects.create( + project=self.project, + first_seen_at="2026-04-28T00:00:00Z", + last_seen_at="2026-04-29T00:00:00Z", + is_active=True, + member_count=1, + dominant_entity=self.entity, + ) + self.theme_suggestion = ThemeSuggestion.objects.create( + project=self.project, + cluster=self.topic_cluster, + title="Permissions Theme", + pitch="Pitch", + why_it_matters="Why", + suggested_angle="Angle", + velocity_at_creation=0.8, + novelty_score=0.7, + ) self.member_feedback = UserFeedback.objects.create( project=self.project, content=self.content, @@ -123,7 +170,7 @@ def setUp(self): ) def test_project_list_includes_resolved_reader_role(self): - self.client.force_authenticate(self.reader_user) + _typed_client(self.client).force_authenticate(self.reader_user) response = self.client.get(reverse("v1:project-list")) @@ -131,19 +178,19 @@ def test_project_list_includes_resolved_reader_role(self): self.assertEqual(response.json()[0]["user_role"], ProjectRole.READER) def test_reader_is_denied_contributor_and_admin_endpoints(self): - self.client.force_authenticate(self.reader_user) + _typed_client(self.client).force_authenticate(self.reader_user) cases = [ ( "patch", - reverse("v1:project-detail", kwargs={"id": self.project.id}), + reverse("v1:project-detail", kwargs={"id": _require_pk(self.project)}), {"name": "Reader Update"}, ), ( "post", reverse( "v1:project-rotate-intake-token", - kwargs={"id": self.project.id}, + kwargs={"id": _require_pk(self.project)}, ), None, ), @@ -151,7 +198,7 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): "get", reverse( "v1:project-review-queue-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ), None, ), @@ -159,7 +206,7 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): "post", reverse( "v1:project-source-config-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ), { "plugin_name": SourcePluginName.RSS, @@ -171,7 +218,7 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): "get", reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ), None, ), @@ -179,10 +226,10 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): "post", reverse( "v1:project-feedback-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ), { - "content": self.content.id, + "content": _require_pk(self.content), "feedback_type": FeedbackType.UPVOTE, }, ), @@ -191,8 +238,8 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): reverse( "v1:project-entity-candidate-accept", kwargs={ - "project_id": self.project.id, - "pk": self.entity_candidate.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.entity_candidate), }, ), None, @@ -201,7 +248,18 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): "get", reverse( "v1:project-topic-centroid-snapshot-summary", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, + ), + None, + ), + ( + "post", + reverse( + "v1:project-theme-suggestion-accept", + kwargs={ + "project_id": _require_pk(self.project), + "pk": _require_pk(self.theme_suggestion), + }, ), None, ), @@ -213,12 +271,12 @@ def test_reader_is_denied_contributor_and_admin_endpoints(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): - self.client.force_authenticate(self.member_user) + _typed_client(self.client).force_authenticate(self.member_user) review_queue_response = self.client.get( reverse( "v1:project-review-queue-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ) ) self.assertEqual(review_queue_response.status_code, status.HTTP_200_OK) @@ -226,7 +284,7 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): source_config_response = self.client.post( reverse( "v1:project-source-config-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ), { "plugin_name": SourcePluginName.RSS, @@ -240,7 +298,7 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): topic_summary_response = self.client.get( reverse( "v1:project-topic-centroid-snapshot-summary", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ) ) self.assertEqual(topic_summary_response.status_code, status.HTTP_200_OK) @@ -249,8 +307,8 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): reverse( "v1:project-entity-candidate-accept", kwargs={ - "project_id": self.project.id, - "pk": self.entity_candidate.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.entity_candidate), }, ), format="json", @@ -261,8 +319,8 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): reverse( "v1:project-feedback-detail", kwargs={ - "project_id": self.project.id, - "pk": self.member_feedback.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.member_feedback), }, ) ) @@ -271,7 +329,7 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): ) update_project_response = self.client.patch( - reverse("v1:project-detail", kwargs={"id": self.project.id}), + reverse("v1:project-detail", kwargs={"id": _require_pk(self.project)}), {"name": "Member Update"}, format="json", ) @@ -280,7 +338,7 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): list_credentials_response = self.client.get( reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ) ) self.assertEqual( @@ -288,20 +346,23 @@ def test_member_can_use_contributor_endpoints_but_not_admin_only_ones(self): ) rotate_token_response = self.client.post( - reverse("v1:project-rotate-intake-token", kwargs={"id": self.project.id}), + reverse( + "v1:project-rotate-intake-token", + kwargs={"id": _require_pk(self.project)}, + ), format="json", ) self.assertEqual(rotate_token_response.status_code, status.HTTP_403_FORBIDDEN) def test_member_cannot_delete_other_users_feedback(self): - self.client.force_authenticate(self.member_user) + _typed_client(self.client).force_authenticate(self.member_user) response = self.client.delete( reverse( "v1:project-feedback-detail", kwargs={ - "project_id": self.project.id, - "pk": self.admin_feedback.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.admin_feedback), }, ) ) @@ -309,10 +370,10 @@ def test_member_cannot_delete_other_users_feedback(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_admin_can_access_admin_endpoints_and_delete_other_feedback(self): - self.client.force_authenticate(self.admin_user) + _typed_client(self.client).force_authenticate(self.admin_user) update_project_response = self.client.patch( - reverse("v1:project-detail", kwargs={"id": self.project.id}), + reverse("v1:project-detail", kwargs={"id": _require_pk(self.project)}), {"name": "Admin Updated Project"}, format="json", ) @@ -321,13 +382,16 @@ def test_admin_can_access_admin_endpoints_and_delete_other_feedback(self): list_credentials_response = self.client.get( reverse( "v1:project-bluesky-credentials-list", - kwargs={"project_id": self.project.id}, + kwargs={"project_id": _require_pk(self.project)}, ) ) self.assertEqual(list_credentials_response.status_code, status.HTTP_200_OK) rotate_token_response = self.client.post( - reverse("v1:project-rotate-intake-token", kwargs={"id": self.project.id}), + reverse( + "v1:project-rotate-intake-token", + kwargs={"id": _require_pk(self.project)}, + ), format="json", ) self.assertEqual(rotate_token_response.status_code, status.HTTP_200_OK) @@ -336,8 +400,8 @@ def test_admin_can_access_admin_endpoints_and_delete_other_feedback(self): reverse( "v1:project-feedback-detail", kwargs={ - "project_id": self.project.id, - "pk": self.member_feedback.id, + "project_id": _require_pk(self.project), + "pk": _require_pk(self.member_feedback), }, ) ) @@ -346,10 +410,10 @@ def test_admin_can_access_admin_endpoints_and_delete_other_feedback(self): ) def test_outsider_cannot_access_project_resources(self): - self.client.force_authenticate(self.outsider_user) + _typed_client(self.client).force_authenticate(self.outsider_user) response = self.client.get( - reverse("v1:project-detail", kwargs={"id": self.project.id}) + reverse("v1:project-detail", kwargs={"id": _require_pk(self.project)}) ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) diff --git a/core/tests/test_pipeline.py b/core/tests/test_pipeline.py index 0fc461ae..a2249866 100644 --- a/core/tests/test_pipeline.py +++ b/core/tests/test_pipeline.py @@ -1,6 +1,7 @@ from types import SimpleNamespace import pytest +from django.db.models import Model from core.deduplication import canonicalize_url from core.models import ( @@ -44,6 +45,15 @@ pytestmark = pytest.mark.django_db +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed pipeline assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + @pytest.fixture def pipeline_context(django_user_model): user = django_user_model.objects.create_user( @@ -116,7 +126,7 @@ def test_process_content_runs_full_pipeline_for_relevant_content( }, ) - result = process_content(pipeline_context.content.id) + result = process_content(_require_pk(pipeline_context.content)) pipeline_context.content.refresh_from_db() assert result["status"] == "completed" @@ -219,7 +229,7 @@ def test_process_content_uses_top_entity_mention_for_authority_adjustment( }, ) - process_content(pipeline_context.content.id) + process_content(_require_pk(pipeline_context.content)) pipeline_context.content.refresh_from_db() assert pipeline_context.content.authority_adjusted_score == pytest.approx(0.872) @@ -401,12 +411,12 @@ def test_process_content_marks_exact_duplicates_and_skips_downstream_skills( relevance_mock = mocker.patch("core.pipeline.run_relevance_scoring") summarize_mock = mocker.patch("core.pipeline.run_summarization") - result = process_content(duplicate.id) + result = process_content(_require_pk(duplicate)) duplicate.refresh_from_db() existing.refresh_from_db() assert result["status"] == "duplicate" - assert duplicate.duplicate_of_id == existing.id + assert duplicate.duplicate_of == existing assert duplicate.is_active is False assert existing.duplicate_signal_count == 1 assert classify_mock.call_count == 0 @@ -444,16 +454,18 @@ def test_process_content_marks_semantic_duplicates_with_high_similarity( ) mocker.patch( "core.pipeline.search_similar_content", - return_value=[SimpleNamespace(score=0.95, payload={"content_id": existing.id})], + return_value=[ + SimpleNamespace(score=0.95, payload={"content_id": _require_pk(existing)}) + ], ) classify_mock = mocker.patch("core.pipeline.run_content_classification") - result = process_content(candidate.id) + result = process_content(_require_pk(candidate)) candidate.refresh_from_db() existing.refresh_from_db() assert result["status"] == "duplicate" - assert candidate.duplicate_of_id == existing.id + assert candidate.duplicate_of == existing assert candidate.is_active is False assert existing.duplicate_signal_count == 1 assert classify_mock.call_count == 0 @@ -721,7 +733,7 @@ def test_execute_ad_hoc_classification_supersedes_previous_result_and_updates_re assert classification_mock.call_count == 2 assert first_result.status == SkillStatus.COMPLETED assert second_result.status == SkillStatus.COMPLETED - assert first_result.superseded_by_id == second_result.id + assert first_result.superseded_by == second_result assert pipeline_context.content.content_type == "tutorial" assert review_item.confidence == pytest.approx(0.45) assert ( @@ -887,7 +899,9 @@ def test_execute_background_skill_result_rejects_skill_name_mismatch(pipeline_co ) with pytest.raises(ValueError, match="is for relevance_scoring, not summarization"): - execute_background_skill_result(pending_result.id, SUMMARIZATION_SKILL_NAME) + execute_background_skill_result( + _require_pk(pending_result), SUMMARIZATION_SKILL_NAME + ) def test_execute_background_skill_result_completes_summary_when_requirements_are_met( @@ -909,7 +923,7 @@ def test_execute_background_skill_result_completes_summary_when_requirements_are ) result = execute_background_skill_result( - pending_result.id, SUMMARIZATION_SKILL_NAME + _require_pk(pending_result), SUMMARIZATION_SKILL_NAME ) pending_result.refresh_from_db() @@ -952,7 +966,9 @@ def test_execute_background_skill_result_uses_adjusted_score_for_relevance_confi }, ) - result = execute_background_skill_result(pending_result.id, RELEVANCE_SKILL_NAME) + result = execute_background_skill_result( + _require_pk(pending_result), RELEVANCE_SKILL_NAME + ) pending_result.refresh_from_db() pipeline_context.content.refresh_from_db() @@ -1005,7 +1021,7 @@ def test_execute_background_skill_result_completes_summary_when_adjusted_score_p ) result = execute_background_skill_result( - pending_result.id, SUMMARIZATION_SKILL_NAME + _require_pk(pending_result), SUMMARIZATION_SKILL_NAME ) pending_result.refresh_from_db() @@ -1031,7 +1047,9 @@ def test_execute_background_skill_result_marks_relevance_failed_when_execution_e side_effect=RuntimeError("embedding unavailable"), ) - result = execute_background_skill_result(pending_result.id, RELEVANCE_SKILL_NAME) + result = execute_background_skill_result( + _require_pk(pending_result), RELEVANCE_SKILL_NAME + ) pending_result.refresh_from_db() assert result.status == SkillStatus.FAILED @@ -1183,7 +1201,9 @@ def test_run_entity_extraction_persists_mentions_and_candidates( pipeline_context.content.save(update_fields=["title", "content_text"]) mocker.patch( "core.entity_extraction.search_similar_entities_for_content", - return_value=[SimpleNamespace(score=0.91, payload={"entity_id": entity.id})], + return_value=[ + SimpleNamespace(score=0.91, payload={"entity_id": _require_pk(entity)}) + ], ) result = run_entity_extraction(pipeline_context.content) @@ -1196,8 +1216,8 @@ def test_run_entity_extraction_persists_mentions_and_candidates( assert mention.role == "subject" assert mention.span == "Acme Cloud" - assert result["primary_entity_id"] == entity.id - assert pipeline_context.content.entity_id == entity.id + assert result["primary_entity_id"] == _require_pk(entity) + assert pipeline_context.content.entity == entity assert candidate.suggested_type == "vendor" assert candidate.occurrence_count == 1 @@ -1225,7 +1245,7 @@ def test_process_content_records_entity_extraction_skill_result( return_value={ "mentions": [ { - "entity_id": entity.id, + "entity_id": _require_pk(entity), "entity_name": entity.name, "role": "subject", "sentiment": "neutral", @@ -1234,7 +1254,7 @@ def test_process_content_records_entity_extraction_skill_result( } ], "candidate_entities": [], - "primary_entity_id": entity.id, + "primary_entity_id": _require_pk(entity), "confidence": 0.88, "explanation": "Tracked entity matched in the title.", "model_used": "heuristic", diff --git a/core/tests/test_plugin_base.py b/core/tests/test_plugin_base.py index f425c097..e16d83e0 100644 --- a/core/tests/test_plugin_base.py +++ b/core/tests/test_plugin_base.py @@ -1,5 +1,6 @@ from datetime import UTC, datetime from types import SimpleNamespace +from typing import Callable, cast import pytest @@ -119,12 +120,20 @@ def test_source_plugin_match_entity_for_item_delegates_to_url_matching(plugin_co def test_source_plugin_abstract_methods_raise_not_implemented(plugin_context): plugin = DummySourcePlugin(plugin_context.source_config) + fetch_method = cast( + Callable[[SourcePlugin, datetime | None], list[ContentItem]], + SourcePlugin.__dict__["fetch_new_content"], + ) + health_check_method = cast( + Callable[[SourcePlugin], bool], + SourcePlugin.__dict__["health_check"], + ) with pytest.raises(NotImplementedError): - SourcePlugin.fetch_new_content(plugin, since=None) + fetch_method(plugin, None) with pytest.raises(NotImplementedError): - SourcePlugin.health_check(plugin) + health_check_method(plugin) def test_dummy_source_plugin_implements_abstract_contract(plugin_context): diff --git a/core/tests/test_serializers.py b/core/tests/test_serializers.py index 8d8c8eeb..64e371be 100644 --- a/core/tests/test_serializers.py +++ b/core/tests/test_serializers.py @@ -1,7 +1,10 @@ from types import SimpleNamespace +from typing import Any, cast import pytest from django.contrib.auth.models import AnonymousUser +from django.db.models import Model +from rest_framework import serializers as drf_serializers from core.models import ( Content, @@ -18,7 +21,13 @@ UserFeedbackSerializer, ) from projects.model_support import SourcePluginName -from projects.models import Project, ProjectMembership, ProjectRole, SourceConfig +from projects.models import ( + MastodonCredentials, + Project, + ProjectMembership, + ProjectRole, + SourceConfig, +) from projects.serializers import ( MastodonCredentialsSerializer, ProjectSerializer, @@ -28,6 +37,37 @@ pytestmark = pytest.mark.django_db +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed serializer assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _serializer_fields( + serializer: drf_serializers.BaseSerializer[Any], +) -> dict[str, Any]: + """Return serializer fields with a mapping type Pylance can index.""" + + return cast(dict[str, Any], cast(Any, serializer).fields) + + +def _serializer_data(serializer: drf_serializers.BaseSerializer[Any]) -> dict[str, Any]: + """Return serializer output data as a dictionary for typed assertions.""" + + return cast(dict[str, Any], serializer.data) + + +def _validated_data( + serializer: drf_serializers.BaseSerializer[Any], +) -> dict[str, Any]: + """Return validated serializer data as a standard dictionary.""" + + return cast(dict[str, Any], serializer.validated_data) + + @pytest.fixture def serializer_context(django_user_model): user = django_user_model.objects.create_user( @@ -115,12 +155,11 @@ def test_project_scoped_serializer_filters_related_querysets_with_project_contex "project": serializer_context.project, } ) + fields = _serializer_fields(serializer) - assert list(serializer.fields["content"].queryset) == [serializer_context.content] - assert list(serializer.fields["superseded_by"].queryset) == [ - serializer_context.skill_result - ] - assert list(serializer.fields["project"].queryset) == [serializer_context.project] + assert list(fields["content"].queryset) == [serializer_context.content] + assert list(fields["superseded_by"].queryset) == [serializer_context.skill_result] + assert list(fields["project"].queryset) == [serializer_context.project] def test_project_scoped_serializer_filters_related_querysets_without_project_context( @@ -129,21 +168,22 @@ def test_project_scoped_serializer_filters_related_querysets_without_project_con serializer = ContentSerializer( context={"request": _request_for(serializer_context.user)} ) + fields = _serializer_fields(serializer) - assert list(serializer.fields["entity"].queryset) == [serializer_context.entity] - assert list(serializer.fields["project"].queryset) == [serializer_context.project] + assert list(fields["entity"].queryset) == [serializer_context.entity] + assert list(fields["project"].queryset) == [serializer_context.project] def test_project_scoped_serializer_skips_filtering_for_anonymous_user(): serializer = ProjectSerializer(context={"request": _request_for(AnonymousUser())}) - assert "project" not in serializer.fields + assert "project" not in _serializer_fields(serializer) def test_content_serializer_rejects_cross_project_entity(serializer_context): serializer = ContentSerializer( instance=serializer_context.content, - data={"entity": serializer_context.other_entity.id}, + data={"entity": _require_pk(serializer_context.other_entity)}, partial=True, context={"project": serializer_context.project}, ) @@ -176,10 +216,11 @@ def test_content_serializer_exposes_duplicate_state_as_read_only_fields( ) serializer = ContentSerializer(instance=duplicate) + data = _serializer_data(serializer) - assert serializer.data["canonical_url"] == "https://example.com/serializer-content" - assert serializer.data["duplicate_of"] == serializer_context.content.id - assert serializer.data["duplicate_signal_count"] == 0 + assert data["canonical_url"] == "https://example.com/serializer-content" + assert data["duplicate_of"] == _require_pk(serializer_context.content) + assert data["duplicate_signal_count"] == 0 def test_content_serializer_ignores_duplicate_fields_on_update(serializer_context): @@ -187,7 +228,7 @@ def test_content_serializer_ignores_duplicate_fields_on_update(serializer_contex instance=serializer_context.content, data={ "canonical_url": "https://malicious.example/canonical", - "duplicate_of": serializer_context.other_content.id, + "duplicate_of": _require_pk(serializer_context.other_content), "duplicate_signal_count": 99, }, partial=True, @@ -195,7 +236,7 @@ def test_content_serializer_ignores_duplicate_fields_on_update(serializer_contex ) assert serializer.is_valid(), serializer.errors - updated = serializer.save() + updated = cast(Content, serializer.save()) assert updated.canonical_url == "" assert updated.duplicate_of is None @@ -205,7 +246,7 @@ def test_content_serializer_ignores_duplicate_fields_on_update(serializer_contex def test_skill_result_serializer_rejects_cross_project_content(serializer_context): serializer = SkillResultSerializer( data={ - "content": serializer_context.other_content.id, + "content": _require_pk(serializer_context.other_content), "skill_name": "summarization", "status": "completed", }, @@ -223,7 +264,7 @@ def test_skill_result_serializer_rejects_cross_project_content(serializer_contex def test_review_queue_serializer_rejects_cross_project_content(serializer_context): serializer = ReviewQueueSerializer( data={ - "content": serializer_context.other_content.id, + "content": _require_pk(serializer_context.other_content), "reason": ReviewReason.BORDERLINE_RELEVANCE, "confidence": 0.5, }, @@ -252,7 +293,7 @@ def test_source_config_serializer_normalizes_valid_config(serializer_context): ) assert serializer.is_valid(), serializer.errors - assert serializer.validated_data["config"] == { + assert _validated_data(serializer)["config"] == { "feed_url": "https://example.com/feed.xml" } @@ -292,7 +333,7 @@ def test_source_config_serializer_normalizes_bluesky_author_handle_config( ) assert serializer.is_valid(), serializer.errors - assert serializer.validated_data["config"] == { + assert _validated_data(serializer)["config"] == { "author_handle": "alice.bsky.social", "include_replies": False, "max_posts_per_fetch": 100, @@ -318,7 +359,7 @@ def test_source_config_serializer_normalizes_mastodon_hashtag_config( ) assert serializer.is_valid(), serializer.errors - assert serializer.validated_data["config"] == { + assert _validated_data(serializer)["config"] == { "instance_url": "https://hachyderm.io", "hashtag": "platformengineering", "include_replies": False, @@ -342,7 +383,10 @@ def test_mastodon_credentials_serializer_encrypts_access_token(serializer_contex ) assert serializer.is_valid(), serializer.errors - credentials = serializer.save(project=serializer_context.project) + credentials = cast( + MastodonCredentials, + serializer.save(project=serializer_context.project), + ) assert credentials.instance_url == "https://hachyderm.io" assert credentials.account_acct == "alice@hachyderm.io" @@ -355,13 +399,15 @@ def test_entity_serializer_filters_project_queryset_to_request_user(serializer_c context={"request": _request_for(serializer_context.user)} ) - assert list(serializer.fields["project"].queryset) == [serializer_context.project] + assert list(_serializer_fields(serializer)["project"].queryset) == [ + serializer_context.project + ] def test_user_feedback_serializer_rejects_cross_project_content(serializer_context): serializer = UserFeedbackSerializer( data={ - "content": serializer_context.other_content.id, + "content": _require_pk(serializer_context.other_content), "feedback_type": "upvote", }, context={ @@ -378,7 +424,7 @@ def test_user_feedback_serializer_rejects_cross_project_content(serializer_conte def test_review_queue_serializer_accepts_same_project_content(serializer_context): serializer = ReviewQueueSerializer( data={ - "content": serializer_context.content.id, + "content": _require_pk(serializer_context.content), "reason": ReviewReason.BORDERLINE_RELEVANCE, "confidence": 0.5, }, @@ -388,7 +434,8 @@ def test_review_queue_serializer_accepts_same_project_content(serializer_context ) assert serializer.is_valid(), serializer.errors - assert serializer.validated_data["content"] == serializer_context.content + validated_data = cast(dict[str, Content | Any], serializer.validated_data) + assert validated_data["content"] == serializer_context.content def test_source_config_serializer_skips_plugin_validation_when_plugin_name_missing( @@ -406,7 +453,7 @@ def test_source_config_serializer_skips_plugin_validation_when_plugin_name_missi ) assert serializer.is_valid(), serializer.errors - assert serializer.validated_data["config"] == {} + assert _validated_data(serializer)["config"] == {} def test_ingestion_run_serializer_filters_project_queryset(serializer_context): @@ -414,4 +461,6 @@ def test_ingestion_run_serializer_filters_project_queryset(serializer_context): context={"request": _request_for(serializer_context.user)} ) - assert list(serializer.fields["project"].queryset) == [serializer_context.project] + assert list(_serializer_fields(serializer)["project"].queryset) == [ + serializer_context.project + ] diff --git a/core/tests/test_tasks.py b/core/tests/test_tasks.py index e44119d3..f2007c10 100644 --- a/core/tests/test_tasks.py +++ b/core/tests/test_tasks.py @@ -2,6 +2,7 @@ from types import SimpleNamespace import pytest +from django.db.models import Model from core.models import ( Content, @@ -14,6 +15,8 @@ IngestionRun, RunStatus, SkillStatus, + ThemeSuggestion, + ThemeSuggestionStatus, TopicCentroidSnapshot, TopicCluster, TopicVelocitySnapshot, @@ -32,7 +35,9 @@ from projects.models import Project, ProjectConfig, SourceConfig from trends.tasks import ( TOPIC_CENTROID_MIN_UPVOTES, + accept_theme_suggestion, assign_content_to_topic_cluster, + generate_theme_suggestions, queue_topic_centroid_recompute, recompute_topic_centroid, recompute_topic_clusters, @@ -44,6 +49,15 @@ pytestmark = pytest.mark.django_db +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for typed task assertions.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + @pytest.fixture def source_plugin_context(django_user_model): user = django_user_model.objects.create_user( @@ -82,7 +96,7 @@ def test_run_ingestion_creates_content_from_rss_entries(source_plugin_context, m ] ) - result = run_ingestion(source_config.id) + result = run_ingestion(_require_pk(source_config)) assert result["items_fetched"] == 1 assert result["items_ingested"] == 1 @@ -90,8 +104,11 @@ def test_run_ingestion_creates_content_from_rss_entries(source_plugin_context, m assert content.project == source_plugin_context.project assert content.entity == source_plugin_context.entity upsert_embedding_mock.assert_called_once_with(content) - process_content_delay_mock.assert_called_once_with(content.id) - assert SourceConfig.objects.get(pk=source_config.id).last_fetched_at is not None + process_content_delay_mock.assert_called_once_with(_require_pk(content)) + assert ( + SourceConfig.objects.get(pk=_require_pk(source_config)).last_fetched_at + is not None + ) ingestion_run = IngestionRun.objects.get( project=source_plugin_context.project, plugin_name=SourcePluginName.RSS ) @@ -131,7 +148,7 @@ def test_run_ingestion_skips_same_source_duplicate_urls(source_plugin_context, m ] ) - result = run_ingestion(source_config.id) + result = run_ingestion(_require_pk(source_config)) assert result["items_fetched"] == 1 assert result["items_ingested"] == 0 @@ -209,13 +226,13 @@ def test_run_ingestion_creates_content_from_reddit_posts(source_plugin_context, ) reddit_mock.return_value.subreddit.return_value = subreddit - result = run_ingestion(source_config.id) + result = run_ingestion(_require_pk(source_config)) assert result["items_fetched"] == 1 assert result["items_ingested"] == 1 content = Content.objects.get(title="Reddit Post") upsert_embedding_mock.assert_called_once_with(content) - process_content_delay_mock.assert_called_once_with(content.id) + process_content_delay_mock.assert_called_once_with(_require_pk(content)) assert content.source_plugin == SourcePluginName.REDDIT assert content.entity is None @@ -347,8 +364,8 @@ def test_run_all_ingestions_enqueues_active_source_configs( enqueued_count = run_all_ingestions() assert enqueued_count == 2 - delay_mock.assert_any_call(active_one.id) - delay_mock.assert_any_call(active_two.id) + delay_mock.assert_any_call(_require_pk(active_one)) + delay_mock.assert_any_call(_require_pk(active_two)) assert delay_mock.call_count == 2 @@ -372,8 +389,8 @@ def test_run_all_ingestions_executes_inline_when_eager( enqueued_count = run_all_ingestions() assert enqueued_count == 2 - run_ingestion_mock.assert_any_call(active_one.id) - run_ingestion_mock.assert_any_call(active_two.id) + run_ingestion_mock.assert_any_call(_require_pk(active_one)) + run_ingestion_mock.assert_any_call(_require_pk(active_two)) assert run_ingestion_mock.call_count == 2 delay_mock.assert_not_called() @@ -391,7 +408,7 @@ def test_run_all_authority_recomputations_enqueues_all_projects( assert enqueued_count == 2 delay_mock.assert_any_call(source_plugin_context.project.id) - delay_mock.assert_any_call(other_project.id) + delay_mock.assert_any_call(_require_pk(other_project)) assert delay_mock.call_count == 2 @@ -410,7 +427,7 @@ def test_run_all_authority_recomputations_executes_inline_when_eager( assert enqueued_count == 2 recompute_mock.assert_any_call(source_plugin_context.project.id) - recompute_mock.assert_any_call(other_project.id) + recompute_mock.assert_any_call(_require_pk(other_project)) assert recompute_mock.call_count == 2 delay_mock.assert_not_called() @@ -428,7 +445,7 @@ def test_run_all_topic_centroid_recomputations_enqueues_all_projects( assert enqueued_count == 2 delay_mock.assert_any_call(source_plugin_context.project.id) - delay_mock.assert_any_call(other_project.id) + delay_mock.assert_any_call(_require_pk(other_project)) assert delay_mock.call_count == 2 @@ -447,7 +464,7 @@ def test_run_all_topic_centroid_recomputations_executes_inline_when_eager( assert enqueued_count == 2 recompute_mock.assert_any_call(source_plugin_context.project.id) - recompute_mock.assert_any_call(other_project.id) + recompute_mock.assert_any_call(_require_pk(other_project)) assert recompute_mock.call_count == 2 delay_mock.assert_not_called() @@ -465,7 +482,7 @@ def test_run_all_topic_cluster_recomputations_enqueues_all_projects( assert enqueued_count == 2 delay_mock.assert_any_call(source_plugin_context.project.id) - delay_mock.assert_any_call(other_project.id) + delay_mock.assert_any_call(_require_pk(other_project)) assert delay_mock.call_count == 2 @@ -785,14 +802,18 @@ def test_recompute_topic_clusters_groups_recent_similar_content( result = recompute_topic_clusters(project.id) cluster = TopicCluster.objects.get(project=project, is_active=True) - memberships = list(cluster.memberships.values_list("content_id", flat=True)) + memberships = list( + ContentClusterMembership.objects.filter(cluster=cluster).values_list( + "content_id", flat=True + ) + ) assert result["contents_considered"] == 5 assert result["clusters_updated"] == 1 assert cluster.member_count == 4 assert cluster.dominant_entity == source_plugin_context.entity - assert set(memberships) == {content.id for content in clustered_contents} - assert outlier.id not in memberships + assert set(memberships) == {_require_pk(content) for content in clustered_contents} + assert _require_pk(outlier) not in memberships delay_mock.assert_called_once_with(project.id) @@ -854,12 +875,12 @@ def test_assign_content_to_topic_cluster_adds_similar_content_to_existing_cluste content_text="New similar cluster content", ) - result = assign_content_to_topic_cluster(candidate.id) + result = assign_content_to_topic_cluster(_require_pk(candidate)) cluster.refresh_from_db() membership = ContentClusterMembership.objects.get(content=candidate) assert result["assigned"] is True - assert result["cluster_id"] == cluster.id + assert result["cluster_id"] == _require_pk(cluster) assert membership.cluster == cluster assert cluster.member_count == 4 assert cluster.is_active is True @@ -933,6 +954,190 @@ def test_recompute_topic_velocity_detects_synthetic_burst( assert snapshot.velocity_score == pytest.approx(1.0) +def test_generate_theme_suggestions_creates_pending_suggestion( + source_plugin_context, settings, mocker +): + settings.OPENROUTER_API_KEY = "test-key" + project = source_plugin_context.project + cluster = TopicCluster.objects.create( + project=project, + first_seen_at=datetime(2026, 4, 20, 12, 0, tzinfo=timezone.utc), + last_seen_at=datetime(2026, 4, 24, 12, 0, tzinfo=timezone.utc), + is_active=True, + member_count=3, + dominant_entity=source_plugin_context.entity, + ) + content = Content.objects.create( + project=project, + entity=source_plugin_context.entity, + url="https://example.com/theme-source", + title="Theme Source", + author="Author", + source_plugin=SourcePluginName.RSS, + published_date="2026-04-24T12:00:00Z", + content_text="Theme source content", + ) + ContentClusterMembership.objects.create( + content=content, + cluster=cluster, + project=project, + similarity=0.95, + ) + TopicVelocitySnapshot.objects.create( + cluster=cluster, + project=project, + window_count=4, + trailing_mean=1.0, + trailing_stddev=0.0, + z_score=3.0, + velocity_score=1.0, + ) + llm_mock = mocker.patch( + "trends.tasks.openrouter_chat_json", + side_effect=[ + SimpleNamespace( + payload={ + "title": "Platform teams are consolidating around one workflow", + "one_sentence_pitch": "A burst of similar coverage suggests a coherent newsletter theme.", + "why_it_matters": "Editors can turn the cluster into a timely section.", + "suggested_angle": "Explain what changed this week.", + }, + model=settings.AI_SUMMARIZATION_MODEL, + latency_ms=123, + ), + SimpleNamespace( + payload={"novelty_score": 0.91, "explanation": "Novel enough."}, + model=settings.AI_RELEVANCE_MODEL, + latency_ms=98, + ), + ], + ) + + result = generate_theme_suggestions(project.id) + + suggestion = ThemeSuggestion.objects.get(project=project, cluster=cluster) + assert result["created"] == 1 + assert suggestion.status == ThemeSuggestionStatus.PENDING + assert suggestion.title == "Platform teams are consolidating around one workflow" + assert suggestion.novelty_score == pytest.approx(0.91) + assert suggestion.velocity_at_creation == pytest.approx(1.0) + assert llm_mock.call_count == 2 + + +def test_generate_theme_suggestions_updates_existing_pending_for_same_cluster( + source_plugin_context, +): + project = source_plugin_context.project + cluster = TopicCluster.objects.create( + project=project, + first_seen_at=datetime(2026, 4, 20, 12, 0, tzinfo=timezone.utc), + last_seen_at=datetime(2026, 4, 24, 12, 0, tzinfo=timezone.utc), + is_active=True, + member_count=3, + dominant_entity=source_plugin_context.entity, + ) + TopicVelocitySnapshot.objects.create( + cluster=cluster, + project=project, + window_count=4, + trailing_mean=1.0, + trailing_stddev=0.0, + z_score=3.0, + velocity_score=0.88, + ) + suggestion = ThemeSuggestion.objects.create( + project=project, + cluster=cluster, + title="Existing pending theme", + pitch="Pitch", + why_it_matters="Why", + suggested_angle="Angle", + velocity_at_creation=0.2, + novelty_score=0.8, + ) + + result = generate_theme_suggestions(project.id) + + suggestion.refresh_from_db() + assert result["created"] == 0 + assert result["updated"] == 1 + assert ThemeSuggestion.objects.filter(project=project, cluster=cluster).count() == 1 + assert suggestion.velocity_at_creation == pytest.approx(0.88) + + +def test_accept_theme_suggestion_marks_cluster_members_for_newsletter_promotion( + source_plugin_context, +): + project = source_plugin_context.project + cluster = TopicCluster.objects.create( + project=project, + first_seen_at=datetime(2026, 4, 20, 12, 0, tzinfo=timezone.utc), + last_seen_at=datetime(2026, 4, 24, 12, 0, tzinfo=timezone.utc), + is_active=True, + member_count=2, + dominant_entity=source_plugin_context.entity, + ) + primary_content = Content.objects.create( + project=project, + entity=source_plugin_context.entity, + url="https://example.com/promote-1", + title="Promoted One", + author="Author", + source_plugin=SourcePluginName.RSS, + published_date="2026-04-24T12:00:00Z", + content_text="Primary theme content", + ) + secondary_content = Content.objects.create( + project=project, + entity=source_plugin_context.entity, + url="https://example.com/promote-2", + title="Promoted Two", + author="Author", + source_plugin=SourcePluginName.REDDIT, + published_date="2026-04-24T13:00:00Z", + content_text="Secondary theme content", + ) + ContentClusterMembership.objects.bulk_create( + [ + ContentClusterMembership( + content=primary_content, + cluster=cluster, + project=project, + similarity=0.95, + ), + ContentClusterMembership( + content=secondary_content, + cluster=cluster, + project=project, + similarity=0.9, + ), + ] + ) + suggestion = ThemeSuggestion.objects.create( + project=project, + cluster=cluster, + title="Accepted Theme", + pitch="Pitch", + why_it_matters="Why", + suggested_angle="Angle", + velocity_at_creation=0.7, + novelty_score=0.8, + ) + + accept_theme_suggestion(suggestion, user_id=source_plugin_context.user.id) + + suggestion.refresh_from_db() + primary_content.refresh_from_db() + secondary_content.refresh_from_db() + assert suggestion.status == ThemeSuggestionStatus.ACCEPTED + assert primary_content.newsletter_promotion_theme == suggestion + assert secondary_content.newsletter_promotion_theme == suggestion + assert primary_content.newsletter_promotion_by == source_plugin_context.user + assert secondary_content.newsletter_promotion_by == source_plugin_context.user + assert primary_content.newsletter_promotion_at is not None + assert secondary_content.newsletter_promotion_at is not None + + def test_run_ingestion_marks_failure_when_plugin_errors(source_plugin_context, mocker): parse_mock = mocker.patch("core.plugins.rss.feedparser.parse") source_config = SourceConfig.objects.create( @@ -943,7 +1148,7 @@ def test_run_ingestion_marks_failure_when_plugin_errors(source_plugin_context, m parse_mock.side_effect = RuntimeError("feed unavailable") with pytest.raises(RuntimeError, match="feed unavailable"): - run_ingestion(source_config.id) + run_ingestion(_require_pk(source_config)) ingestion_run = IngestionRun.objects.get( project=source_plugin_context.project, plugin_name=SourcePluginName.RSS @@ -968,7 +1173,7 @@ def test_queue_content_skill_enqueues_relevance_task(source_plugin_context, mock skill_result = queue_content_skill(content, RELEVANCE_SKILL_NAME) assert skill_result.status == SkillStatus.PENDING - delay_mock.assert_called_once_with(skill_result.id) + delay_mock.assert_called_once_with(_require_pk(skill_result)) def test_queue_content_skill_executes_inline_when_eager( @@ -991,7 +1196,7 @@ def test_queue_content_skill_executes_inline_when_eager( skill_result = queue_content_skill(content, RELEVANCE_SKILL_NAME) assert skill_result.status == SkillStatus.PENDING - task_mock.assert_called_once_with(skill_result.id) + task_mock.assert_called_once_with(_require_pk(skill_result)) delay_mock.assert_not_called() @@ -1016,7 +1221,7 @@ def test_queue_content_skill_executes_summary_inline_when_eager( skill_result = queue_content_skill(content, SUMMARIZATION_SKILL_NAME) assert skill_result.status == SkillStatus.PENDING - task_mock.assert_called_once_with(skill_result.id) + task_mock.assert_called_once_with(_require_pk(skill_result)) delay_mock.assert_not_called() @@ -1155,9 +1360,9 @@ def test_run_relevance_scoring_skill_updates_pending_result( delay_mock = mocker.patch("core.tasks.run_relevance_scoring_skill.delay") pending_result = queue_content_skill(content, RELEVANCE_SKILL_NAME) - delay_mock.assert_called_once_with(pending_result.id) + delay_mock.assert_called_once_with(_require_pk(pending_result)) - result = run_relevance_scoring_skill(pending_result.id) + result = run_relevance_scoring_skill(_require_pk(pending_result)) content.refresh_from_db() pending_result.refresh_from_db() @@ -1184,9 +1389,9 @@ def test_run_summarization_skill_marks_result_failed_when_relevance_is_too_low( delay_mock = mocker.patch("core.tasks.run_summarization_skill.delay") pending_result = queue_content_skill(content, SUMMARIZATION_SKILL_NAME) - delay_mock.assert_called_once_with(pending_result.id) + delay_mock.assert_called_once_with(_require_pk(pending_result)) - result = run_summarization_skill(pending_result.id) + result = run_summarization_skill(_require_pk(pending_result)) pending_result.refresh_from_db() assert result.status == SkillStatus.FAILED @@ -1229,5 +1434,5 @@ def test_ingest_source_config_truncates_fields_and_processes_inline( assert len(created.title) == 512 assert len(created.author) == 255 upsert_mock.assert_called_once_with(created) - process_mock.assert_called_once_with(created.id) + process_mock.assert_called_once_with(_require_pk(created)) delay_mock.assert_not_called() diff --git a/frontend/src/app/admin/sources/page.tsx b/frontend/src/app/admin/sources/page.tsx index ab9c1748..f18e7549 100644 --- a/frontend/src/app/admin/sources/page.tsx +++ b/frontend/src/app/admin/sources/page.tsx @@ -281,9 +281,15 @@ export default async function SourcesPage({ searchParams }: SourcesPageProps) {
- Intake token +
- Address pattern +
(
  • {item.title || item.url} -
    {item.url}
    +
    {item.url}
    {item.excerpt ?
    {item.excerpt}
    : null}
  • ))} diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo index 98915b54..cc902b62 100644 --- a/frontend/tsconfig.tsbuildinfo +++ b/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.es2025.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.es2025.collection.d.ts","./node_modules/typescript/lib/lib.es2025.float16.d.ts","./node_modules/typescript/lib/lib.es2025.intl.d.ts","./node_modules/typescript/lib/lib.es2025.iterator.d.ts","./node_modules/typescript/lib/lib.es2025.promise.d.ts","./node_modules/typescript/lib/lib.es2025.regexp.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.date.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.esnext.temporal.d.ts","./node_modules/typescript/lib/lib.esnext.typedarrays.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/entry-constants.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/lib/bundler.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/next/dist/server/app-render/vary-params.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/shared/lib/app-router-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/client/components/readonly-url-search-params.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/segment-cache/types.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/next/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/next/dist/client/components/segment-cache/cache.d.ts","./node_modules/next/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/next/dist/client/components/segment-cache/navigation.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/next/dist/server/load-default-error-components.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/app-render/create-error-handler.d.ts","./node_modules/next/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/staged-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/get-supported-browsers.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/define-env.d.ts","./node_modules/next/dist/build/swc/index.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/debug-channel.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/next/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/dist/server/web/exports/index.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/shared/lib/size-limit.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/build/adapter/build-complete.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/dist/client/components/catch-error.d.ts","./node_modules/next/dist/api/error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./.next/dev/types/routes.d.ts","./next-env.d.ts","./next.config.ts","./node_modules/vite/types/hmrPayload.d.ts","./node_modules/vite/dist/node/chunks/moduleRunnerTransport.d.ts","./node_modules/vite/types/customEvent.d.ts","./node_modules/rolldown/dist/shared/logging-C6h4g8dA.d.mts","./node_modules/@oxc-project/types/types.d.ts","./node_modules/rolldown/dist/shared/binding-zH1vcmbM.d.mts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/composable-filters.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/filter-vite-plugins.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/simple-filters.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/index.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/index.d.ts","./node_modules/rolldown/dist/shared/define-config-5HJ1b9vG.d.mts","./node_modules/rolldown/dist/index.d.mts","./node_modules/rolldown/dist/parse-ast-index.d.mts","./node_modules/vite/types/internal/rollupTypeCompat.d.ts","./node_modules/rolldown/dist/shared/constructors-D0W3rNfA.d.mts","./node_modules/rolldown/dist/plugins-index.d.mts","./node_modules/rolldown/dist/shared/transform-DgZ3paSD.d.mts","./node_modules/rolldown/dist/utils-index.d.mts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/vite/types/internal/esbuildOptions.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/vite/types/internal/terserOptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/lightningcss/node/ast.d.ts","./node_modules/lightningcss/node/targets.d.ts","./node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssOptions.d.ts","./node_modules/vite/types/internal/cssPreprocessorOptions.d.ts","./node_modules/rolldown/dist/filter-index.d.mts","./node_modules/vite/types/importGlob.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/@vitejs/plugin-react/types/optionalTypes.d.ts","./node_modules/@vitejs/plugin-react/dist/index.d.ts","./node_modules/@vitest/spy/optional-types.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","./node_modules/tinyrainbow/dist/index.d.ts","./node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","./node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/utils/dist/display.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d.ts","./node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/@vitest/utils/dist/timers.d.ts","./node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-Bh0IjN67.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/vitest/dist/chunks/traces.d.D2T_R8rx.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","./node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/vitest/dist/chunks/config.d.A1h_Y6Jt.d.ts","./node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","./node_modules/vitest/dist/chunks/rpc.d.B_8sPU0w.d.ts","./node_modules/vitest/dist/chunks/worker.d.ZpHpO4yb.d.ts","./node_modules/vitest/dist/chunks/browser.d.BcoexmFG.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","./node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","./node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","./node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/vitest/dist/chunks/coverage.d.BZtK59WP.d.ts","./node_modules/@vitest/utils/dist/serialize.d.ts","./node_modules/@vitest/utils/dist/error.d.ts","./node_modules/vitest/dist/browser.d.ts","./node_modules/vitest/browser/context.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.CEnv6XRv.d.ts","./node_modules/vitest/dist/chunks/plugin.d.BM2TCi12.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/jest-dom/types/matchers.d.ts","./node_modules/@testing-library/jest-dom/types/jest.d.ts","./node_modules/@testing-library/jest-dom/types/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@types/react-dom/test-utils/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./node_modules/vitest/dist/chunks/global.d.DVsSRdQ5.d.ts","./node_modules/vitest/optional-runtime-types.d.ts","./node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","./node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","./node_modules/vitest/dist/runners.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./vitest.setup.ts","./node_modules/next-auth/adapters.d.ts","./node_modules/jose/dist/types/types.d.ts","./node_modules/jose/dist/types/jwe/compact/decrypt.d.ts","./node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts","./node_modules/jose/dist/types/jwe/general/decrypt.d.ts","./node_modules/jose/dist/types/jwe/general/encrypt.d.ts","./node_modules/jose/dist/types/jws/compact/verify.d.ts","./node_modules/jose/dist/types/jws/flattened/verify.d.ts","./node_modules/jose/dist/types/jws/general/verify.d.ts","./node_modules/jose/dist/types/jwt/verify.d.ts","./node_modules/jose/dist/types/jwt/decrypt.d.ts","./node_modules/jose/dist/types/jwt/produce.d.ts","./node_modules/jose/dist/types/jwe/compact/encrypt.d.ts","./node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts","./node_modules/jose/dist/types/jws/compact/sign.d.ts","./node_modules/jose/dist/types/jws/flattened/sign.d.ts","./node_modules/jose/dist/types/jws/general/sign.d.ts","./node_modules/jose/dist/types/jwt/sign.d.ts","./node_modules/jose/dist/types/jwt/encrypt.d.ts","./node_modules/jose/dist/types/jwk/thumbprint.d.ts","./node_modules/jose/dist/types/jwk/embedded.d.ts","./node_modules/jose/dist/types/jwks/local.d.ts","./node_modules/jose/dist/types/jwks/remote.d.ts","./node_modules/jose/dist/types/jwt/unsecured.d.ts","./node_modules/jose/dist/types/key/export.d.ts","./node_modules/jose/dist/types/key/import.d.ts","./node_modules/jose/dist/types/util/decode_protected_header.d.ts","./node_modules/jose/dist/types/util/decode_jwt.d.ts","./node_modules/jose/dist/types/util/errors.d.ts","./node_modules/jose/dist/types/key/generate_key_pair.d.ts","./node_modules/jose/dist/types/key/generate_secret.d.ts","./node_modules/jose/dist/types/util/base64url.d.ts","./node_modules/jose/dist/types/util/runtime.d.ts","./node_modules/jose/dist/types/index.d.ts","./node_modules/openid-client/types/index.d.ts","./node_modules/next-auth/providers/oauth-types.d.ts","./node_modules/next-auth/providers/oauth.d.ts","./node_modules/next-auth/providers/email.d.ts","./node_modules/next-auth/core/lib/cookie.d.ts","./node_modules/next-auth/core/index.d.ts","./node_modules/next-auth/providers/credentials.d.ts","./node_modules/next-auth/providers/index.d.ts","./node_modules/next-auth/jwt/types.d.ts","./node_modules/next-auth/jwt/index.d.ts","./node_modules/next-auth/utils/logger.d.ts","./node_modules/next-auth/core/types.d.ts","./node_modules/next-auth/next/index.d.ts","./node_modules/next-auth/index.d.ts","./node_modules/next-auth/providers/github.d.ts","./node_modules/next-auth/providers/google.d.ts","./src/lib/auth.ts","./src/app/api/auth/[...nextauth]/route.ts","./src/lib/types.ts","./src/lib/api.ts","./src/app/api/content-skills/route.ts","./src/app/api/content-skills/__tests__/route.test.ts","./src/app/api/entities/route.ts","./src/app/api/entities/[id]/route.ts","./src/app/api/entities/[id]/__tests__/route.test.ts","./src/app/api/entities/__tests__/route.test.ts","./src/app/api/entity-candidates/[id]/route.ts","./src/app/api/entity-candidates/[id]/__tests__/route.test.ts","./src/app/api/feedback/route.ts","./src/app/api/feedback/__tests__/route.test.ts","./src/app/api/invitations/[token]/accept/route.ts","./src/app/api/invitations/[token]/accept/__tests__/route.test.ts","./src/app/api/profile/route.ts","./src/app/api/profile/__tests__/route.test.ts","./src/app/api/profile/avatar/route.ts","./src/app/api/profile/avatar/__tests__/route.test.ts","./src/app/api/projects/route.ts","./src/app/api/projects/[id]/bluesky-credentials/route.ts","./src/app/api/projects/[id]/bluesky-credentials/__tests__/route.test.ts","./src/app/api/projects/[id]/intake/route.ts","./src/app/api/projects/[id]/intake/__tests__/route.test.ts","./src/app/api/projects/[id]/intake-allowlist/route.ts","./src/app/api/projects/[id]/intake-allowlist/[allowlistId]/route.ts","./src/app/api/projects/[id]/intake-allowlist/[allowlistId]/__tests__/route.test.ts","./src/app/api/projects/[id]/intake-allowlist/__tests__/route.test.ts","./src/app/api/projects/[id]/invitations/route.ts","./src/app/api/projects/[id]/invitations/[invitationId]/revoke/route.ts","./src/app/api/projects/[id]/invitations/__tests__/route.test.ts","./src/app/api/projects/[id]/mastodon-credentials/route.ts","./src/app/api/projects/[id]/mastodon-credentials/__tests__/route.test.ts","./src/app/api/projects/[id]/members/[membershipId]/route.ts","./src/app/api/projects/[id]/members/[membershipId]/__tests__/route.test.ts","./src/app/api/projects/[id]/rotate-intake-token/route.ts","./src/app/api/projects/[id]/rotate-intake-token/__tests__/route.test.ts","./src/app/api/projects/[id]/verify-bluesky-credentials/route.ts","./src/app/api/projects/[id]/verify-bluesky-credentials/__tests__/route.test.ts","./src/app/api/projects/[id]/verify-mastodon-credentials/route.ts","./src/app/api/projects/[id]/verify-mastodon-credentials/__tests__/route.test.ts","./src/app/api/projects/__tests__/route.test.ts","./src/app/api/review/[id]/route.ts","./src/app/api/review/[id]/__tests__/route.test.ts","./src/app/api/skills/[skillName]/route.ts","./src/app/api/skills/[skillName]/__tests__/route.test.ts","./src/app/api/source-configs/route.ts","./src/app/api/source-configs/[id]/route.ts","./src/app/api/source-configs/[id]/__tests__/route.test.ts","./src/app/api/source-configs/__tests__/route.test.ts","./src/lib/view-helpers.ts","./src/lib/dashboard-view.ts","./src/lib/profile.ts","./node_modules/@tanstack/query-core/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/query-core/build/modern/index.d.ts","./node_modules/@tanstack/react-query/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/react-query/build/modern/index.d.ts","./src/lib/useRole.ts","./src/lib/__tests__/api.test.ts","./src/lib/__tests__/auth.test.ts","./src/lib/__tests__/dashboard-view.test.ts","./src/lib/__tests__/view-helpers.test.ts","./test-support/server-only.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./src/components/query-provider.tsx","./src/app/layout.tsx","./node_modules/next-auth/client/_utils.d.ts","./node_modules/next-auth/react/types.d.ts","./node_modules/next-auth/react/index.d.ts","./src/components/user-menu.tsx","./src/components/app-shell.tsx","./src/components/status-badge.tsx","./src/app/page.tsx","./src/app/__tests__/page.test.tsx","./src/app/admin/health/page.tsx","./src/app/admin/health/__tests__/page.test.tsx","./src/app/admin/projects/new/page.tsx","./src/components/copy-button.tsx","./src/app/admin/sources/page.tsx","./src/app/admin/sources/__tests__/page.test.tsx","./src/components/skill-action-bar.tsx","./src/app/content/[id]/page.tsx","./src/app/content/[id]/__tests__/page.test.tsx","./src/app/entities/page.tsx","./src/app/entities/[id]/page.tsx","./src/app/entities/[id]/__tests__/page.test.tsx","./src/app/entities/__tests__/page.test.tsx","./src/app/invite/[token]/page.tsx","./src/components/auth/social-auth-buttons.tsx","./src/components/auth/login-form.tsx","./src/app/login/page.tsx","./src/app/login/__tests__/page.test.tsx","./node_modules/file-selector/dist/file.d.ts","./node_modules/file-selector/dist/file-selector.d.ts","./node_modules/file-selector/dist/index.d.ts","./node_modules/react-dropzone/typings/react-dropzone.d.ts","./src/components/profile/avatar-dropzone.tsx","./src/components/profile/avatar-preview.tsx","./src/components/profile/profile-form.tsx","./src/components/profile/profile-settings-panel.tsx","./src/app/profile/page.tsx","./src/app/projects/[id]/members/page.tsx","./src/app/projects/[id]/members/invite/page.tsx","./src/components/__tests__/app-shell.test.tsx","./src/components/__tests__/query-provider.test.tsx","./src/components/__tests__/skill-action-bar.test.tsx","./src/components/__tests__/status-badge.test.tsx","./src/components/__tests__/user-menu.test.tsx","./src/components/auth/__tests__/login-form.test.tsx","./src/components/auth/__tests__/social-auth-buttons.test.tsx","./src/components/profile/__tests__/avatar-dropzone.test.tsx","./src/lib/__tests__/useRole.test.tsx","./.next/types/cache-life.d.ts","./.next/types/routes.d.ts","./.next/types/validator.ts","./.next/dev/types/cache-life.d.ts","./.next/dev/types/validator.ts","./node_modules/vitest/globals.d.ts"],"fileIdsList":[[101,164,172,176,179,181,182,183,195,512,513,514,515,861],[101,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,255,553,556,559,747,750,752,753,756,758,760,762,764,766,767,769,771,772,775,776,778,780,782,784,786,789,791,793,794,814,821,823,825,827,830,832,833,836,839,849,850,851,861,864],[101,164,172,176,179,181,182,183,195,512,513,514,515,864],[101,164,172,176,179,181,182,183,195,255,553,556,747,750,752,753,756,758,767,769,771,772,782,784,789,791,793,794,814,821,823,827,830,832,833,839,861,862,864],[101,164,172,176,179,181,182,183,195,557,558,559,861,864],[101,164,172,176,179,181,182,183,195,255,557,861,864],[101,164,172,176,179,181,182,183,195,800,861,864],[92,101,164,172,176,179,181,182,183,195,255,801,861,864],[101,164,172,176,179,181,182,183,195,802,861,864],[101,164,172,176,179,181,182,183,195,669,861,864],[101,164,172,176,179,181,182,183,195,666,667,668,669,670,673,674,675,676,677,678,679,680,861,864],[101,164,172,176,179,181,182,183,195,662,861,864],[101,164,172,176,179,181,182,183,195,672,861,864],[101,164,172,176,179,181,182,183,195,666,667,668,861,864],[101,164,172,176,179,181,182,183,195,666,667,861,864],[101,164,172,176,179,181,182,183,195,669,670,672,861,864],[101,164,172,176,179,181,182,183,195,667,861,864],[101,164,172,176,179,181,182,183,195,664,861,864],[101,164,172,176,179,181,182,183,195,663,861,864],[92,101,164,172,176,179,181,182,183,195,225,488,681,682,861,864],[101,164,172,176,179,181,182,183,195,624,625,861,864],[101,161,162,164,172,176,179,181,182,183,195,861,864],[101,163,164,172,176,179,181,182,183,195,861,864],[164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,203,861,864],[101,164,165,170,172,175,176,179,181,182,183,185,195,200,212,861,864],[101,164,165,166,172,175,176,179,181,182,183,195,861,864],[101,164,167,172,176,179,181,182,183,195,213,861,864],[101,164,168,169,172,176,179,181,182,183,186,195,861,864],[101,164,169,172,176,179,181,182,183,195,200,209,861,864],[101,164,170,172,175,176,179,181,182,183,185,195,861,864],[101,163,164,171,172,176,179,181,182,183,195,861,864],[101,164,172,173,176,179,181,182,183,195,861,864],[101,164,172,174,175,176,179,181,182,183,195,861,864],[101,163,164,172,175,176,179,181,182,183,195,861,864],[101,164,172,175,176,177,179,181,182,183,195,200,212,861,864],[101,164,172,175,176,177,179,181,182,183,195,200,203,861,864],[101,151,164,172,175,176,178,179,181,182,183,185,195,200,212,861,864],[101,164,172,175,176,178,179,181,182,183,185,195,200,209,212,861,864],[101,164,172,176,178,179,180,181,182,183,195,200,209,212,861,864],[99,100,101,102,103,104,105,106,107,108,109,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,861,864],[101,164,172,175,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,183,195,861,864],[101,164,172,176,179,181,182,183,184,195,212,861,864],[101,164,172,175,176,179,181,182,183,185,195,200,861,864],[101,164,172,176,179,181,182,183,186,195,861,864],[101,164,172,176,179,181,182,183,187,195,861,864],[101,164,172,175,176,179,181,182,183,190,195,861,864],[101,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,861,864],[101,164,172,176,179,181,182,183,192,195,861,864],[101,164,172,176,179,181,182,183,193,195,861,864],[101,164,169,172,176,179,181,182,183,185,195,203,861,864],[101,164,172,175,176,179,181,182,183,195,196,861,864],[101,164,172,176,179,181,182,183,195,197,213,216,861,864],[101,164,172,175,176,179,181,182,183,195,200,202,203,861,864],[101,164,172,176,179,181,182,183,195,201,203,861,864],[101,164,172,176,179,181,182,183,195,203,213,861,864],[101,164,172,176,179,181,182,183,195,204,861,864],[101,161,164,172,176,179,181,182,183,195,200,206,212,861,864],[101,164,172,176,179,181,182,183,195,200,205,861,864],[101,164,172,175,176,179,181,182,183,195,207,208,861,864],[101,164,172,176,179,181,182,183,195,207,208,861,864],[101,164,169,172,176,179,181,182,183,185,195,200,209,861,864],[101,164,172,176,179,181,182,183,195,210,861,864],[101,164,172,176,179,181,182,183,185,195,211,861,864],[101,164,172,176,178,179,181,182,183,193,195,212,861,864],[101,164,172,176,179,181,182,183,195,213,214,861,864],[101,164,169,172,176,179,181,182,183,195,214,861,864],[101,164,172,176,179,181,182,183,195,200,215,861,864],[101,164,172,176,179,181,182,183,184,195,216,861,864],[101,164,172,176,179,181,182,183,195,217,861,864],[101,164,167,172,176,179,181,182,183,195,861,864],[101,164,169,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,213,861,864],[101,151,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,212,861,864],[101,164,172,176,179,181,182,183,195,218,861,864],[101,164,172,176,179,181,182,183,190,195,861,864],[101,164,172,176,179,181,182,183,195,208,861,864],[101,151,164,172,175,176,177,179,181,182,183,190,195,200,203,212,215,216,218,861,864],[101,164,172,176,179,181,182,183,195,200,219,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,222,223,225,507,552,861,864],[92,101,164,172,176,179,181,182,183,195,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,222,223,224,488,507,552,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,222,224,225,507,552,861,864],[92,101,164,172,176,179,181,182,183,195,225,488,489,861,864],[92,101,164,172,176,179,181,182,183,195,225,488,861,864],[92,96,101,164,172,176,179,181,182,183,195,222,223,224,225,507,552,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,223,224,225,507,552,861,864],[90,91,101,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,613,614,659,861,864],[101,164,172,176,179,181,182,183,195,617,618,619,622,623,626,861,864],[101,164,172,176,179,181,182,183,195,647,861,864],[101,164,172,176,179,181,182,183,195,647,648,861,864],[101,164,172,176,179,181,182,183,195,622,631,632,861,864],[101,164,172,176,179,181,182,183,195,622,631,861,864],[101,164,172,176,179,181,182,183,195,631,861,864],[101,164,172,176,179,181,182,183,195,620,631,635,636,861,864],[101,164,172,176,179,181,182,183,195,620,631,635,861,864],[101,164,172,176,179,181,182,183,195,616,861,864],[101,164,172,176,179,181,182,183,195,620,621,861,864],[101,164,172,176,179,181,182,183,195,620,861,864],[101,164,172,176,179,181,182,183,195,620,621,628,652,861,864],[101,164,172,176,179,181,182,183,195,628,861,864],[101,164,172,176,179,181,182,183,195,620,623,628,629,630,861,864],[101,164,172,176,179,181,182,183,195,689,690,861,864],[101,164,172,176,179,181,182,183,195,689,690,691,692,861,864],[101,164,172,176,179,181,182,183,195,689,691,861,864],[101,164,172,176,179,181,182,183,195,689,861,864],[101,164,172,176,179,181,182,183,195,841,861,864],[101,164,172,176,179,181,182,183,195,841,842,861,864],[101,164,172,176,179,181,182,183,195,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,861,864],[101,164,172,176,179,181,182,183,195,697,861,864],[101,164,172,176,179,181,182,183,195,697,707,861,864],[101,164,172,176,179,181,182,183,195,606,607,861,864],[101,164,172,176,179,181,182,183,195,743,861,864],[101,164,172,176,178,179,181,182,183,195,220,743,861,864],[101,164,172,176,179,181,182,183,195,734,741,861,864],[101,164,172,176,179,181,182,183,195,553,557,741,743,861,864],[101,164,172,176,179,181,182,183,195,696,730,737,739,740,861,864],[101,164,172,176,179,181,182,183,195,735,741,742,861,864],[101,164,172,176,179,181,182,183,195,553,557,738,743,861,864],[101,164,172,176,179,181,182,183,195,220,743,861,864],[101,164,172,176,179,181,182,183,195,735,737,743,861,864],[101,164,172,176,179,181,182,183,195,737,741,743,861,864],[101,164,172,176,179,181,182,183,195,737,861,864],[101,164,172,176,179,181,182,183,195,732,733,736,861,864],[101,164,172,176,179,181,182,183,195,729,730,731,737,743,861,864],[92,101,164,172,176,179,181,182,183,195,737,743,815,816,861,864],[92,101,164,172,176,179,181,182,183,195,737,743,861,864],[101,164,172,176,179,181,182,183,195,510,861,864],[101,164,172,176,179,181,182,183,195,512,513,514,515,861,864],[101,164,172,176,179,181,182,183,195,458,521,522,861,864],[101,164,172,176,179,181,182,183,195,230,231,233,245,269,384,395,503,861,864],[101,164,172,176,179,181,182,183,195,233,264,265,266,268,503,861,864],[101,164,172,176,179,181,182,183,195,233,401,403,405,406,408,503,505,861,864],[101,164,172,176,179,181,182,183,195,233,267,304,503,861,864],[101,164,172,176,179,181,182,183,195,231,233,244,245,251,257,262,383,384,385,394,503,505,861,864],[101,164,172,176,179,181,182,183,195,503,861,864],[101,164,172,176,179,181,182,183,195,240,246,265,285,380,861,864],[101,164,172,176,179,181,182,183,195,233,861,864],[101,164,172,176,179,181,182,183,195,226,240,246,861,864],[101,164,172,176,179,181,182,183,195,412,861,864],[101,164,172,176,179,181,182,183,195,409,410,412,861,864],[101,164,172,176,179,181,182,183,195,409,411,503,861,864],[101,164,172,176,178,179,181,182,183,195,285,482,500,861,864],[101,164,172,176,178,179,181,182,183,195,356,359,375,380,500,861,864],[101,164,172,176,178,179,181,182,183,195,328,500,861,864],[101,164,172,176,179,181,182,183,195,388,861,864],[101,164,172,176,179,181,182,183,195,387,388,389,861,864],[101,164,172,176,179,181,182,183,195,387,861,864],[98,101,164,172,176,178,179,181,182,183,195,226,233,245,251,257,263,265,269,270,283,284,351,381,382,395,503,507,861,864],[101,164,172,176,179,181,182,183,195,230,233,267,304,401,402,407,503,555,861,864],[101,164,172,176,179,181,182,183,195,267,555,861,864],[101,164,172,176,179,181,182,183,195,230,284,453,503,555,861,864],[101,164,172,176,179,181,182,183,195,555,861,864],[101,164,172,176,179,181,182,183,195,233,267,268,555,861,864],[101,164,172,176,179,181,182,183,195,404,555,861,864],[101,164,172,176,179,181,182,183,195,270,383,386,393,861,864],[92,101,164,172,176,179,181,182,183,195,458,861,864],[101,164,172,176,179,181,182,183,193,195,240,255,861,864],[101,164,172,176,179,181,182,183,195,240,255,861,864],[92,101,164,172,176,179,181,182,183,195,325,861,864],[92,101,164,172,176,179,181,182,183,195,255,861,864],[92,101,164,172,176,179,181,182,183,195,246,255,458,861,864],[101,164,172,176,179,181,182,183,195,240,311,325,326,537,544,861,864],[101,164,172,176,179,181,182,183,195,310,538,539,540,541,543,861,864],[101,164,172,176,179,181,182,183,195,361,861,864],[101,164,172,176,179,181,182,183,195,361,362,861,864],[101,164,172,176,179,181,182,183,195,244,246,313,314,861,864],[101,164,172,176,179,181,182,183,195,246,320,321,861,864],[101,164,172,176,179,181,182,183,195,246,315,323,861,864],[101,164,172,176,179,181,182,183,195,320,861,864],[101,164,172,176,179,181,182,183,195,238,246,313,314,315,316,317,318,319,320,323,861,864],[101,164,172,176,179,181,182,183,195,246,313,320,321,322,324,861,864],[101,164,172,176,179,181,182,183,195,246,314,316,317,861,864],[101,164,172,176,179,181,182,183,195,314,316,319,321,861,864],[101,164,172,176,179,181,182,183,195,542,861,864],[101,164,172,176,179,181,182,183,195,246,861,864],[92,101,164,172,176,179,181,182,183,195,234,531,861,864],[92,101,164,172,176,179,181,182,183,195,212,861,864],[92,101,164,172,176,179,181,182,183,195,267,302,861,864],[92,101,164,172,176,179,181,182,183,195,267,395,861,864],[101,164,172,176,179,181,182,183,195,300,305,861,864],[92,101,164,172,176,179,181,182,183,195,301,509,861,864],[101,164,172,176,179,181,182,183,195,810,861,864],[92,96,101,164,172,176,178,179,181,182,183,195,221,222,223,224,225,507,551,861,864],[101,164,172,176,178,179,181,182,183,195,246,861,864],[101,164,172,176,178,179,181,182,183,195,245,250,331,348,390,391,395,450,452,503,504,861,864],[101,164,172,176,179,181,182,183,195,283,392,861,864],[101,164,172,176,179,181,182,183,195,507,861,864],[101,164,172,176,179,181,182,183,195,232,861,864],[92,101,164,172,176,179,181,182,183,195,237,240,455,471,473,861,864],[101,164,172,176,179,181,182,183,193,195,240,455,470,471,472,554,861,864],[101,164,172,176,179,181,182,183,195,464,465,466,467,468,469,861,864],[101,164,172,176,179,181,182,183,195,466,861,864],[101,164,172,176,179,181,182,183,195,470,861,864],[101,164,172,176,179,181,182,183,195,255,419,420,422,861,864],[92,101,164,172,176,179,181,182,183,195,246,413,414,415,416,421,861,864],[101,164,172,176,179,181,182,183,195,419,421,861,864],[101,164,172,176,179,181,182,183,195,417,861,864],[101,164,172,176,179,181,182,183,195,418,861,864],[92,101,164,172,176,179,181,182,183,195,255,301,509,861,864],[92,101,164,172,176,179,181,182,183,195,255,508,509,861,864],[92,101,164,172,176,179,181,182,183,195,255,509,861,864],[101,164,172,176,179,181,182,183,195,348,349,861,864],[101,164,172,176,179,181,182,183,195,349,861,864],[101,164,172,176,178,179,181,182,183,195,504,509,861,864],[101,164,172,176,179,181,182,183,195,378,861,864],[101,163,164,172,176,179,181,182,183,195,377,861,864],[101,164,172,176,179,181,182,183,195,240,246,252,254,356,369,373,375,452,455,492,493,500,504,861,864],[101,164,172,176,179,181,182,183,195,246,295,317,861,864],[101,164,172,176,179,181,182,183,195,356,367,370,375,861,864],[92,101,164,172,176,179,181,182,183,195,237,240,356,359,375,378,412,459,460,461,462,463,474,475,476,477,478,479,480,481,555,861,864],[101,164,172,176,179,181,182,183,195,237,240,265,356,363,364,365,368,369,861,864],[101,164,172,176,179,181,182,183,195,200,246,265,367,374,455,456,500,861,864],[101,164,172,176,179,181,182,183,195,371,861,864],[101,164,172,176,178,179,181,182,183,193,195,234,246,250,260,292,293,296,348,351,416,450,451,492,503,504,505,507,555,861,864],[101,164,172,176,179,181,182,183,195,237,238,240,861,864],[101,164,172,176,179,181,182,183,195,356,861,864],[101,163,164,172,176,179,181,182,183,195,265,292,293,350,351,352,353,354,355,504,861,864],[101,164,172,176,179,181,182,183,195,375,861,864],[101,163,164,172,176,179,181,182,183,195,239,240,250,254,290,356,363,364,365,366,367,370,371,372,373,374,493,861,864],[101,164,172,176,178,179,181,182,183,195,290,291,363,504,505,861,864],[101,164,172,176,179,181,182,183,195,265,293,348,351,356,452,504,861,864],[101,164,172,176,178,179,181,182,183,195,503,505,861,864],[101,164,172,176,178,179,181,182,183,195,200,500,504,505,861,864],[101,164,172,176,178,179,181,182,183,193,195,226,240,245,252,254,257,260,267,287,292,293,294,295,296,331,332,334,337,339,342,343,344,345,347,395,450,452,500,503,504,505,861,864],[101,164,172,176,178,179,181,182,183,195,200,861,864],[101,164,172,176,179,181,182,183,195,233,234,235,263,500,501,502,507,509,555,861,864],[101,164,172,176,179,181,182,183,195,230,231,503,861,864],[101,164,172,176,179,181,182,183,195,424,861,864],[101,164,172,176,178,179,181,182,183,195,200,212,242,408,412,413,414,415,416,422,423,555,861,864],[101,164,172,176,179,181,182,183,193,195,212,226,240,242,254,257,293,332,337,347,348,401,428,429,430,436,439,440,450,452,500,503,861,864],[101,164,172,176,179,181,182,183,195,257,263,270,283,293,351,503,861,864],[101,164,172,176,178,179,181,182,183,195,212,234,245,254,293,434,500,503,861,864],[101,164,172,176,179,181,182,183,195,454,861,864],[101,164,172,176,178,179,181,182,183,195,424,437,438,447,861,864],[101,164,172,176,179,181,182,183,195,500,503,861,864],[101,164,172,176,179,181,182,183,195,353,493,861,864],[101,164,172,176,179,181,182,183,195,254,292,395,509,861,864],[101,164,172,176,178,179,181,182,183,193,195,232,337,397,401,430,436,439,442,500,861,864],[101,164,172,176,178,179,181,182,183,195,270,283,401,443,861,864],[101,164,172,176,179,181,182,183,195,233,294,395,445,503,505,861,864],[101,164,172,176,178,179,181,182,183,195,212,416,503,861,864],[101,164,172,176,178,179,181,182,183,195,267,294,395,396,397,406,424,444,446,503,861,864],[98,101,164,172,176,178,179,181,182,183,195,292,449,507,509,861,864],[101,164,172,176,179,181,182,183,195,346,450,861,864],[101,164,172,176,178,179,181,182,183,193,195,240,243,245,246,252,254,260,269,270,283,293,296,332,334,344,347,348,395,428,429,430,431,433,435,450,452,500,509,861,864],[101,164,172,176,178,179,181,182,183,195,200,270,436,441,447,500,861,864],[101,164,172,176,179,181,182,183,195,273,274,275,276,277,278,279,280,281,282,861,864],[101,164,172,176,179,181,182,183,195,287,338,861,864],[101,164,172,176,179,181,182,183,195,340,861,864],[101,164,172,176,179,181,182,183,195,338,861,864],[101,164,172,176,179,181,182,183,195,340,341,861,864],[101,164,172,176,178,179,181,182,183,195,244,245,246,250,251,504,861,864],[101,164,172,176,178,179,181,182,183,193,195,232,234,252,256,292,295,296,330,450,500,505,507,509,861,864],[101,164,172,176,178,179,181,182,183,193,195,212,236,243,244,254,256,293,448,493,499,504,861,864],[101,164,172,176,179,181,182,183,195,363,861,864],[101,164,172,176,179,181,182,183,195,364,861,864],[101,164,172,176,179,181,182,183,195,246,257,492,861,864],[101,164,172,176,179,181,182,183,195,365,861,864],[101,164,172,176,179,181,182,183,195,239,861,864],[101,164,172,176,179,181,182,183,195,241,253,861,864],[101,164,172,176,178,179,181,182,183,195,241,245,252,861,864],[101,164,172,176,179,181,182,183,195,248,253,861,864],[101,164,172,176,179,181,182,183,195,249,861,864],[101,164,172,176,179,181,182,183,195,241,242,861,864],[101,164,172,176,179,181,182,183,195,241,297,861,864],[101,164,172,176,179,181,182,183,195,241,861,864],[101,164,172,176,179,181,182,183,195,243,287,336,861,864],[101,164,172,176,179,181,182,183,195,335,861,864],[101,164,172,176,179,181,182,183,195,240,242,243,861,864],[101,164,172,176,179,181,182,183,195,243,333,861,864],[101,164,172,176,179,181,182,183,195,240,242,861,864],[101,164,172,176,179,181,182,183,195,292,395,861,864],[101,164,172,176,179,181,182,183,195,492,861,864],[101,164,172,176,178,179,181,182,183,195,212,252,254,258,292,395,449,452,455,456,457,483,484,487,491,493,500,504,861,864],[101,164,172,176,179,181,182,183,195,306,309,311,312,325,326,861,864],[92,101,164,172,176,179,181,182,183,195,223,225,255,485,486,861,864],[92,101,164,172,176,179,181,182,183,195,223,225,255,485,486,490,861,864],[101,164,172,176,179,181,182,183,195,379,861,864],[101,164,172,176,179,181,182,183,195,265,286,291,292,356,357,358,359,360,362,375,376,378,381,449,452,503,505,861,864],[101,164,172,176,179,181,182,183,195,325,861,864],[101,164,172,176,178,179,181,182,183,195,330,500,861,864],[101,164,172,176,179,181,182,183,195,330,861,864],[101,164,172,176,178,179,181,182,183,195,252,298,327,329,331,449,500,507,509,861,864],[101,164,172,176,179,181,182,183,195,306,307,308,309,311,312,325,326,508,861,864],[98,101,164,172,176,178,179,181,182,183,193,195,212,241,242,254,260,292,293,296,395,447,448,450,500,503,504,507,861,864],[101,164,172,176,179,181,182,183,195,237,240,247,861,864],[101,164,172,176,179,181,182,183,195,291,293,425,428,861,864],[101,164,172,176,179,181,182,183,195,291,426,494,495,496,497,498,861,864],[101,164,172,176,178,179,181,182,183,195,287,503,861,864],[101,164,172,176,178,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,290,375,861,864],[101,164,172,176,179,181,182,183,195,289,861,864],[101,164,172,176,179,181,182,183,195,291,344,861,864],[101,164,172,176,179,181,182,183,195,288,290,503,861,864],[101,164,172,176,178,179,181,182,183,195,236,291,425,426,427,500,503,504,861,864],[92,101,164,172,176,179,181,182,183,195,240,246,324,861,864],[92,101,164,172,176,179,181,182,183,195,238,861,864],[101,164,172,176,179,181,182,183,195,228,229,861,864],[92,101,164,172,176,179,181,182,183,195,234,861,864],[92,101,164,172,176,179,181,182,183,195,240,310,861,864],[92,98,101,164,172,176,179,181,182,183,195,292,296,507,509,861,864],[101,164,172,176,179,181,182,183,195,234,531,532,861,864],[92,101,164,172,176,179,181,182,183,195,305,861,864],[92,101,164,172,176,179,181,182,183,193,195,212,232,299,301,303,304,509,861,864],[101,164,172,176,179,181,182,183,195,240,267,504,861,864],[101,164,172,176,179,181,182,183,195,240,432,861,864],[92,101,164,172,176,178,179,181,182,183,193,195,230,232,305,403,507,508,861,864],[92,101,164,172,176,179,181,182,183,195,221,222,223,224,225,507,552,861,864],[92,93,94,95,96,101,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,398,399,400,861,864],[101,164,172,176,179,181,182,183,195,398,861,864],[92,96,101,164,172,176,178,179,180,181,182,183,193,195,220,221,222,223,224,225,226,232,260,265,442,470,505,506,509,552,861,864],[101,164,172,176,179,181,182,183,195,517,861,864],[101,164,172,176,179,181,182,183,195,519,861,864],[101,164,172,176,179,181,182,183,195,523,861,864],[101,164,172,176,179,181,182,183,195,811,861,864],[101,164,172,176,179,181,182,183,195,525,861,864],[101,164,172,176,179,181,182,183,195,527,528,529,861,864],[101,164,172,176,179,181,182,183,195,533,861,864],[97,101,164,172,176,179,181,182,183,195,511,516,518,520,524,526,530,534,536,546,547,549,553,554,555,556,861,864],[101,164,172,176,179,181,182,183,195,535,861,864],[101,164,172,176,179,181,182,183,195,545,861,864],[101,164,172,176,179,181,182,183,195,301,861,864],[101,164,172,176,179,181,182,183,195,548,861,864],[101,163,164,172,176,179,181,182,183,195,291,425,426,428,494,495,497,498,550,552,861,864],[101,164,172,176,179,181,182,183,195,220,861,864],[101,164,169,172,176,178,179,180,181,182,183,195,212,213,220,729,861,864],[101,164,172,176,179,181,182,183,195,601,861,864],[101,164,172,176,179,181,182,183,195,599,601,861,864],[101,164,172,176,179,181,182,183,195,590,598,599,600,602,604,861,864],[101,164,172,176,179,181,182,183,195,588,861,864],[101,164,172,176,179,181,182,183,195,591,596,601,604,861,864],[101,164,172,176,179,181,182,183,195,587,604,861,864],[101,164,172,176,179,181,182,183,195,591,592,595,596,597,604,861,864],[101,164,172,176,179,181,182,183,195,591,592,593,595,596,604,861,864],[101,164,172,176,179,181,182,183,195,588,589,590,591,592,596,597,598,600,601,602,604,861,864],[101,164,172,176,179,181,182,183,195,604,861,864],[101,164,172,176,179,181,182,183,195,586,588,589,590,591,592,593,595,596,597,598,599,600,601,602,603,861,864],[101,164,172,176,179,181,182,183,195,586,604,861,864],[101,164,172,176,179,181,182,183,195,591,593,594,596,597,604,861,864],[101,164,172,176,179,181,182,183,195,595,604,861,864],[101,164,172,176,179,181,182,183,195,596,597,601,604,861,864],[101,164,172,176,179,181,182,183,195,589,599,861,864],[101,164,172,176,179,181,182,183,195,671,861,864],[92,101,164,172,176,179,181,182,183,195,843,861,864],[101,164,172,176,179,181,182,183,195,573,861,864],[101,164,172,176,179,181,182,183,195,565,567,573,861,864],[101,164,172,176,179,181,182,183,195,566,567,861,864],[101,164,172,176,179,181,182,183,195,567,573,577,861,864],[101,164,172,176,179,181,182,183,195,566,861,864],[101,164,172,176,179,181,182,183,195,567,573,861,864],[101,164,172,176,179,181,182,183,195,565,566,567,572,861,864],[101,164,172,176,179,181,182,183,195,565,567,861,864],[101,164,172,176,179,181,182,183,195,566,567,579,861,864],[101,164,172,176,179,181,182,183,195,568,569,570,861,864],[101,164,172,176,179,181,182,183,195,571,861,864],[101,164,172,176,179,181,182,183,195,200,220,861,864],[101,116,119,122,123,164,172,176,179,181,182,183,195,212,861,864],[101,119,164,172,176,179,181,182,183,195,200,212,861,864],[101,119,123,164,172,176,179,181,182,183,195,212,861,864],[101,164,172,176,179,181,182,183,195,200,861,864],[101,113,164,172,176,179,181,182,183,195,861,864],[101,117,164,172,176,179,181,182,183,195,861,864],[101,115,116,119,164,172,176,179,181,182,183,195,212,861,864],[101,164,172,176,179,181,182,183,185,195,209,861,864],[101,113,164,172,176,179,181,182,183,195,220,861,864],[101,115,119,164,172,176,179,181,182,183,185,195,212,861,864],[101,110,111,112,114,118,164,172,175,176,179,181,182,183,195,200,212,861,864],[101,119,128,136,164,172,176,179,181,182,183,195,861,864],[101,111,117,164,172,176,179,181,182,183,195,861,864],[101,119,145,146,164,172,176,179,181,182,183,195,861,864],[101,111,114,119,164,172,176,179,181,182,183,195,203,212,220,861,864],[101,119,164,172,176,179,181,182,183,195,861,864],[101,115,119,164,172,176,179,181,182,183,195,212,861,864],[101,110,164,172,176,179,181,182,183,195,861,864],[101,113,114,115,117,118,119,120,121,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,146,147,148,149,150,164,172,176,179,181,182,183,195,861,864],[101,119,138,141,164,172,176,179,181,182,183,195,861,864],[101,119,128,129,130,164,172,176,179,181,182,183,195,861,864],[101,117,119,129,131,164,172,176,179,181,182,183,195,861,864],[101,118,164,172,176,179,181,182,183,195,861,864],[101,111,113,119,164,172,176,179,181,182,183,195,861,864],[101,119,123,129,131,164,172,176,179,181,182,183,195,861,864],[101,123,164,172,176,179,181,182,183,195,861,864],[101,117,119,122,164,172,176,179,181,182,183,195,212,861,864],[101,111,115,119,128,164,172,176,179,181,182,183,195,861,864],[101,119,138,164,172,176,179,181,182,183,195,861,864],[101,131,164,172,176,179,181,182,183,195,861,864],[101,113,119,145,164,172,176,179,181,182,183,195,203,218,220,861,864],[101,164,172,176,179,181,182,183,195,562,861,864],[101,164,172,175,176,178,179,180,181,182,183,185,195,200,209,212,219,220,562,563,564,574,575,576,578,580,582,583,584,585,605,609,610,611,612,613,861,864],[101,164,172,176,179,181,182,183,195,562,563,564,581,861,864],[101,164,172,176,179,181,182,183,195,564,861,864],[101,164,172,176,179,181,182,183,195,608,861,864],[101,164,172,176,179,181,182,183,195,574,584,613,861,864],[101,164,172,176,179,181,182,183,195,574,613,861,864],[101,164,172,176,179,181,182,183,195,654,861,864],[101,164,172,176,179,181,182,183,195,627,659,684,861,864],[101,164,172,176,179,181,182,183,195,617,620,622,623,629,630,631,633,634,637,638,650,651,653,684,861,864],[101,164,172,176,179,181,182,183,195,633,644,645,684,861,864],[101,164,172,176,179,181,182,183,195,633,634,641,684,861,864],[101,164,172,176,179,181,182,183,195,620,622,633,634,637,684,861,864],[101,164,172,176,179,181,182,183,195,582,861,864],[101,164,172,176,179,181,182,183,195,620,627,633,634,637,646,684,861,864],[101,164,172,176,179,181,182,183,195,613,657,659,861,864],[101,164,167,172,176,179,181,182,183,195,200,613,620,622,627,631,633,634,637,638,641,642,643,646,649,650,651,655,656,659,684,861,864],[101,164,172,176,179,181,182,183,195,582,633,634,637,684,861,864],[101,164,172,176,179,181,182,183,195,633,644,645,646,684,861,864],[101,164,172,176,179,181,182,183,195,582,633,638,639,640,684,861,864],[101,164,167,172,176,179,181,182,183,195,200,582,613,620,622,627,631,633,634,637,638,639,640,641,642,643,644,645,646,649,650,651,655,656,657,658,659,684,861,864],[101,164,172,176,179,181,182,183,195,582,617,620,622,627,631,633,634,637,638,639,640,641,642,644,645,646,649,684,685,686,687,688,693,861,864],[101,164,172,176,179,181,182,183,195,620,622,633,634,637,638,644,645,646,684,686,861,864],[101,164,172,176,179,181,182,183,195,694,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,821,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,823,861,864],[101,164,172,176,179,181,182,183,195,255,536,748,749,797,819,820,861,864],[101,164,172,176,179,181,182,183,195,255,749,797,819,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,827,861,864],[101,164,172,176,179,181,182,183,195,255,748,749,797,819,820,826,861,864],[101,164,172,176,179,181,182,183,195,255,746,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,749,750,861,864],[101,164,172,176,179,181,182,183,195,255,553,749,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,753,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,752,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,749,756,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,758,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,760,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,762,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,764,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,767,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,772,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,771,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,769,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,775,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,778,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,780,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,782,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,784,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,786,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,766,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,789,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,749,791,861,864],[101,164,172,176,179,181,182,183,195,255,553,748,749,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,794,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,793,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,830,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,820,829,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,833,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,820,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,832,861,864],[101,164,172,176,179,181,182,183,195,255,536,743,746,749,797,861,864],[92,101,164,172,176,179,181,182,183,195,255,554,557,812,813,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,839,861,864],[101,164,172,176,179,181,182,183,195,255,838,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,798,819,820,861,864],[101,164,172,176,179,181,182,183,195,255,749,797,819,848,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,826,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,748,819,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,803,813,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,813,829,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,820,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,813,818,861,864],[92,101,164,172,176,179,181,182,183,195,255,536,748,818,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,838,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,837,861,864],[92,101,164,172,176,179,181,182,183,195,255,536,546,817,837,861,864],[101,164,172,176,179,181,182,183,195,255,817,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,845,861,864],[92,101,164,172,176,179,181,182,183,195,255,844,861,864],[101,164,172,176,179,181,182,183,195,255,748,861,864],[92,101,164,172,176,179,181,182,183,195,255,748,861,864],[92,101,164,172,176,179,181,182,183,195,255,748,799,803,845,846,847,861,864],[92,101,164,172,176,179,181,182,183,195,255,803,861,864],[92,101,164,172,176,179,181,182,183,195,255,546,748,803,861,864],[92,101,164,172,176,179,181,182,183,195,255,536,799,803,817,861,864],[92,101,164,172,176,179,181,182,183,195,255,694,749,861,864],[101,164,172,176,179,181,182,183,195,255,694,746,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,798,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,748,803,804,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,797,861,864],[92,101,164,172,176,179,181,182,183,195,255,554,743,746,748,861,864],[101,164,172,176,179,181,182,183,195,255,554,736,743,744,745,861,864],[101,164,172,176,179,181,182,183,195,255,748,797,861,864],[101,164,172,176,179,181,182,183,195,255,861,864],[101,164,172,176,179,181,182,183,195,255,748,803,861,864],[101,164,172,176,179,181,182,183,195,212,255,615,660,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,861,864]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"1e9332c23e9a907175e0ffc6a49e236f97b48838cc8aec9ce7e4cec21e544b65","impliedFormat":1},{"version":"3753fbc1113dc511214802a2342280a8b284ab9094f6420e7aa171e868679f91","impliedFormat":1},{"version":"999ca32883495a866aa5737fe1babc764a469e4cde6ee6b136a4b9ae68853e4b","impliedFormat":1},{"version":"17f13ecb98cbc39243f2eee1f16d45cd8ec4706b03ee314f1915f1a8b42f6984","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bd714129fca875f7d4c477a1a392200b0bcd13fb2e80928cd334b63830ea047","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2c9037ae6cd2c52d80ceef0b3c5ffdb488627d71529cf4f63776daf11161c9a","affectsGlobalScope":true,"impliedFormat":1},{"version":"135d5cf4d345f59f1a9caadfafcd858d3d9cc68290db616cc85797224448cccc","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc238c3f81c2984751932b6aab223cd5b830e0ac6cad76389e5e9d2ffc03287d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4a07f9b76d361f572620927e5735b77d6d2101c23cdd94383eb5b706e7b36357","affectsGlobalScope":true,"impliedFormat":1},{"version":"7c4e8dc6ab834cc6baa0227e030606d29e3e8449a9f67cdf5605ea5493c4db29","affectsGlobalScope":true,"impliedFormat":1},{"version":"de7ba0fd02e06cd9a5bd4ab441ed0e122735786e67dde1e849cced1cd8b46b78","affectsGlobalScope":true,"impliedFormat":1},{"version":"6148e4e88d720a06855071c3db02069434142a8332cf9c182cda551adedf3156","affectsGlobalScope":true,"impliedFormat":1},{"version":"d63dba625b108316a40c95a4425f8d4294e0deeccfd6c7e59d819efa19e23409","affectsGlobalScope":true,"impliedFormat":1},{"version":"0568d6befee03dd435bed4fc25c4e46865b24bdcb8c563fdc21f580a2c301904","affectsGlobalScope":true,"impliedFormat":1},{"version":"30d62269b05b584741f19a5369852d5d34895aa2ac4fd948956f886d15f9cc0d","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"ffbe6d7b295306b2ba88030f65b74c107d8d99bdcf596ea99c62a02f606108b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"996fb27b15277369c68a4ba46ed138b4e9e839a02fb4ec756f7997629242fd9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"79b712591b270d4778c89706ca2cfc56ddb8c3f895840e477388f1710dc5eda9","affectsGlobalScope":true,"impliedFormat":1},{"version":"20884846cef428b992b9bd032e70a4ef88e349263f63aeddf04dda837a7dba26","affectsGlobalScope":true,"impliedFormat":1},{"version":"5fcab789c73a97cd43828ee3cc94a61264cf24d4c44472ce64ced0e0f148bdb2","affectsGlobalScope":true,"impliedFormat":1},{"version":"db59a81f070c1880ad645b2c0275022baa6a0c4f0acdc58d29d349c6efcf0903","affectsGlobalScope":true,"impliedFormat":1},{"version":"673294292640f5722b700e7d814e17aaf7d93f83a48a2c9b38f33cbc940ad8b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"d786b48f934cbca483b3c6d0a798cb43bbb4ada283e76fb22c28e53ae05b9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"142efd4ce210576f777dc34df121777be89eda476942d6d6663b03dcb53be3ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"379bc41580c2d774f82e828c70308f24a005b490c25ba34d679d84bcf05c3d9d","affectsGlobalScope":true,"impliedFormat":1},{"version":"ed484fb2aa8a1a23d0277056ec3336e0a0b52f9b8d6a961f338a642faf43235d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ffedae1d1c2d53fdbca1c96d3c7dda544281f7d262f99b6880634f8fd8d9820","affectsGlobalScope":true,"impliedFormat":1},{"version":"83a730b125d477dd264df8ba479afab27a3dae7152b005c214ab94dc7ee44fd3","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"dc0a7f107690ee5cd8afc8dbf05c4df78085471ce16bdd9881642ec738bc81fe","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"21da358700a3893281ce0c517a7a30cbd46be020d9f0c3f2834d0a8ad1f5fc75","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc2110f7decca6bfb9392e30421cfa1436479e4a6756e8fec6cbc22625d4f881","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"4137ebf04166f3a325f056aa56101adc75e9dceb30404a1844eb8604d89770e2","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"98498b101803bb3dde9f76a56e65c14b75db1cc8bec5f4db72be541570f74fc5","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2ae155afe8a01cc0ae612d99117cf8ef16692ba7c4366590156fdec1bcf2d8c","impliedFormat":1},{"version":"3f5e5d9be35913db9fea42a63f3df0b7e3c8703b97670a2125587b4dbbd56d7c","impliedFormat":1},{"version":"8caeb65fdc3bfe0d13f86f67324fcb2d858ed1c55f1f0cce892eb1acfb9f3239","impliedFormat":1},{"version":"57c23df0b5f7a8e26363a3849b0bc7763f6b241207157c8e40089d1df4116f35","affectsGlobalScope":true,"impliedFormat":1},{"version":"3b8bc0c17b54081b0878673989216229e575d67a10874e84566a21025a2461ee","impliedFormat":1},{"version":"5b0db5a58b73498792a29bfebc333438e61906fef75da898b410e24e52229e6f","impliedFormat":1},{"version":"dbe055b2b29a7bab2c1ca8f259436306adb43f469dca7e639a02cd3695d3f621","impliedFormat":1},{"version":"1678b04557dca52feab73cc67610918a7f5e25bfdba3e7fa081acd625d93106d","impliedFormat":1},{"version":"e3905f6902f0b69e5eefc230daa69fdd4ab707a973ec2d086d65af1b3ea47ef0","impliedFormat":1},{"version":"2ea729503db9793f2691162fec3dd1118cab62e96d025f8eeb376d43ec293395","impliedFormat":1},{"version":"9ec87fea42b92894b0f209931a880789d43c3397d09dd99c631ae40a2f7071d1","impliedFormat":1},{"version":"c68e88cdfadfb6c8ba5fc38e58a3a166b0beae77b1f05b7d921150a32a5ffb8d","impliedFormat":1},{"version":"2bc7aa4fba46df0bd495425a7c8201437a7d465f83854fac859df2d67f664df3","impliedFormat":1},{"version":"41d17e1ad9a002feb11c8cdd2777e5bbc0cdb1e3f595d237e4dded0b6949983b","impliedFormat":1},{"version":"07e4e61e946a9c15045539ecd5f5d2d02e7aab6fa82567826857e09cf0f37c2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c4714ccc29149efb8777a1da0b04b8d2258f5d13ddbf4cd3c3d361fb531ac86","impliedFormat":1},{"version":"3ff275f84f89f8a7c0543da838f9da9614201abc4ce74c533029825adfb4433d","impliedFormat":1},{"version":"0eb5d0cbf09de5d34542b977fd6a933bb2e0817bffe8e1a541b2f1ad1b9af1ff","impliedFormat":1},{"version":"f9713757bcdfa4d58b48c0fb249e752c94a3eee8bf4532b906094246ac49ef88","impliedFormat":1},{"version":"2c2bdaa1d8ead9f68628d6d9d250e46ee8e81aa4898b4769a36956ae15e060fe","impliedFormat":1},{"version":"c32c840c62d8bd7aeb3147aa6754cd2d922b990a6b6634530cb2ebdce5adc8e9","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"82b91e4e42e6c41bc7fc1b6c2dc5eba6a2ba98375eb1f210e6ff6bba2d54177e","impliedFormat":1},{"version":"6fe28249ac0c7bc19a79aa9264baf00efbd080e868dbe1d3052033ad1c64f206","affectsGlobalScope":true,"impliedFormat":1},{"version":"cbed824fec91efefc7bbdcb8b43d1a531fdbebd0e2ef19481501ff365a93cb70","impliedFormat":1},{"version":"d0716593b3f2b0451bcf0c24cfa86dec2235c325c89f201934248b7c742715fc","impliedFormat":1},{"version":"ec501101c2a96133a6c695f934c8f6642149cc728571b29cbb7b770984c1088e","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"2991bca2cc0f0628a278df2a2ccdb8d6cbcb700f3761abbed62bba137d5b1790","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"230763250f20449fa7b3c9273e1967adb0023dc890d4be1553faca658ee65971","impliedFormat":1},{"version":"c3e9078b60cb329d1221f5878e88cecfa3e74460550e605a58fcfb41a66029ff","impliedFormat":1},{"version":"a74edb3bab7394a9dbde529d60632be590def2f5f01024dbd85441587fbfbbe0","impliedFormat":1},{"version":"0ea59f7d3e51440baa64f429253759b106cfcbaf51e474cae606e02265b37cf8","impliedFormat":1},{"version":"bc18a1991ba681f03e13285fa1d7b99b03b67ee671b7bc936254467177543890","impliedFormat":1},{"version":"00049ccc87f3f37726db03c01ca68fe74fd9c0109b68c29eb9923ebec2c76b13","impliedFormat":1},{"version":"fa94bbf532b7af8f394b95fa310980d6e20bd2d4c871c6a6cb9f70f03750a44b","impliedFormat":1},{"version":"68d3f35108e2608b1f2f28b36d19d7055f31c4465cc5692cbd06c716a9fe7973","impliedFormat":1},{"version":"a6d543044570fbeed13a7f9925a868081cd2b14ef59cdd9da6ae76d41cab03d3","affectsGlobalScope":true,"impliedFormat":1},{"version":"7fa2214bb0d64701bc6f9ce8cde2fd2ff8c571e0b23065fa04a8a5a6beb91511","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"eab2f3179607acb3d44b2db2a76dd7d621c5039b145dc160a1ee733963f9d2f5","impliedFormat":1},{"version":"841983e39bd4cbb463be385e92fda11057cab368bf27100a801c492f1d86cbaa","impliedFormat":1},{"version":"6f5383b3df1cdf4ff1aa7fb0850f77042b5786b5e65ec9a9b6be56ebfe4d9036","impliedFormat":1},{"version":"62fc21ed9ccbd83bd1166de277a4b5daaa8d15b5fa614c75610d20f3b73fba87","impliedFormat":1},{"version":"e4156ddb25aa0e3b5303d372f26957b36778f0f6bbd4326359269873295e3058","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc1b433a84cae05ddc5672d4823170af78606ad21ecef60dbc4570190cbf1357","impliedFormat":1},{"version":"9d3821bc75c59577e52643324cec92fc2145642e8d17cf7ee07a3181f21d985d","impliedFormat":1},{"version":"7f78cfb2b343838612c192cb251746e3a7c62ac7675726a47e130d9b213f6580","impliedFormat":1},{"version":"201db9cf1687fab1adf5282fcba861f382b32303dc4f67c89d59655e78a25461","impliedFormat":1},{"version":"c77fb31bc17fd241d3922a9f88c59e3361cdf76d1328ba9412fc6bf7310b638d","impliedFormat":1},{"version":"0a20eaf2e4b1e3c1e1f87f7bccb0c936375b23b022baeea750519b7c9bc6ce83","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"a16b91b27bd6b706c687c88cbc8a7d4ee98e5ed6043026d6b84bda923c0aed67","impliedFormat":1},{"version":"694b812e0ed11285e8822cf8131e3ce7083a500b3b1d185fff9ed1089677bd0a","impliedFormat":1},{"version":"99ab6d0d660ce4d21efb52288a39fd35bb3f556980ec5463b1ae8f304a3bbc85","impliedFormat":1},{"version":"6eeded8c7e352be6e0efb83f4935ec752513c4d22043b52522b90849a49a3a11","impliedFormat":1},{"version":"6c1ad90050ffbb151cacc68e2d06ea1a26a945659391e32651f5d42b86fd7f2c","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"446a50749b24d14deac6f8843e057a6355dd6437d1fac4f9e5ce4a5071f34bff","impliedFormat":1},{"version":"182e9fcbe08ac7c012e0a6e2b5798b4352470be29a64fdc114d23c2bab7d5106","impliedFormat":1},{"version":"2f4e6b4d39426a1b85ecf4bdeb9dddbf4d9b3397d95d8555d46f925c9519ec7d","impliedFormat":1},{"version":"78a2869ad0cbf3f9045dda08c0d4562b7e1b2bfe07b19e0db072f5c3c56e9584","impliedFormat":1},{"version":"89d5d28d4f57e000b836ac273079be1b75710e28ce14750d081fb420d37e2ca5","impliedFormat":1},{"version":"fd4e24ccff3966390600d7f5d6aa1fed5a512e92ada735ea5fbc933d313ad3d3","impliedFormat":1},{"version":"b7cddfe1aa6b86b5fad3c9ccb30d05b3ccb165aebbf112f48d2d8a5f69dd98b1","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"bd2c7ada3dee03653d3f601011d30072194bc3970cd93208f9588fbdc0c69347","impliedFormat":1},{"version":"e480da45d32313e7174b265674da504f075f59ef326852f0c5a5d863b438ae85","impliedFormat":1},{"version":"ad54850f61fcf5d014e11be80d2f46fea9265cfa7e77456da876f7833ef81769","impliedFormat":1},{"version":"6f7c9e8bd2b5b6a080b07080065f94900bd3c7e5ebbd3047bc33fcce2fab1dd8","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"da5950ee2a90721df6f3fba45f5d05308f7e4c35835392215dd2cd404505e2de","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"f42d5fed19610d485c646a0c430e768115567d078c7fc855c57b0c578b3d6cd3","impliedFormat":1},{"version":"ee8df1cb8d0faaca4013a1b442e99130769ce06f438d18d510fed95890067563","impliedFormat":1},{"version":"d5630f2ad9b4541e5ce891648121022f9412ecdca1820baa1f0104f70fd7eff7","impliedFormat":1},{"version":"4d15375ab13497104bc8fe56fdef2b5fd6853f29255737d23a33fa306ff7fd69","impliedFormat":1},{"version":"2cd3fc1d0d6a1e85baffd2d4f50f5efb192b5446eef567e97c94765402f0aad4","impliedFormat":1},{"version":"e4cbf2f1e89ecccaddd2c045e600ae41b732295953fb06247c7dcbc2d281ed30","impliedFormat":1},{"version":"6dcedaef57dff0d79a05ab0ab602cde74db803d1e765468bf91263786a383e1b","impliedFormat":1},{"version":"8c1697d90c394a6fd955b98eae01238eff628e129b987a68aea10f898a48e7da","impliedFormat":1},{"version":"7580e62139cb2b44a0270c8d01abcbfcba2819a02514a527342447fa69b34ef1","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"d10d63718e1646c2279e3b33831f82c60e31f622b2b7020f1196409ca4c09242","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"148679c6d0f449210a96e7d2e562d589e56fcde87f843a92808b3ff103f1a774","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"02436d7e9ead85e09a2f8e27d5f47d9464bced31738dec138ca735390815c9f0","impliedFormat":1},{"version":"f8d5ff8eafd37499f2b6a98659dd9b45a321de186b8db6b6142faed0fea3de77","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"a22dd55aa4d39906252000ab8e8a1b83b195eef7f4274eb51e457c1f11cf6580","impliedFormat":1},{"version":"540cc83ab772a2c6bc509fe1354f314825b5dba3669efdfbe4693ecd3048e34f","impliedFormat":1},{"version":"121b0696021ab885c570bbeb331be8ad82c6efe2f3b93a6e63874901bebc13e3","impliedFormat":1},{"version":"612d9da66bb046a9c1e2e8d026245ded881fc4b9f98cbfae714415d57ee0ae0b","impliedFormat":1},{"version":"32c2ad9494dad5d11b0564a619fee18f388db6c1e9e2cd3c360b3122549691eb","impliedFormat":1},{"version":"6c301d40aec56a74ec7bd7324e31a728dadf9bfba3e96def02938d3d973534ec","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"aa14cee20aa0db79f8df101fc027d929aec10feb5b8a8da3b9af3895d05b7ba2","impliedFormat":1},{"version":"493c700ac3bd317177b2eb913805c87fe60d4e8af4fb39c41f04ba81fae7e170","impliedFormat":1},{"version":"aeb554d876c6b8c818da2e118d8b11e1e559adbe6bf606cc9a611c1b6c09f670","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"25a5f6fd3a2243c859eddc99ab5fba11d970af2fe7a5df9c32b7668f76f97b01","impliedFormat":1},{"version":"8d207e1f9d2c30d6f77dfa693f3827c3fbf0d89240297e10bdfe1041d433df68","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"8c70ddc0c22d85e56011d49fddfaae3405eb53d47b59327b9dd589e82df672e7","impliedFormat":1},{"version":"2f9c89cbb29d362290531b48880a4024f258c6033aaeb7e59fbc62db26819650","impliedFormat":1},{"version":"a365c4d3bed3be4e4e20793c999c51f5cd7e6792322f14650949d827fbcd170f","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"273782b8454e78f6a8b30d2cfbf6860499c930595095fcc1689637115f0eddda","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fbdd025f9d4d820414417eeb4107ffa0078d454a033b506e22d3a23bc3d9c41","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"9f9bb6755a8ce32d656ffa4763a8144aa4f274d6b69b59d7c32811031467216e","impliedFormat":1},{"version":"5c32bdfbd2d65e8fffbb9fbda04d7165e9181b08dad61154961852366deb7540","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"0c05e9842ec4f8b7bfebfd3ca61604bb8c914ba8da9b5337c4f25da427a005f2","impliedFormat":1},{"version":"faed7a5153215dbd6ebe76dfdcc0af0cfe760f7362bed43284be544308b114cf","impliedFormat":1},{"version":"7029e566b8df176f703fb59fd437a38670c7a0e02c58b2d66dfb5b2e2b2defdb","impliedFormat":1},{"version":"7f2aa4d4989a82530aaac3f72b3dceca90e9c25bee0b1a327e8a08a1262435ad","impliedFormat":1},{"version":"d96b39301d0ded3f1a27b47759676a33a02f6f5049bfcbde81e533fd10f50dcb","impliedFormat":1},{"version":"e9f147ecca73d9346a4c073432843c159ccbe50bdcb678a78f6da10eae2cecf4","impliedFormat":1},{"version":"de061f7d72bd65c06fc1419f841dfdcb29a8e22fe6fa527d1e6eb20b897d4de0","impliedFormat":1},{"version":"663beafc2446079574570cba86e9b15f986f908ddb1b01274509970126fee945","impliedFormat":1},{"version":"a3102887d5058bf4cb5b37fa6964c09e9527c42053b3b5c642b89878620748de","impliedFormat":1},{"version":"0aaaa1727edd29673d85c9b26d7ca4d54e5407a48586903c51b48b7f7d196f61","impliedFormat":1},{"version":"d35bca0b261bff02635758c48e8ab99c61c420d0dfabbcf467e847171d876b7d","impliedFormat":1},{"version":"3bc12c40d90c342ff88a3d876996c555ed5cbee5fe8c3308a240b321f401ee46","impliedFormat":1},{"version":"ba130768aae855a5477e9e148e5c879548e6e7ccbcc56fd1934c8a18ea5b7569","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d38530db0601215d6d767f280e3a3c54b2a83b709e8d9001acb6f61c67e965fc","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"b499af2054a037a162b3b72cd886f48bbf32a3502c865c6e29fac7d2ab3ce0b5","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"48773ca557b0319c2ee62ae249cf52a81709e8be139920d6479a66274de7c4ed","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"4cceef18d7f088e797a463e90b7a9dad10c6bc667724b7686e3e740ae00122be","impliedFormat":1},{"version":"7ee86fbb3754388e004de0ef9e6505485ddfb3be7640783d6d015711c03d302d","impliedFormat":1},{"version":"cc1954b539604b1e562319119ac7e888172208b32ca873f9a357a92c826bd046","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"bb8f2dbc03533abca2066ce4655c119bff353dd4514375beb93c08590c03e023","impliedFormat":1},{"version":"706dd95827e7ebaabda91d5db2b755233e0952d98570e9c032b0f066a15c1177","affectsGlobalScope":true,"impliedFormat":1},{"version":"0b103e9abfe82d14c0ad06a55d9f91d6747154ef7cacc73cf27ecad2bfb3afcf","impliedFormat":1},{"version":"990b8fad2327b77e6920cc792af320e8867e68f02ce849b12c0a6ab9a1aebb09","impliedFormat":1},{"version":"5eb8cd1cb0c9143d74a8190b577c522720878c31aef67d866fcd29973f83e955","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"a6805fcafed712aea7759f8bc731014f9d22738c1d6ef9d43b8091d1d48346d5","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"d88ea80a6447d7391f52352ec97e56b52ebec934a4a4af6e2464cfd8b39c3ba8","impliedFormat":1},{"version":"142617b3cdf902b69c6464c9fbd942b60ab3e733ca18c032b19e0f7e2adbefe8","impliedFormat":1},{"version":"0b603555f1881f87256ffd6344d3e3ed6d466c2e701eabf381f28be8c2125892","impliedFormat":1},{"version":"897e4f7662488e3ecc79e743bdd3b78f13bdb69a97851afa5b440c4211e32ea9","impliedFormat":1},{"version":"e2e1c6d3b2d93add5200bd7bc1a8cccb4e446836b2111ece45db8683a2c765de","impliedFormat":1},{"version":"251b03d5cd243854ce870d9a9a39f491faf69898c5d6b5eee28cc7649c57417b","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"2c4de79f406d137390608e8c0a44fba2ff8e00bacfcae7c9d1781fef10e9440d","impliedFormat":1},{"version":"07ba23a10465791be5d22deaf5ef7de7658774ddff53721e5ea17fedea1bc721","impliedFormat":1},{"version":"dca8c645c5afeb03b1ecedbf16323f33e7d0afaa6256c8e047e6e38087a97f53","impliedFormat":1},{"version":"775f181bd4a533d6f8b5e55ec1d9f1624559720ae8a70e9432258da26b38d27c","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"9109a1291dd4b9f1541bea81ee11c247a2ca9e1ea89f87f13aa1811c3c069616","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"622694a8522b46f6310c2a9b5d2530dde1e2854cb5829354e6d1ff8f371cf469","impliedFormat":1},{"version":"cd8ce8d68567f62dd580b3c3c37777ac3f5b81944c7417f5ea83030eab533385","impliedFormat":1},{"version":"e374d1eaa05b7dc38580062942ac8351ce79cbe11f6dbce4946a582a5680582d","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9e6c0ff3f8186fccd05752cf75fc94e147c02645087ac6de5cc16403323d870","impliedFormat":1},{"version":"49af4b52f0d4d2304c5f2c6fe5fab3e153e0acc38830d0202821b877c097dd02","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"e68b8e5a1df7c1be2bc105141456ecba70215806e1c28bfbc5c12bfce4be6e68","impliedFormat":1},{"version":"511c8f02329808d47d00b859c532ae9115590048b17325a946c74dac48428650","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"b5f9e66625783eefcbe3d2da074b2e7ba2066d61ce3fc6ef4f22805ad946cab4","impliedFormat":1},{"version":"e37115962d284b9f7a37c2bdd2add50f88365dde41f5e0ff591ffc48a8ec7575","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"f89488602bec98a142072fae7ea5ba99431a569ff580c64b7be39896474799d8","impliedFormat":1},{"version":"bbbc47961f39a57df103cf4ca3bb8f8732b4b6678a18225a0aa76d59c466956c","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"2ffb043dc5163458e473b7010859f86e01dc4edffcae0a93d885d028b426a546","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"05c7280d72f3ed26f346cbe7cbbbb002fb7f15739197cbbee6ab3fd1a6cb9347","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"04b7b2e0832dfd3c31e81df3975e8d8fda28e7ff999b0aa2932608a8f6661d5c","impliedFormat":1},{"version":"ca2d34c6ed5cbd3070b8b6f32f42ae54adcc6499c1e4b99f0a5798b3f27cc653","impliedFormat":1},{"version":"9ec68995e66dd6b9dac834bf5ae85fde802714ea2e82151a5d1d53ef01b463ef","impliedFormat":1},{"version":"5c4d626b4902f2ef8a1cc146d761d276cef988016dc674e3b98fbad70e64bc9f","impliedFormat":1},{"version":"fdfaa0aad899524962e2955287b5b991ffe3be50f64e02eb60c933ca44644a94","impliedFormat":1},{"version":"53c972a0f9bc3a4ec70fff7314123ea8cfcf75b3703046f767d2dc1eea87b2fb","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"d130c5f73768de51402351d5dc7d1b36eaec980ca697846e53156e4ea9911476","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"7303b45138d2511035056a5901a1490ebdcbf055cbb1276f8629c5121cbe733e","impliedFormat":1},{"version":"27f874cd5327507eeff699a74567f60c1215b94509f4308633a7b01922471ed2","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"2c6cf04bc525caf6546e859e8ef10bfb9573837ec0bc5ec7b53a7b1b8ca72781","impliedFormat":1},{"version":"8695dec09ad439b0ceef3776ea68a232e381135b516878f0901ed2ea114fd0fe","impliedFormat":1},{"version":"304b44b1e97dd4c94697c3313df89a578dca4930a104454c99863f1784a54357","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"87cc05fe13108f02e12da7e3efd8e360fef78d96a0c9e11408ea1b1b9fb3e03d","impliedFormat":1},{"version":"1abbf67c218d23c2ce76887caac2df6c7dab3d97ba2b65348432b876f510002a","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"c06ef3b2569b1c1ad99fcd7fe5fba8d466e2619da5375dfa940a94e0feea899b","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"8bd496cf710d4873d15e4891a5dbf945673e3321ca74cf75187e347fd5ed295e","impliedFormat":1},{"version":"a6dba407fc287f1e25454e75028c91bbc00675f2d1c4e8b3edcc36c08611a486","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"c0723195c85e19656d6b5b9fdb81d3f3403c1ae4679e722c6ea058c516b38d12","impliedFormat":1},{"version":"186eea74805194f04e41038fc5eca653788b9dedbab7c2d7d17e10139622dd92","impliedFormat":1},{"version":"71d9eb4c4e99456b78ae182fb20a5dfc20eb1667f091dbb9335b3c017dd1c783","impliedFormat":1},{"version":"cfa846a7b7847a1d973605fbb8c91f47f3a0f0643c18ac05c47077ebc72e71c7","impliedFormat":1},{"version":"1594da19968752a22b2ac48c2d0e60575700e745c577a8a4a676b841238ad5bb","impliedFormat":1},{"version":"e0cee12109e0a10a4c3d6769fcc7644b7c1ea7f52365bea51728f5af29f8a137","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"3536968defef8a75514f547ead5e2e9c1e984820290ec9b00c5fdfb6ef786535","impliedFormat":1},{"version":"d83773870080c30a230e322ce13a9c6f3398e8dacea4ea8a83e26370f3bac23e","impliedFormat":1},{"version":"dcfeaf98d66314fec29a9076c4290e45d0b196a65827becc19138e9c7b855f37","impliedFormat":1},{"version":"6849fe9210fe4946d5f085bfed36758f33dc6ae15a751338d178dd4daa017c46","impliedFormat":1},{"version":"888cda0fa66d7f74e985a3f7b1af1f64b8ff03eb3d5e80d051c3cbdeb7f32ab7","impliedFormat":1},{"version":"60681e13f3545be5e9477acb752b741eae6eaf4cc01658a25ec05bff8b82a2ef","impliedFormat":1},{"version":"ffae4e1e06aa848a1e4bcef162cd1c48e5909b26223515981310af9c036bdfc7","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"34e16eb7c31768a11a08aebcfb3d70d7b8f0b016197e98d8419e566ceae6d6c8","impliedFormat":1},{"version":"f94ec1f7e4b709d26960306c9082a7a1b728a6e13089346aa48ba57c74cbf47e","impliedFormat":1},{"version":"9a11cb4033405e96c247cd5aa29790212aaffdd127869e8a5219103f0b389fd5","impliedFormat":1},{"version":"01479d9d5a5dda16d529b91811375187f61a06e74be294a35ecce77e0b9e8d6c","impliedFormat":1},{"version":"aff5213585cb72e94054dfe17250ff315f3569b3919d1ef1ad235f37c4ee894e","impliedFormat":1},{"version":"fb2ea35e1be6388d722d7725e2b49c697d34d9c890c3b96758faaeb86d35cef8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"1a4dc28334a926d90ba6a2d811ba0ff6c22775fcc13679521f034c124269fd40","impliedFormat":1},{"version":"f05315ff85714f0b87cc0b54bcd3dde2716e5a6b99aedcc19cad02bf2403e08c","impliedFormat":1},{"version":"5fad3b31fc17a5bc58095118a8b160f5260964787c52e7eb51e3d4fcf5d4a6f0","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"456006a6975b26c0a1785feddae165f6d307e2d601ffde27e21fc4a790e448a4","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"1fe0d18b111e1145a7e7601855bccd4ca20f24e3b9a5aba6bb1fa9d1a7059170","impliedFormat":1},{"version":"5632c3c26d420c063eebe64c45b1248b9492a67bf44f1d0c57e9dc8f6cf449bb","impliedFormat":1},{"version":"0df5aa619ab12993a39ea6dae062ee46eadbb4d738916460e636ada52bced75b","impliedFormat":1},{"version":"8fca3039857709484e5893c05c1f9126ab7451fa6c29e19bb8c2411a2e937345","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"10ab7be91f87ebe8916b62cf28af2e45b5601fc7b0e311adf838f912c6b31dd8","impliedFormat":1},{"version":"bc636fbc08e0979ceb7eb0731a33000283d77a33b62e1f71ee65be50394e40ba","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"75bbd3be047d539988a0ff0b56384ef7a6a25f3b676ad96bee547d44c31622a7","impliedFormat":1},{"version":"42960001a776b089ade681ab5cfddc936e0afb0615133ec1841f3dee89d3e1bf","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"da47712b394d944328245482603bc6f416d3949b67c9392279caab595076b510","affectsGlobalScope":true,"impliedFormat":1},{"version":"37d0071d8f0a06dc55c2c5e0ec3391affd4fd107c53410bf358196ec0bf3923f","impliedFormat":1},{"version":"b213dad76ca37fd552274c9499056e1c0d9c1bd38a55bb7f68b22ba6b84c3ad7","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"5a49adaef698b7ad7e6127949fa1b0bbd3d46b7cbd11c54e392a4dcdd51f5190","impliedFormat":1},{"version":"96171c03c2e7f314d66d38acd581f9667439845865b7f85da8df598ff9617476","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"2489bf04d77dc025ba67f49f1a56eb24b9db477d5ff88123d887e163ed1776aa","impliedFormat":1},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"0b77b819b5417775fccb20c678293cf614c054a5b1a65421a5b933a9124ba998","impliedFormat":1},{"version":"e1f6076688a95bd82deaac740fccbe3cdea0d8a22057cccc9c5bce4398bdd33b","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"b1f1d57fde8247599731b24a733395c880a6561ec0c882efaaf20d7df968c5af","impliedFormat":1},{"version":"6715dc4eb59c8ea9abe2b78c235ed331dc710a06fe56798868dbc4d40cd1b707","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"5a3ea721d03a361ccbdd7390ccd75f6e84cbca3a3f01f4b331ecc9af31890c49","impliedFormat":1},{"version":"e7dfaee4af38d45b1cab8a1ee0b3bc1f85ddcf64545ed391d675d78ae6526274","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8daa443eaf9a27fd382cc1f8ebe30330c0f4d89511cfb469166874806751d35","impliedFormat":1},{"version":"af48e58339188d5737b608d41411a9c054685413d8ae88b8c1d0d9bfabdf6e7e","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"1de8c302fd35220d8f29dea378a4ae45199dc8ff83ca9923aca1400f2b28848a","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"332248ee37cca52903572e66c11bef755ccc6e235835e63d3c3e60ddda3e9b93","impliedFormat":1},{"version":"94e8cc88ae2ef3d920bb3bdc369f48436db123aa2dc07f683309ad8c9968a1e1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"07ed3ddab975995eea41b22f3010506fb9f5fb301d04820b07d7a1aee5477d7c","impliedFormat":1},{"version":"969d8b0965849f4bae7cab0ba90bd1e1220e95999c2c6f01117fa7500901c017","impliedFormat":1},{"version":"6ec840ee5e2bc103f557fe38b1d585ee250540468713d7634ee066de372bf332","impliedFormat":1},{"version":"b0309e1eda99a9e76f87c18992d9c3689b0938266242835dd4611f2b69efe456","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"6ceb10ca57943be87ff9debe978f4ab73593c0c85ee802c051a93fc96aaf7a20","impliedFormat":1},{"version":"1de3ffe0cc28a9fe2ac761ece075826836b5a02f340b412510a59ba1d41a505a","impliedFormat":1},{"version":"e46d6cc08d243d8d0d83986f609d830991f00450fb234f5b2f861648c42dc0d8","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"ff863d17c6c659440f7c5c536e4db7762d8c2565547b2608f36b798a743606ca","impliedFormat":1},{"version":"5412ad0043cd60d1f1406fc12cb4fb987e9a734decbdd4db6f6acf71791e36fe","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"e297c0a524edee7677939122f90027bfbe5f2698939d9a85728e5044b39c7124","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"b62381cae176db34f003cc6172ee8f3e0122014889d66391aa73698105cf4934","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"41eb514d9ce0a6e87957f08a4b7af70d93f87637f37dee706e2d92a6601c25a9","impliedFormat":1},{"version":"e7765aa8bcb74a38b3230d212b4547686eb9796621ffb4367a104451c3f9614f","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"7bd01f0f28cd3aeb2046274d85208e245965f6f2948edf4f7b2057bcf9f22ccc","impliedFormat":99},{"version":"d2f2cf2b8cc92bea913cda4a076e0f790b23a21e84f989d12f0116a7fe3906e0","impliedFormat":99},{"version":"6de125ea94866c736c6d58d68eb15272cf7d1020a5b459fea1c660027eca9a90","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5b20bc288ee49989c95b20847fc93b96bf61cc0845598897a6a53a967dd7d07","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"d3b315763d91265d6b0e7e7fa93cfdb8a80ce7cdd2d9f55ba0f37a22db00bdb8","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},{"version":"bb6d9c2b075a5c675259c75950c5f2359b17bf031b8cbdff8c04aee66a6a514f","affectsGlobalScope":true},"7ad303e40d4fddf44f156129e397511953a71481c5cfd86b1862649aaaf240cc",{"version":"168aa44771e55cda628eeb5fbb6f626f6f263d827967b119e8c06abd7f1362ec","signature":"435a1e418e8338be3f39614b96b81a9aa2700bc8c27bc6b98f064ff9ce17c363"},{"version":"3b89216a7e38a454985ad17bb2ff85792837dc812f2a89fa5f60ad0a2e216fa7","impliedFormat":99},{"version":"16fe60bb544cfedfd2b5bb2f7d0b3957be7978706d57d9f06edc9c0c8dbdba23","impliedFormat":99},{"version":"82179358c2d9d7347f1602dc9300039a2250e483137b38ebf31d4d2e5519c181","impliedFormat":99},{"version":"c73fdf42528325dd17940937ed787b15ae3445c6a2dae1a2b74bc4d87d337ca2","impliedFormat":99},{"version":"e8e17dfef3cfa9f0847ac93dd535a9896af7fb57c1a1b164484bb1b0ee4a25d8","impliedFormat":99},{"version":"51d2ffea2d1ee4a81c775938588c1e16620281adb60cbc26579a2fc6baa10bd2","impliedFormat":99},{"version":"148debd12783ded0a60d115daeacd8136f77757ae89a05c4e18de6dd77646fd2","impliedFormat":99},{"version":"0088b02dca63c47b273a140d0a3944bdc6dc2eb765fff0ca98e3c3a2786b3a5a","impliedFormat":99},{"version":"a651d06b780fa354231f19b040cbcde484bede3218885752b4f9e9a8f72d3b5f","impliedFormat":99},{"version":"06e26f75bed4c8389a8a63f0e6d6a9068038873dc95d8d1338e8c370a0ae8bc3","impliedFormat":99},{"version":"a2155e2675fd1af52b0b70779371c28611cdd1076b29d0f68bf93b983e5ddce0","impliedFormat":99},{"version":"a413e4b0b99280e1e58f5fe7b2b585e8a9be4996df8c58585399c9e2ca8a683e","impliedFormat":99},{"version":"609ab2c225766bc0851251c1db0fd5492673e190074045d21dc5dc7c3c46d785","impliedFormat":99},{"version":"c074e054c9db79055d37d7d70131e9a3234b8186773b3edb617c13f80bcf8774","impliedFormat":99},{"version":"7d3e062a778b8f5ea4f0cac7e925e31f88e6739812ebc5f827474324a4048f14","impliedFormat":99},{"version":"7f3857dc5cfe1e5e977edb14e931d9939a952e8e41997263a927f8f0299ea652","impliedFormat":99},{"version":"3559624d0102d10d7765c292c60ccbc229541534db32061e06df88bfe1064636","impliedFormat":99},{"version":"5a9834c603c65aee5cba0c1d6b3c7aee85cdc7862832a23165c6aa4139c165f2","impliedFormat":99},{"version":"a7d7b5fa83cd7b3b4c2aa73bc29e7cbd53d5690b74f6fb39a5558af0a94967ba","impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","impliedFormat":99},{"version":"605450898939e8abce51e8085a41b60640278337a969c33cd6b169e7c4f9c3f2","impliedFormat":99},{"version":"e0864480ea083087d705f9405bd6bf59b795e8474c3447f0d6413b2bce535a09","impliedFormat":99},{"version":"e67cbea16f1994af89efd700542dbf3828a46a52b29e4d67e801bd7869dc103c","impliedFormat":99},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":99},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"e3cf0611709328b449ec13f8c436712d62003620ce480139fae46ce001c2ee9f","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"3fd8a5aefd8c3feb3936ca66f5aa89dff7bf6e6537b4158dbd0f6e0d65ed3b9e","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"41c41c6e90133bb2a14f7561f29944771886e5535945b2b372e2f6ed6987746e","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":99},{"version":"f093d4bd6a9267be5f8ecbfbca19f4f3359b3839883206150c5d833606569e84","impliedFormat":99},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":99},{"version":"4a13397dffad4475c45c70fde584c925fe8c9218b3c7ab94397b68fc434f63b6","impliedFormat":99},{"version":"2faebfa830ae4cfbfb58e48b0ec20a2a63882d776f0ca36ec7155d45cf1b7f2d","impliedFormat":99},{"version":"b478fad6cb2c66bfbfc027983240b416a7733013f878056ba92cf809020018a0","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"9443967db823b66d1682be7fc66392be7c7924e10c3e54900f456341e94591a6","impliedFormat":99},{"version":"424f71d1fae96ac2e878af92345bb87bea1d29f757228fbc190133b305643f2c","impliedFormat":99},{"version":"61bb64660ee150f3ab618340e15cca0a81664801bede7c966ca0eca3a952fe63","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"61fd6c17235d530c40f543dd7c40afab091d91c1ef890baeed30db6d82b04b28","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"091767bc841f937654ed597d49e023ed59850355e746ae1a6f20ab31076ee1fb","impliedFormat":99},{"version":"19c6d6135af59693698d384050b45a8a049493500add442f58e4bd7c8a255ab6","impliedFormat":99},{"version":"6a0dba12d55314638a8c51108b20fe2f68f1364a619d098918bda91c22dec154","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"ed9bb55ddcbebd5cb3eee991f57ff21438546ee40ee1c310281bd12a6c7cf65b","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"437b7613a30a2fcde463f7b707c6d5567a8823fbc51de50b8641bf5b1d126fad","impliedFormat":99},{"version":"63ea959e28c110923f495576e614fb8b36c09b6828b467b2c7cd7f03b03ccf9f","impliedFormat":99},{"version":"1601a95dbb33059fc3d12638ed2a9aecff899e339c5c0f3a0b28768866d385b4","impliedFormat":99},{"version":"56fc978580577d30f4c2cdb5b1eb9217b66ed66537dd27141256f426e4b8dd68","impliedFormat":99},{"version":"2c5413050a2580becf9d82dd7e3006b95623e96f145356bf73230cd635352f70","impliedFormat":99},{"version":"860bedc71ead192ea4a0ea5ef4686e65724d14b391ebd1a6671a7044e6bd8e15","impliedFormat":99},{"version":"7c0a845bee4a084cbb8654709f48e5f13e2f6d45e5e2dde7c57cadf79fd9e3d5","impliedFormat":99},{"version":"07ad8a597ac75084e3dd9f9fadf5e8d7ccdcfe2f0c94ea0cf1cd8aa027a6c46e","impliedFormat":99},{"version":"94ddb4a2bb0c69e8efea22c58c2b6f84017eba469a4e433f5396ea8619d051cb","impliedFormat":99},{"version":"064499a671b662b25675beccdd04fb0bdebb6bd49bdb90d448e4b1ce3db20526","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"ae7d986f19db00cd62ce8573307f910ec2103d7fc30df09cedeec3cabec13082","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"ae77d81a5541a8abb938a0efedf9ac4bea36fb3a24cc28cfa11c598863aba571","impliedFormat":1},{"version":"f329dfad7970297cbf07ddc8fce2ad4a24e2a3855917c661922ef86eb24dd1f1","impliedFormat":1},{"version":"841784cfa9046a2b3e453d638ea5c3e53680eb8225a45db1c13813f6ea4095e5","affectsGlobalScope":true,"impliedFormat":1},{"version":"646ef1cff0ec3cf8e96adb1848357788f244b217345944c2be2942a62764b771","impliedFormat":1},{"version":"3cfb7c0c642b19fb75132154040bb7cd840f0002f9955b14154e69611b9b3f81","impliedFormat":1},{"version":"8387ec1601cf6b8948672537cf8d430431ba0d87b1f9537b4597c1ab8d3ade5b","impliedFormat":1},{"version":"d16f1c460b1ca9158e030fdf3641e1de11135e0c7169d3e8cf17cc4cc35d5e64","impliedFormat":1},{"version":"a934063af84f8117b8ce51851c1af2b76efe960aa4c7b48d0343a1b15c01aedf","impliedFormat":1},{"version":"e3c5ad476eb2fca8505aee5bdfdf9bf11760df5d0f9545db23f12a5c4d72a718","impliedFormat":1},{"version":"462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","impliedFormat":1},{"version":"5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","impliedFormat":1},{"version":"d0570ce419fb38287e7b39c910b468becb5b2278cf33b1000a3d3e82a46ecae2","impliedFormat":1},{"version":"3aca7f4260dad9dcc0a0333654cb3cde6664d34a553ec06c953bce11151764d7","impliedFormat":1},{"version":"a0a6f0095f25f08a7129bc4d7cb8438039ec422dc341218d274e1e5131115988","impliedFormat":1},{"version":"b58f396fe4cfe5a0e4d594996bc8c1bfe25496fbc66cf169d41ac3c139418c77","impliedFormat":1},{"version":"45785e608b3d380c79e21957a6d1467e1206ac0281644e43e8ed6498808ace72","impliedFormat":1},{"version":"bece27602416508ba946868ad34d09997911016dbd6893fb884633017f74e2c5","impliedFormat":1},{"version":"2a90177ebaef25de89351de964c2c601ab54d6e3a157cba60d9cd3eaf5a5ee1a","impliedFormat":1},{"version":"82200e963d3c767976a5a9f41ecf8c65eca14a6b33dcbe00214fcbe959698c46","impliedFormat":1},{"version":"b4966c503c08bbd9e834037a8ab60e5f53c5fd1092e8873c4a1c344806acdab2","impliedFormat":1},{"version":"3d3208d0f061e4836dd5f144425781c172987c430f7eaee483fadaa3c5780f9f","impliedFormat":1},{"version":"34a8a5b4c21e7a6d07d3b6bce72371da300ec1aed58961067e13f1f4dc849712","impliedFormat":1},{"version":"4ffba3c5848b4fe62ee59b754fd5f256ad9656a0db6d37b9a2a8cb40dfc7ac21","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"59e5e964b84fdb2378e9455e4e59405030e4ed2b4c6f891ce395f17796af3cbb","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"90ba95a763101bb61b8a799731a2ed60b5016b8135c1a2d5186862d4b534d4a1","impliedFormat":99},{"version":"ad763fa0c24ede2b818eb6598c12dd581451f94688fb9ed963beba20d513a7ec","signature":"90ec9100c29e008c3d9194acd818e2cfa6dc6e177154bc8e10c5959aa35619ed"},{"version":"b7ca2f47522d4ea41e65ff92c4c6dd9c4c8260da7c456a7631a9c88dc056b4d0","impliedFormat":1},{"version":"4f01e4d0959f9125b89e5737eb1ca2bfa69fd6b7d6126eba22feb8b505b00cde","impliedFormat":1},{"version":"4363a1adb9c77f2ed1ca383a41fbab1afadd35d485c018b2f84e834edde6a2c7","impliedFormat":1},{"version":"1d6458533adb99938d041a93e73c51d6c00e65f84724e9585e3cc8940b25523f","impliedFormat":1},{"version":"b0878fbd194bdc4d49fc9c42bfeeb25650842fe1412c88e283dc80854b019768","impliedFormat":1},{"version":"a892ea0b88d9d19281e99d61baba3155200acced679b8af290f86f695b589b16","impliedFormat":1},{"version":"03b42e83b3bcdf5973d28641d72b81979e3ce200318e4b46feb8347a1828cd5d","impliedFormat":1},{"version":"8a3d57426cd8fb0d59f6ca86f62e05dde8bfd769de3ba45a1a4b2265d84bac5a","impliedFormat":1},{"version":"afc6e1f323b476fdf274e61dab70f26550a1be2353e061ab34e6eed180d349b6","impliedFormat":1},{"version":"7c14483430d839976481fe42e26207f5092f797e1a4190823086f02cd09c113c","impliedFormat":1},{"version":"828a3bea78921789cbd015e968b5b09b671f19b1c14c4bbf3490b58fbf7d6841","impliedFormat":1},{"version":"69759c42e48938a714ee2f002fe5679a7ab56f0b5f29d571e4c31a5398d038fe","impliedFormat":1},{"version":"6e5e666fa6adeb60774b576084eeff65181a40443166f0a46ae9ba0829300fcb","impliedFormat":1},{"version":"1a4d43bdc0f2e240395fd204e597349411c1141dd08f5114c37d6268c3c9d577","impliedFormat":1},{"version":"874e58f8d945c7ac25599128a40ec9615aa67546e91ca12cbf12f97f6baf54ff","impliedFormat":1},{"version":"da2627da8d01662eb137ccd84af7ffa8c94cf2b2547d4970f17802324e54defc","impliedFormat":1},{"version":"07af06b740c01ed0473ebdd3f2911c8e4f5ebf4094291d31db7c1ab24ff559aa","impliedFormat":1},{"version":"ba1450574b1962fcf595fc53362b4d684c76603da5f45b44bc4c7eeed5de045b","impliedFormat":1},{"version":"b7903668ee9558d758c64c15d66a89ed328fee5ac629b2077415f0b6ca2f41bc","impliedFormat":1},{"version":"c7628425ee3076c4530b4074f7d48f012577a59f5ddade39cea236d6405c36ba","impliedFormat":1},{"version":"28c8aff998cc623ab0864a26e2eb1a31da8eb04e59f31fa80f02ec78eb225bcd","impliedFormat":1},{"version":"78d542989bdf7b6ba5410d5a884c0ab5ec54aa9ce46916d34267f885fcf65270","impliedFormat":1},{"version":"4d95060af2775a3a86db5ab47ca7a0ed146d1f6f13e71d96f7ac3b321718a832","impliedFormat":1},{"version":"6708cd298541a89c2abf66cceffc6c661f8ee31c013f98ddb58d2ec4407d0876","impliedFormat":1},{"version":"2e90928c29c445563409d89a834662c2ba6a660204fb3d4dc181914e77f8e29d","impliedFormat":1},{"version":"84be1b8b8011c2aab613901b83309d017d57f6e1c2450dfda11f7b107953286a","impliedFormat":1},{"version":"d7af890ef486b4734d206a66b215ebc09f6743b7fb2f3c79f2fb8716d1912d27","impliedFormat":1},{"version":"7e82c1d070c866eaf448ac7f820403d4e1b86112de582901178906317efc35ad","impliedFormat":1},{"version":"c5c4f547338457f4e8e2bec09f661af14ee6e157c7dc711ccca321ab476dbc6d","impliedFormat":1},{"version":"223e233cb645b44fa058320425293e68c5c00744920fc31f55f7df37b32f11ad","impliedFormat":1},{"version":"1394fe4da1ab8ab3ea2f2b0fcbfd7ccbb8f65f5581f98d10b037c91194141b03","impliedFormat":1},{"version":"086d9e59a579981bdf4f3bfa6e8e893570e5005f7219292bf7d90c153066cdfc","impliedFormat":1},{"version":"1ea59d0d71022de8ea1c98a3f88d452ad5701c7f85e74ddaa0b3b9a34ed0e81c","impliedFormat":1},{"version":"cd66a32437a555f7eb63490509a038d1122467f77fe7a114986186d156363215","impliedFormat":1},{"version":"f53d243499acfacc46e882bbf0bf1ae93ecea350e6c22066a062520b94055e47","impliedFormat":1},{"version":"65522e30a02d2720811b11b658c976bff99b553436d99bafd80944acba5b33b4","impliedFormat":1},{"version":"76b3244ec0b2f5b09b4ebf0c7419260813820f128d2b592b07ea59622038e45c","impliedFormat":1},{"version":"66eb7e876b49beff61e33f746f87b6e586382b49f3de21d54d41313aadb27ee6","impliedFormat":1},{"version":"69e8dc4b276b4d431f5517cd6507f209669691c9fb2f97933e7dbd5619fd07b7","impliedFormat":1},{"version":"361a647c06cec2e7437fa5d7cdf07a0dcce3247d93fbf3b6de1dc75139ff5700","impliedFormat":1},{"version":"fe5726291be816d0c89213057cd0c411bb9e39e315ed7e1987adc873f0e26856","impliedFormat":1},{"version":"1b76990de23762eb038e8d80b3f9c810974a7ed2335caa97262c5b752760f11a","impliedFormat":1},{"version":"5e050e05fe99cd06f2d4ad70e73aa4a72961d0df99525e9cad4a78fa588f387b","impliedFormat":1},{"version":"4ff327e8b16da9d54347b548f85675e35a1dc1076f2c22b2858e276771010dd2","impliedFormat":1},{"version":"f767787945b5c51c0c488f50b3b3aeb2804dfd2ddafcb61125d8d8857c339f5a","impliedFormat":1},{"version":"14ab21a9aeff5710d1d1262459a6d49fb42bed835aa0f4cfc36b75aa36faddcd","impliedFormat":1},{"version":"ba3c4682491b477c63716864a035b2cfdd727e64ec3a61f2ca0c9af3c0116cfd","affectsGlobalScope":true,"impliedFormat":1},{"version":"b222d32836d745e1e021bb10f6a0f4a562dd42206203060a8539a6b9f16523f0","impliedFormat":1},{"version":"a3f6d8995864820a0207b7ef4ce1ed6a8dd2fccc7e70d015da15034807c38e1c","impliedFormat":1},{"version":"651df11341eff0b769fb83af75b1872e6cedf406674c5eaa2650551aceb5a816","impliedFormat":1},{"version":"774a466295d26eddab911b9f567040364e7b7d0eb8003ad3bfc92b97eeecf066","signature":"f15b4a91c10bf30ff3708a5e3968c0a52ca4e86a4b9a5b1c4fc8e9b5f1292f21"},{"version":"1e9d4c3b066e0228765cef074a9bf49d0b8d3af461c97f2c511e8f7110b56235","signature":"e59faabf094dd75dcf08847ca1b8ae16daf269ca02c744c521b38ed5d297578c"},{"version":"48d3d3a869cf85c67c62d87c031946cb9ae89fc59d3d0b274d17c88097e9847d","signature":"16e6aa6706cf2bfdef5a587057b3b672099a2dd478584f26aacc4dd07336d0a0"},{"version":"a6a2173b6c4ebb031d158b06004ef4db7678ea8df66be27b317973460591c433","signature":"7bf23ce970d42624e8d08ff91d0a8dbe0063953e9038c2c75e827a0770da33cf"},{"version":"9dfe98d745dfc706198852e032c3c936092a4484951872c0d18639cf2ab698cf","signature":"326009db2b0a0f3290cbde2271f91e51869148a3e286ccc3ac41559f6c830642"},{"version":"9f01d110e167ddcfaa01b236040a12699b6ed7a2070d7c362f4403f092fed008","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"030523c514236148e0f25975ef74b2490c391cb9596cd668728f7db5b0702e08","signature":"411166ca21d7ae80b737bb1c7e9900e897ac7b27c06647063730ba8207522e46"},{"version":"ca52a606c31ddaae5f7c4055fccb3563cf7e85601c96dbf28b7d0795d2d21af0","signature":"82d82e5e9d2c282a1dddebbcc73d9ff5c89f60859cd8769d0f6eae7b76f3f4d1"},{"version":"41d8a2df75ff7b6ee4c82ef8ce52032aa272358a5f9187a6b5986849200ed411","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f25d11b3d781ff8db8614eeee12b43f81ad05389fb25acf10b8c3473b0e2a1d0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"78b3214dba4e86c41e4cdf5de5dcc853806ff74fad5df3dc1087cdbe19ad00e0","signature":"5c4de1b33c2b0c5b0b823fca5ba9e5dde3c995aa8a2c42db12b2e8b64f408805"},{"version":"9980dd11b1e848c16ad84eae18f4e7bfa3331f81c2617533b0e3bba61cb9ab3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45b82fd88270dd3d0aff39220b341eca789f77c82813dc6695b2058479d8dc28","signature":"3d67e5bdef7b1039301b51fc5ba5303c133b863f30e0de39c8aa1db5d897f1b6"},{"version":"915d1bc5f4c3b9cc8e25964dcd29b3c00ffb36ab2c8b12b2472533b3113485a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f212078a0ecefabbbbc627b89e28c871b559c1c57a4dcae32c8d7c2b6c4f0a00","signature":"b495b6770d46b4ebe3dbe0c1f6d64e8b50ea14ea8d2b63377813ae35672a4535"},{"version":"d237e0ff6755c658dc810e02b3a290f593f0ee2708eccbf1e2cd65c512c8de4a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cadbf01db107d2e9cceb5ecdd3f8f0084d996f07f2fca714409247deb26f0b85","signature":"ed905f29ff05cd1d3cf260949314ca896356f5c8bc011fdee9d75cde0404c6a0"},{"version":"a0563dec1ee31e5e8150681234665a122d20bb748d51a4b841ba1bcee31697a7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ea7a4796f350344bbf39b19518d78169bd66a819f787b0a4d1a6ad642dbce7a2","signature":"8f6ef9414b1d23dc0a927160ed27770f93c1fe749a9af0e626f5a0e8918d5fc1"},{"version":"428631c15a500daa23f33c9e1ca427423fe56515e811133ff5e80891238fb242","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc5bfa36a8f86eea23a84b78f27ba0f842426cefb5a13d4b53b80a331aaaa268","signature":"3dcde6c75b6c7fc3bc7fca2ca1273467974a8b54061edd648ed854bc4c5d842a"},{"version":"fcc8fb0967eda9256921f3cfc521d49f3d473232d4c754c09de1d50faa996e35","signature":"37e053a2b54ed97cab1c0e0e6a0ed4d610d6011b24e4cef7633007fb573468e1"},{"version":"463efb47c0879fb7e518ca8d209ffc49e2d682d0c47eaf30d074a1032290ac50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a794572bfb0885ec1de20b701e498a415c0480e4fb6c20dbaa125b374ae691cb","signature":"46d3c81ba82aaaeabab3baa813cbb84b4c9808ec7ad495ad5fdc27d9358aa314"},{"version":"dd26e8bac53b883eef657e5c7961170c27944f68825e82a43625c5ccc51c2937","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"016e4353e0414c98c2ed2eb75df1509a38b457ddf000e9ff98125f82ea42c335","signature":"767c0245753b6e027a6f0550eb7b69bbd09d3f758aa1e9a07dcfe0992c0f629e"},{"version":"3ef6579bdd5883302539b5370c5670331f3584dff9e2ef14bab39991f00a29cb","signature":"1e894cd9840e407527a3f67119371f90d7ae5f8179905de7776a345ed4af6d32"},{"version":"e34737b913a2f8775f45e495e660016e0ab0fda68c579f2024d1b596dde2d81c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6677681f753ead74430e87a3398cc92c183570992c77804fc4215178ffc86116","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b642738f6cd019494d207949a63b67b6ad9013bb3f1fb5f8fa36687879db5c4","signature":"f583f64899de9c7804febba31f2808aad09101b0d3342e42793c8588ed608f63"},{"version":"35a644763f50424937b17109eb552ddc767270a5526a0a827810ab86585dc53a","signature":"1b4159a10366adf4fd777a6bd595b4b846f1d3d37d07498c3498fd4b71e0f813"},{"version":"12fc2285fa58f8178c3fce7cf56795d11bdbc3974e264f31ff3cdcb689fd5813","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c81045d6db59c1c83c84797bc1f78422a691ced9ca9a3554e2f8ef9aacc7dcfa","signature":"441f2bb4f78eb9fb86d27aceee4d92908b532110aa6b7fac80adede58bab926d"},{"version":"56d498db61c90d4706b23d1b2235e8e8fb4c525a16c19c33cabe50812506a134","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bce05280900b4eb3333d10064531569c9fcfce6aa7aeca1ca03b2120539892a5","signature":"c96264be178c5e42597043c462b21cbc073618f43d5e1e88bd32a516068a2380"},{"version":"26f7e680445f38787829c77c194f3df7741657f8e80e7b51588dfe74da7b2c7f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f5a22523635ca6f47c20b386b010ca1258aa19af5f4299f8752809c599315bcf","signature":"48c3a71b54800c134fcff4becfedca8347ff86645e004f8754d1e2b1385d1e9c"},{"version":"487012655811883dddb922cd44d08642d753adc1df21ba652e11ac7030aeede2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f69209ce0e934946c859c4cc6248ef4a2dc528f5baf9b4fcdea5cf3e08d9d38","signature":"e8f8aa08e63443a0cc63ce2f9fc9582addd622528fad2ecfc413b2a91c688fac"},{"version":"041b81f9c2cc3f95588f5ef2da13fce1d895ddd5160979c84c01aaa8873145b4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad15d915012090304670ff62dd5e48d7694c11787f4884f51cf80f873dd40aa4","signature":"ef3c092bb7ed970d2273e55a61b12bc4741bca2219e1fe703350550c99ca6f42"},{"version":"cad40fd88fb3c219fc234d0d56bf87e8d3ceb86505f11e3714fc21c6d761cd59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a51d9c3485a1f3fc48f7f04f771cd827828082e49c12754d7e5708719675449e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fb5ce2f104fde415289b4febf27da970e40afefe42b87b15db28818ca9f94132","signature":"ccefe690307d556c5e44dd7ffb7d23faa0a3388926925ed0352107cd2efc4d9c"},{"version":"6e568ff38a9d7070783b26b39b93df658c7466032f4ffa22431dfe2808b7a8bd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3613723853e81414c2e1220e3e0a696a997cc49e4390060b6ac7fd91aec150e4","signature":"0334a8b41901a52fc9195c66c97cee6aaaecc5aae79b9fc31f245a5df48b3ecc"},{"version":"e4bba7f9edd598a76e24e59f3d47d09cb0295d6c52c8a89e8fa04dc6e6428b16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"082900437524e8bc6903f0d26479f590af1814cc5080fc502e07d38c951500be","signature":"24564adc2074d75d218bfe8b711580df8848afbd3ce2890184cd98e76529d387"},{"version":"34eb44fd7813a487540b6afa04da3175388b1841aa3612569e8aa07652363b69","signature":"9bada675e959a3571ad60f30eaee87c92b05fea94beca1f7fd212dc65734177a"},{"version":"a76367ab8ebbbfb0db994a3d394b71b72c3b72fb69542222ace317e4b61f74ca","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cbeab042293806df5cb4e45ba714036477971cb40a2539b20c331a632c1a2c46","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f55b5907842f16784dca83f2c82ac05e1e8740f2ec866c95289e05061384e4b","signature":"93bf045e7f996840ff1a3e1fa340836585224d394c52de4e98ff79f8be816dd1"},{"version":"7c8545a7c4ec6978ebb4af07475d76007eb084c4f7f45aeba7ee817eeb4316e1","signature":"37eeb4730a8634d70c51bd1933939e66cba31f30600246487f3568cee7742b9c"},{"version":"852c367110c2934ed35a33ad276111aa9b3016ec92b36b86a37b43642ad9458d","signature":"92a24950b269736d532ea9daf0cdd8ea7361b70095f3468e59afea80af884516"},{"version":"5a2cdf6adeec348bbc876221be4367e8adff0bb78a5680ebd7d71e5c3bad6cc0","impliedFormat":99},{"version":"e004826eac62081f867c66dabd92d3ef7d126d93a70430a2c88429228c3ecc50","impliedFormat":99},{"version":"38d6857b58d2ac42442e396311c542062d4f0dad40f2adb496dd5fd0756ee400","impliedFormat":99},{"version":"34b7d1e2d15845cf08bcf5e3c01adbb92cea1ec27564ee249ba486cdfb28526c","impliedFormat":99},{"version":"0d0861810ff9d344ab37f055edbc4d14e68e2fa18e113ce8cc33aef9bb500b5f","signature":"2791178671f71d9b41ed9ef814e549d85cc6a77c61e2f8a87ec25f2176d9a3fc"},{"version":"992404964e9cefb3143cde9bc8e5eb5e7010c3a207f5bfa52df49287fce758be","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"968043e1fd7bb3c6b1c22d3341ede621d16a628b2bc4c35d1e43f0064ff7c1a6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6d261c7be483027c9d281ba71314350ad8ce66efabf46f7b6099f39fe3b8d218","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c02d45c2a6bc50186c1972f44c31ff22e887b30205ce84607bb4661c0fdc9846","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fc06e1e53688bb32ccd86e730e08bacd38d9ad7ad006a234211b230825efb2d","signature":"d95aac1823e54b4183acab8f7fe3bec5dd7bd4aa297f56004fbcb972d299e377"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"3255b97f3f24af29c79cc1aa88004efb13b6285ebdde0a567bf32e19bb65250d","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"cc0e0705b17f5987925bf05b5a7da622a76ad691274a428cf18fb28b33a7a1cf","signature":"01e6799210215286acf67be8c15da37b72af300e0f7f32c7f11535415e25ee88"},{"version":"ab640e52df6129fa178d5c0f2860542954ea38af4b0801a92c3ac09f6a9eec7c","signature":"91212f9905f489a1993df856acac1939544f6166e4cafff1c4f0949e37a8a11d"},{"version":"c3d577953f04c0188d8b9c63b2748b814efda6440336fa49557f0079f5cf748a","impliedFormat":1},{"version":"787fe950e18951b7970ec98cb05b3d0b11fcdfeb2091a7ea481ac9e52bf6c086","impliedFormat":1},{"version":"13ceda04874f09091da1994ba5f58bf1e9439af93336616257691863560b3f13","impliedFormat":1},{"version":"488c53c963104e91a6f2a1f16cbaee1a963f6f4527f0051256740c94ed34d6bb","signature":"fb69d502157f1cf71cb8c737f6909c2e82f2a53b8157f840411444435f5da3d1"},{"version":"7075686875dce9990810c2dfeefc1d3e1dd29cd815389854746fcc457dfbdce7","signature":"c2f4c6ab17d07762713d80c4c29cba3cfffd690fe6c569a17c0be5d0d3e810f5"},{"version":"a941595362ff7e12adee1605aea8495d9bc96cd833d95c87f83cad2b5838165a","signature":"b67fa3b5b051ead6f5048d73c953d289234953f832922ffc4dfe293d5c6bfc98"},{"version":"c98b1727a4c0ccfbd4df609bad278f1af184a069d232f978a327d53110677480","signature":"1556f3a35ddd259c925802c27bac4fe626e489e685fc3ee1f3101169f02f993d"},{"version":"1114a96ff6bdee7270e584688b4c46a5be6e50c47e6d8d26e4a8649556a851c9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"76cc225f61f545122672c27ff69aa27d1e7578d653c5fe942ebe88601cea0b02","signature":"87d223b2d0fc4ffc6f3bd5bbf3d4e036171c472cffb6a792c31427b714f4f442"},{"version":"06272d55719e7d65de722274ae4593bfe06a90f4924b8807e4e04cbd15fb43c1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"46b2d594365b3ffa714de5625c0859471e14d1b010e24cce18be153d1f6efa8d","signature":"3ebf64d5f0b695aef10ddeeff762fc0216e05bcd3d9572fb8763859b2d74be41"},{"version":"f3415880499901a01feef00e1b3042f670dabad8b5a131c22994f5f951dbdf2f","signature":"28007b7d2b577a868c587c22500f2ab77490b6390909ffbdd3b04dac98e69a18"},{"version":"da5c43ad335eaac2e7a2517ff7584a1b919ec4d8d7f8bea932e9928a27f95d07","signature":"711d67575686fe3e0ac16b0a6080ab554fa53447a33228be88bfd57323da61bb"},{"version":"58c88a6bf756bcb45f70e3c79ac4e08209093ad4f112df86d57acc500290e067","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc51205c27f22f7194f3c026cdf19c5b27f19190a97955b9ffce0db45858e42f","signature":"ba90586e9f08bbe0d660358dcee98b83dadce2bfa013ca3e8d93f8d7924a9c66"},{"version":"54c008f175512ea8e8854d138dcf76b2af5e59e6816e82e87a360d76f3c7f820","signature":"34c21c211ba158af8c7cbdf93784d24a472017b37b792b1c2d8ae21c36488729"},{"version":"1eb5c0da9dcb448145de4b74e1b37b6a05da0e4ae0b393c7224af6ee7cccf913","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8bc24afa3c5fb73fd0dc89f091d2cb65b9d54f7a90b37302e9bd679ba504b0e3","signature":"e5ff90224997311a3ef066d1d0e3a85f3e2b2348a035581ff7099304497d0775"},{"version":"51610870e75caaf1ff890f1fb949366cd7d843b4aa2e734c166bb307a78f33ea","signature":"2ed4659f7cb57cc7471545251d21c6f8ab503526ec15c1a991a3d5be96258c79"},{"version":"de3c85bccb34f80e5cfd3f5e63648f2f8bdc8c9d19a67b0807adb4cfd8793afc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c064058bfb6150ce094497c75bc491e7f92389c2b1fed5f6923e7a035315d3a1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3936ac555912c80003fd9e659007f016ae2bc61fb8b91f696f860d19fd6e9bc","signature":"dc035ffa274dd975386f883b4d99361db8e73edd0fc77b1e4bc0d09be0c5074d"},{"version":"f20b8d5c86e426ce2d1505f4a3114e66411272aa8394e0ad323c0c3b1d1fadff","signature":"b41f35e5ae414583d72cc2aa3a17cc23d40f4b9e221f92acd74ed09bb3daaa63"},{"version":"74b006e51c1fe0198db4fd239bdfb2063fb3b0139bbb3dbaab79f323f42ba6bf","signature":"83057fe16cf05e5bf626fd4e46379506199130438abbd2ae42de6234bb202181"},{"version":"c247b5bb6d297cb7c9607645b1486b2f7b3ee0c05fac97d18e6daf4e3b25ebeb","signature":"5a224c6b95c526ad3f70b7b5894a2fe9b50cb5a9eed53b87806cf1045f8d7bab"},{"version":"dd7a9804bfd52806479d69214126956478367bb3a2c333b589ddcc253aae03d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"882b28abe64dae4932c83ebb71e4155da340929fe08a2055f3e573ef17f70fc3","impliedFormat":1},{"version":"4a3e425808751200a7709671667ad3d7e7cbfd0a06d469cab42adf06c2601f4a","impliedFormat":1},{"version":"401da46338f5b4f97c2a5f8a0faaace045c51aabd751d2dc704159f64feafe89","impliedFormat":1},{"version":"c705d4594093bcde53fc292c5526aedd3145170ceba73a9476ee97de6a915fe2","impliedFormat":1},{"version":"65399deec596f31712911c2d81964d913370e0d4a04c51df29cc3c99c9ac298b","signature":"c8b6a1356346524d07db6d395ed25c816fb0935840b3f6af9296402392feca76"},{"version":"b44403d97ecd48d2f5ec3f3175a9a9dce873ec5d3797459ed057e7a1ad597d54","signature":"1fcf7139261418de9dce0edd9f8e95a8ace6fd591da1c95fb959e19e7c6f4281"},{"version":"01922ef0992b637b2a096856708c280e6e2b5085d3ff743c27e891e0d3d28ea7","signature":"450f56af343ef42c693dde50c0dbe427f37297afb67c4864f81dd7c69fdfbd8b"},{"version":"02ed4b9c64b599d8a0d9c242c9f7e43fa44ebc4cdda1b8143a29d2bfdcaebb44","signature":"5bfa909232756aeaa1797184b579b7b47f5f6917a0fdf3b2566fe4bc4afc72a8"},{"version":"f4adb32677aa22d47ed1048448f8974667250c8deb8135f321cad0cb4d0d4007","signature":"be914a2abd74279a5ff3c561f641f30569d1a2f7618fcd806cfdb8c1fad34326"},{"version":"15a1cea3d3fd19c8818aaac408d84096485d3f154eac44e129c2a2a2609d85ba","signature":"88a64ff66b36ff55ce621b22e512515acd895e815a065f2897813b5f194521d8"},{"version":"a0e1a608868e8805852e4e9274fc1e3e22573273a292b4e59f19892c495fa239","signature":"0f601b1cf9ca46ef05b387bc05b169852e0a2e3e30babe89a59d21af43187522"},{"version":"4d1b4f2a7c556f22c71dfce1be2455614fb9f838d4b9144a447a03bd514bdd5e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20375a205b37d0f527f1f3fb6cc5d6c2076c1b57f74b9024f8153e0f3f0289a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3557b3416d97219e58a39fecce338b086bd42db6ce7ef701e8265783fbd20c6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"83f21e09f1a6d980cc7cf83252deccd5d997e67266ae8bd450ef8899fcab1884","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"38757383a22721ebcf7a7430d10cd39967c0f896d758798906d29c8ab8722924","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4722121739886d9694fe6d74b91f654b2b26459edd6e275fb5ff1a509bbc262","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1c46f9542d2d4a44df84e20e37348c86abfb57804268f5236874fa8a8b7639f5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6915db003807400f9c80754166096b1ca5552111f80eb58c311ab561cca84735","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad42288f8c9ebfd4451e4256a2f091cfc26b958d29db3612c19efbbb476882a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7",{"version":"e37704e8bdf72de83d6ca4620f748ebf6272afc9b748f4e541afffd32b0c2924","affectsGlobalScope":true},{"version":"4332f611f915908b335662f95ac6047288d3bc0b939e06d59e369b96a28eaa70","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7",{"version":"ec9db470620906cec5c2b53d821e2917355bfed3fd87cea28eafd5d6d7496459","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d7cbeea0454e05a3cdf3370c5df267072c4f1dc6c48a45a9ad750d7890443d7","affectsGlobalScope":true,"impliedFormat":99}],"root":[[559,561],661,695,[746,799],[804,809],813,814,[818,840],[845,865]],"options":{"allowJs":false,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[864,1],[559,2],[865,3],[861,4],[862,2],[863,5],[560,6],[561,7],[403,2],[566,2],[619,2],[800,2],[801,8],[802,9],[803,10],[679,2],[676,2],[675,2],[670,11],[681,12],[666,13],[677,14],[669,15],[668,16],[678,2],[673,17],[680,2],[674,18],[667,2],[665,19],[664,20],[663,13],[683,21],[662,2],[626,22],[624,2],[161,23],[162,23],[163,24],[101,25],[164,26],[165,27],[166,28],[99,2],[167,29],[168,30],[169,31],[170,32],[171,33],[172,34],[173,34],[174,35],[175,36],[176,37],[177,38],[102,2],[100,2],[178,39],[179,40],[180,41],[220,42],[181,43],[182,44],[183,43],[184,45],[185,46],[186,47],[187,48],[188,48],[189,48],[190,49],[191,50],[192,51],[193,52],[194,53],[195,54],[196,54],[197,55],[198,2],[199,2],[200,56],[201,57],[202,56],[203,58],[204,59],[205,60],[206,61],[207,62],[208,63],[209,64],[210,65],[211,66],[212,67],[213,68],[214,69],[215,70],[216,71],[217,72],[103,43],[104,2],[105,73],[106,74],[107,2],[108,75],[109,2],[152,76],[153,77],[154,78],[155,78],[156,79],[157,2],[158,26],[159,80],[160,77],[218,81],[219,82],[224,83],[488,84],[225,85],[223,86],[490,87],[489,88],[682,84],[221,89],[486,2],[222,90],[90,2],[92,91],[485,84],[255,84],[615,92],[614,2],[627,93],[648,94],[649,95],[647,2],[620,2],[633,96],[632,97],[644,96],[635,98],[637,99],[656,99],[636,100],[617,101],[616,2],[622,102],[623,103],[653,104],[629,105],[631,106],[652,2],[650,105],[630,2],[621,103],[628,2],[625,2],[91,2],[691,107],[693,108],[692,109],[690,110],[689,2],[842,111],[841,2],[843,112],[729,113],[698,114],[708,114],[699,114],[709,114],[700,114],[701,114],[716,114],[715,114],[717,114],[718,114],[710,114],[702,114],[711,114],[703,114],[712,114],[704,114],[706,114],[714,115],[707,114],[713,115],[719,115],[705,114],[720,114],[725,114],[726,114],[721,114],[697,2],[727,2],[723,114],[722,114],[724,114],[728,114],[606,2],[608,116],[607,2],[696,117],[815,118],[735,119],[734,120],[741,121],[743,122],[739,123],[738,124],[742,120],[736,125],[733,126],[744,127],[745,127],[737,128],[731,2],[732,129],[817,130],[816,131],[740,2],[511,132],[516,133],[523,134],[506,135],[259,2],[267,136],[407,137],[410,138],[382,2],[395,139],[402,140],[284,2],[384,2],[265,2],[381,141],[427,142],[266,2],[257,143],[409,144],[411,145],[412,146],[483,147],[376,148],[329,149],[389,150],[390,151],[388,152],[387,2],[383,153],[408,154],[268,155],[453,2],[454,156],[295,157],[269,158],[296,157],[332,157],[235,157],[405,159],[404,2],[394,160],[501,2],[244,2],[522,161],[461,162],[462,163],[458,164],[540,2],[359,2],[463,165],[459,166],[545,167],[544,168],[539,2],[310,2],[362,169],[361,2],[538,170],[460,84],[315,171],[322,172],[324,173],[314,2],[319,174],[321,175],[323,176],[318,177],[316,2],[320,178],[541,2],[537,2],[543,179],[542,2],[313,180],[532,181],[535,182],[303,183],[302,184],[301,185],[548,84],[300,186],[289,2],[550,2],[811,187],[810,2],[551,84],[552,188],[227,2],[391,189],[392,190],[393,191],[231,2],[396,2],[251,192],[226,2],[475,84],[233,193],[474,194],[473,195],[464,2],[465,2],[472,2],[467,2],[470,196],[466,2],[468,197],[471,198],[469,197],[264,2],[261,2],[262,157],[416,2],[421,199],[422,200],[420,201],[418,202],[419,203],[414,2],[481,165],[256,165],[510,204],[517,205],[521,206],[350,207],[349,2],[344,2],[497,208],[505,209],[377,210],[378,211],[456,212],[366,2],[479,213],[354,84],[371,214],[482,215],[367,2],[370,216],[368,2],[480,217],[477,218],[476,2],[478,2],[374,2],[452,219],[239,220],[352,221],[356,222],[372,223],[375,224],[364,225],[357,226],[504,227],[430,228],[348,229],[236,230],[503,231],[232,232],[423,233],[415,2],[424,234],[441,235],[413,2],[440,236],[98,2],[435,237],[260,2],[455,238],[431,2],[245,2],[247,2],[386,2],[439,239],[263,2],[287,240],[373,241],[293,242],[353,2],[438,2],[417,2],[443,243],[444,244],[385,2],[446,245],[448,246],[447,247],[397,2],[437,230],[450,248],[347,249],[436,250],[442,251],[272,2],[276,2],[275,2],[274,2],[279,2],[273,2],[282,2],[281,2],[278,2],[277,2],[280,2],[283,252],[271,2],[339,253],[338,2],[343,254],[340,255],[342,256],[345,254],[341,255],[252,257],[331,258],[500,259],[498,2],[527,260],[529,261],[493,262],[528,263],[240,264],[237,264],[270,2],[254,265],[253,266],[249,267],[250,268],[258,269],[286,269],[297,269],[333,270],[298,270],[242,271],[241,2],[337,272],[336,273],[335,274],[334,275],[243,276],[484,277],[285,278],[492,279],[457,280],[487,281],[491,282],[380,283],[379,284],[360,285],[346,286],[328,287],[330,288],[327,289],[449,290],[351,2],[515,2],[248,291],[451,292],[499,293],[358,2],[288,294],[365,295],[363,296],[290,297],[425,298],[494,2],[291,299],[426,299],[513,2],[512,2],[514,2],[496,2],[495,2],[428,300],[355,2],[325,301],[246,302],[304,2],[230,303],[292,2],[519,84],[229,2],[531,304],[312,84],[525,165],[311,305],[508,306],[309,304],[234,2],[533,307],[307,84],[308,84],[299,2],[228,2],[306,308],[305,309],[294,310],[369,52],[429,52],[445,2],[433,311],[432,2],[317,180],[238,2],[326,84],[502,192],[509,312],[93,84],[96,313],[97,314],[94,84],[95,2],[406,74],[401,315],[400,2],[399,316],[398,2],[507,317],[518,318],[520,319],[524,320],[812,321],[526,322],[530,323],[558,324],[534,324],[557,325],[536,326],[546,327],[547,328],[549,329],[553,330],[556,192],[555,2],[554,331],[730,332],[602,333],[600,334],[601,335],[589,336],[590,334],[597,337],[588,338],[593,339],[603,2],[594,340],[599,341],[605,342],[604,343],[587,344],[595,345],[596,346],[591,347],[598,333],[592,348],[672,349],[671,2],[844,350],[611,351],[574,352],[575,353],[578,354],[567,355],[577,356],[573,357],[565,2],[579,358],[580,359],[568,2],[569,2],[571,360],[570,2],[572,361],[434,362],[586,2],[645,2],[618,2],[88,2],[89,2],[14,2],[15,2],[17,2],[16,2],[2,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[25,2],[3,2],[26,2],[27,2],[4,2],[28,2],[32,2],[29,2],[30,2],[31,2],[33,2],[34,2],[35,2],[5,2],[36,2],[37,2],[38,2],[39,2],[6,2],[43,2],[40,2],[41,2],[42,2],[44,2],[7,2],[45,2],[50,2],[51,2],[46,2],[47,2],[48,2],[49,2],[8,2],[55,2],[52,2],[53,2],[54,2],[56,2],[9,2],[57,2],[58,2],[59,2],[61,2],[60,2],[62,2],[63,2],[10,2],[64,2],[65,2],[66,2],[11,2],[67,2],[68,2],[69,2],[70,2],[71,2],[72,2],[12,2],[73,2],[74,2],[75,2],[76,2],[77,2],[1,2],[78,2],[79,2],[13,2],[80,2],[81,2],[82,2],[83,2],[84,2],[85,2],[86,2],[87,2],[128,363],[140,364],[125,365],[141,366],[150,367],[116,368],[117,369],[115,370],[149,331],[144,371],[148,372],[119,373],[137,374],[118,375],[147,376],[113,377],[114,371],[120,378],[121,2],[127,379],[124,378],[111,380],[151,381],[142,382],[131,383],[130,378],[132,384],[135,385],[129,386],[133,387],[145,331],[122,388],[123,389],[136,390],[112,366],[139,391],[138,378],[126,389],[134,392],[143,2],[110,2],[146,393],[563,394],[613,395],[582,396],[564,394],[562,2],[581,397],[612,2],[610,2],[583,2],[609,398],[576,399],[585,2],[584,400],[655,401],[660,402],[654,403],[646,404],[642,405],[638,406],[651,2],[639,98],[687,407],[684,408],[658,409],[657,410],[640,411],[686,412],[634,2],[641,413],[659,414],[694,415],[688,416],[866,417],[685,2],[643,2],[822,418],[824,419],[823,420],[825,421],[828,422],[827,423],[747,424],[751,425],[750,426],[754,427],[753,426],[755,428],[752,426],[757,429],[756,426],[759,430],[758,426],[761,431],[760,426],[763,432],[765,433],[764,426],[762,426],[768,434],[767,426],[773,435],[772,426],[774,436],[771,426],[770,437],[769,426],[776,426],[777,438],[775,426],[779,439],[778,426],[781,440],[780,426],[783,441],[782,426],[785,442],[784,426],[787,443],[786,426],[788,444],[766,426],[790,445],[789,426],[792,446],[791,447],[795,448],[794,426],[796,449],[793,426],[831,450],[830,451],[834,452],[833,453],[835,454],[832,453],[836,455],[814,456],[840,457],[839,458],[821,459],[849,460],[851,461],[850,462],[852,463],[853,464],[854,465],[855,466],[856,467],[819,468],[857,469],[858,470],[838,471],[837,472],[826,165],[859,473],[845,474],[846,475],[847,476],[848,477],[813,478],[829,479],[820,165],[818,480],[805,481],[806,482],[807,483],[860,484],[808,485],[749,486],[746,487],[798,488],[799,475],[748,489],[804,490],[797,475],[809,489],[661,491],[695,492]],"affectedFilesPendingEmit":[865,863,561,822,824,823,825,828,827,747,751,750,754,753,755,752,757,756,759,758,761,760,763,765,764,762,768,767,773,772,774,771,770,769,776,777,775,779,778,781,780,783,782,785,784,787,786,788,766,790,789,792,791,795,794,796,793,831,830,834,833,835,832,836,814,840,839,821,849,851,850,852,853,854,855,856,819,857,858,838,837,826,859,845,846,847,848,813,829,820,818,805,806,807,860,808,749,746,798,799,748,804,797,809,661,695],"version":"6.0.3"} \ No newline at end of file +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.es2025.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.es2025.collection.d.ts","./node_modules/typescript/lib/lib.es2025.float16.d.ts","./node_modules/typescript/lib/lib.es2025.intl.d.ts","./node_modules/typescript/lib/lib.es2025.iterator.d.ts","./node_modules/typescript/lib/lib.es2025.promise.d.ts","./node_modules/typescript/lib/lib.es2025.regexp.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.date.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.esnext.temporal.d.ts","./node_modules/typescript/lib/lib.esnext.typedarrays.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/entry-constants.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/lib/bundler.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/next/dist/server/app-render/vary-params.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/shared/lib/app-router-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/client/components/readonly-url-search-params.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/segment-cache/types.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/next/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/next/dist/client/components/segment-cache/cache.d.ts","./node_modules/next/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/next/dist/client/components/segment-cache/navigation.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/next/dist/server/load-default-error-components.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/app-render/create-error-handler.d.ts","./node_modules/next/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/staged-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/get-supported-browsers.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/define-env.d.ts","./node_modules/next/dist/build/swc/index.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/debug-channel.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/next/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/dist/server/web/exports/index.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/shared/lib/size-limit.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/build/adapter/build-complete.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/dist/client/components/catch-error.d.ts","./node_modules/next/dist/api/error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./.next/dev/types/routes.d.ts","./next-env.d.ts","./next.config.ts","./node_modules/vite/types/hmrPayload.d.ts","./node_modules/vite/dist/node/chunks/moduleRunnerTransport.d.ts","./node_modules/vite/types/customEvent.d.ts","./node_modules/rolldown/dist/shared/logging-C6h4g8dA.d.mts","./node_modules/@oxc-project/types/types.d.ts","./node_modules/rolldown/dist/shared/binding-zH1vcmbM.d.mts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/composable-filters.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/filter-vite-plugins.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/simple-filters.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/filter/index.d.ts","./node_modules/rolldown/node_modules/@rolldown/pluginutils/dist/index.d.ts","./node_modules/rolldown/dist/shared/define-config-5HJ1b9vG.d.mts","./node_modules/rolldown/dist/index.d.mts","./node_modules/rolldown/dist/parse-ast-index.d.mts","./node_modules/vite/types/internal/rollupTypeCompat.d.ts","./node_modules/rolldown/dist/shared/constructors-D0W3rNfA.d.mts","./node_modules/rolldown/dist/plugins-index.d.mts","./node_modules/rolldown/dist/shared/transform-DgZ3paSD.d.mts","./node_modules/rolldown/dist/utils-index.d.mts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/vite/types/internal/esbuildOptions.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/vite/types/internal/terserOptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/lightningcss/node/ast.d.ts","./node_modules/lightningcss/node/targets.d.ts","./node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssOptions.d.ts","./node_modules/vite/types/internal/cssPreprocessorOptions.d.ts","./node_modules/rolldown/dist/filter-index.d.mts","./node_modules/vite/types/importGlob.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/@vitejs/plugin-react/types/optionalTypes.d.ts","./node_modules/@vitejs/plugin-react/dist/index.d.ts","./node_modules/@vitest/spy/optional-types.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","./node_modules/tinyrainbow/dist/index.d.ts","./node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","./node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/utils/dist/display.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d.ts","./node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/@vitest/utils/dist/timers.d.ts","./node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-Bh0IjN67.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/vitest/dist/chunks/traces.d.D2T_R8rx.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","./node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/vitest/dist/chunks/config.d.A1h_Y6Jt.d.ts","./node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","./node_modules/vitest/dist/chunks/rpc.d.B_8sPU0w.d.ts","./node_modules/vitest/dist/chunks/worker.d.ZpHpO4yb.d.ts","./node_modules/vitest/dist/chunks/browser.d.BcoexmFG.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","./node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","./node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","./node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/vitest/dist/chunks/coverage.d.BZtK59WP.d.ts","./node_modules/@vitest/utils/dist/serialize.d.ts","./node_modules/@vitest/utils/dist/error.d.ts","./node_modules/vitest/dist/browser.d.ts","./node_modules/vitest/browser/context.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.CEnv6XRv.d.ts","./node_modules/vitest/dist/chunks/plugin.d.BM2TCi12.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/jest-dom/types/matchers.d.ts","./node_modules/@testing-library/jest-dom/types/jest.d.ts","./node_modules/@testing-library/jest-dom/types/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@types/react-dom/test-utils/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./node_modules/vitest/dist/chunks/global.d.DVsSRdQ5.d.ts","./node_modules/vitest/optional-runtime-types.d.ts","./node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","./node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","./node_modules/vitest/dist/runners.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./vitest.setup.ts","./node_modules/next-auth/adapters.d.ts","./node_modules/jose/dist/types/types.d.ts","./node_modules/jose/dist/types/jwe/compact/decrypt.d.ts","./node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts","./node_modules/jose/dist/types/jwe/general/decrypt.d.ts","./node_modules/jose/dist/types/jwe/general/encrypt.d.ts","./node_modules/jose/dist/types/jws/compact/verify.d.ts","./node_modules/jose/dist/types/jws/flattened/verify.d.ts","./node_modules/jose/dist/types/jws/general/verify.d.ts","./node_modules/jose/dist/types/jwt/verify.d.ts","./node_modules/jose/dist/types/jwt/decrypt.d.ts","./node_modules/jose/dist/types/jwt/produce.d.ts","./node_modules/jose/dist/types/jwe/compact/encrypt.d.ts","./node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts","./node_modules/jose/dist/types/jws/compact/sign.d.ts","./node_modules/jose/dist/types/jws/flattened/sign.d.ts","./node_modules/jose/dist/types/jws/general/sign.d.ts","./node_modules/jose/dist/types/jwt/sign.d.ts","./node_modules/jose/dist/types/jwt/encrypt.d.ts","./node_modules/jose/dist/types/jwk/thumbprint.d.ts","./node_modules/jose/dist/types/jwk/embedded.d.ts","./node_modules/jose/dist/types/jwks/local.d.ts","./node_modules/jose/dist/types/jwks/remote.d.ts","./node_modules/jose/dist/types/jwt/unsecured.d.ts","./node_modules/jose/dist/types/key/export.d.ts","./node_modules/jose/dist/types/key/import.d.ts","./node_modules/jose/dist/types/util/decode_protected_header.d.ts","./node_modules/jose/dist/types/util/decode_jwt.d.ts","./node_modules/jose/dist/types/util/errors.d.ts","./node_modules/jose/dist/types/key/generate_key_pair.d.ts","./node_modules/jose/dist/types/key/generate_secret.d.ts","./node_modules/jose/dist/types/util/base64url.d.ts","./node_modules/jose/dist/types/util/runtime.d.ts","./node_modules/jose/dist/types/index.d.ts","./node_modules/openid-client/types/index.d.ts","./node_modules/next-auth/providers/oauth-types.d.ts","./node_modules/next-auth/providers/oauth.d.ts","./node_modules/next-auth/providers/email.d.ts","./node_modules/next-auth/core/lib/cookie.d.ts","./node_modules/next-auth/core/index.d.ts","./node_modules/next-auth/providers/credentials.d.ts","./node_modules/next-auth/providers/index.d.ts","./node_modules/next-auth/jwt/types.d.ts","./node_modules/next-auth/jwt/index.d.ts","./node_modules/next-auth/utils/logger.d.ts","./node_modules/next-auth/core/types.d.ts","./node_modules/next-auth/next/index.d.ts","./node_modules/next-auth/index.d.ts","./node_modules/next-auth/providers/github.d.ts","./node_modules/next-auth/providers/google.d.ts","./src/lib/auth.ts","./src/app/api/auth/[...nextauth]/route.ts","./src/lib/types.ts","./src/lib/api.ts","./src/app/api/content-skills/route.ts","./src/app/api/content-skills/__tests__/route.test.ts","./src/app/api/entities/route.ts","./src/app/api/entities/[id]/route.ts","./src/app/api/entities/[id]/__tests__/route.test.ts","./src/app/api/entities/__tests__/route.test.ts","./src/app/api/entity-candidates/[id]/route.ts","./src/app/api/entity-candidates/[id]/__tests__/route.test.ts","./src/app/api/feedback/route.ts","./src/app/api/feedback/__tests__/route.test.ts","./src/app/api/invitations/[token]/accept/route.ts","./src/app/api/invitations/[token]/accept/__tests__/route.test.ts","./src/app/api/profile/route.ts","./src/app/api/profile/__tests__/route.test.ts","./src/app/api/profile/avatar/route.ts","./src/app/api/profile/avatar/__tests__/route.test.ts","./src/app/api/projects/route.ts","./src/app/api/projects/[id]/bluesky-credentials/route.ts","./src/app/api/projects/[id]/bluesky-credentials/__tests__/route.test.ts","./src/app/api/projects/[id]/intake/route.ts","./src/app/api/projects/[id]/intake/__tests__/route.test.ts","./src/app/api/projects/[id]/intake-allowlist/route.ts","./src/app/api/projects/[id]/intake-allowlist/[allowlistId]/route.ts","./src/app/api/projects/[id]/intake-allowlist/[allowlistId]/__tests__/route.test.ts","./src/app/api/projects/[id]/intake-allowlist/__tests__/route.test.ts","./src/app/api/projects/[id]/invitations/route.ts","./src/app/api/projects/[id]/invitations/[invitationId]/revoke/route.ts","./src/app/api/projects/[id]/invitations/__tests__/route.test.ts","./src/app/api/projects/[id]/mastodon-credentials/route.ts","./src/app/api/projects/[id]/mastodon-credentials/__tests__/route.test.ts","./src/app/api/projects/[id]/members/[membershipId]/route.ts","./src/app/api/projects/[id]/members/[membershipId]/__tests__/route.test.ts","./src/app/api/projects/[id]/rotate-intake-token/route.ts","./src/app/api/projects/[id]/rotate-intake-token/__tests__/route.test.ts","./src/app/api/projects/[id]/verify-bluesky-credentials/route.ts","./src/app/api/projects/[id]/verify-bluesky-credentials/__tests__/route.test.ts","./src/app/api/projects/[id]/verify-mastodon-credentials/route.ts","./src/app/api/projects/[id]/verify-mastodon-credentials/__tests__/route.test.ts","./src/app/api/projects/__tests__/route.test.ts","./src/app/api/review/[id]/route.ts","./src/app/api/review/[id]/__tests__/route.test.ts","./src/app/api/skills/[skillName]/route.ts","./src/app/api/skills/[skillName]/__tests__/route.test.ts","./src/app/api/source-configs/route.ts","./src/app/api/source-configs/[id]/route.ts","./src/app/api/source-configs/[id]/__tests__/route.test.ts","./src/app/api/source-configs/__tests__/route.test.ts","./src/lib/view-helpers.ts","./src/lib/dashboard-view.ts","./src/lib/profile.ts","./node_modules/@tanstack/query-core/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/query-core/build/modern/index.d.ts","./node_modules/@tanstack/react-query/build/modern/_tsup-dts-rollup.d.ts","./node_modules/@tanstack/react-query/build/modern/index.d.ts","./src/lib/useRole.ts","./src/lib/__tests__/api.test.ts","./src/lib/__tests__/auth.test.ts","./src/lib/__tests__/dashboard-view.test.ts","./src/lib/__tests__/view-helpers.test.ts","./test-support/server-only.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./src/components/query-provider.tsx","./src/app/layout.tsx","./node_modules/next-auth/client/_utils.d.ts","./node_modules/next-auth/react/types.d.ts","./node_modules/next-auth/react/index.d.ts","./src/components/user-menu.tsx","./src/components/app-shell.tsx","./src/components/status-badge.tsx","./src/app/page.tsx","./src/app/__tests__/page.test.tsx","./src/app/admin/health/page.tsx","./src/app/admin/health/__tests__/page.test.tsx","./src/app/admin/projects/new/page.tsx","./src/components/copy-button.tsx","./src/app/admin/sources/page.tsx","./src/app/admin/sources/__tests__/page.test.tsx","./src/components/skill-action-bar.tsx","./src/app/content/[id]/page.tsx","./src/app/content/[id]/__tests__/page.test.tsx","./src/app/entities/page.tsx","./src/app/entities/[id]/page.tsx","./src/app/entities/[id]/__tests__/page.test.tsx","./src/app/entities/__tests__/page.test.tsx","./src/app/invite/[token]/page.tsx","./src/components/auth/social-auth-buttons.tsx","./src/components/auth/login-form.tsx","./src/app/login/page.tsx","./src/app/login/__tests__/page.test.tsx","./node_modules/file-selector/dist/file.d.ts","./node_modules/file-selector/dist/file-selector.d.ts","./node_modules/file-selector/dist/index.d.ts","./node_modules/react-dropzone/typings/react-dropzone.d.ts","./src/components/profile/avatar-dropzone.tsx","./src/components/profile/avatar-preview.tsx","./src/components/profile/profile-form.tsx","./src/components/profile/profile-settings-panel.tsx","./src/app/profile/page.tsx","./src/app/projects/[id]/members/page.tsx","./src/app/projects/[id]/members/invite/page.tsx","./src/components/__tests__/app-shell.test.tsx","./src/components/__tests__/query-provider.test.tsx","./src/components/__tests__/skill-action-bar.test.tsx","./src/components/__tests__/status-badge.test.tsx","./src/components/__tests__/user-menu.test.tsx","./src/components/auth/__tests__/login-form.test.tsx","./src/components/auth/__tests__/social-auth-buttons.test.tsx","./src/components/profile/__tests__/avatar-dropzone.test.tsx","./src/lib/__tests__/useRole.test.tsx","./.next/types/cache-life.d.ts","./.next/types/routes.d.ts","./.next/types/validator.ts","./.next/dev/types/cache-life.d.ts","./.next/dev/types/validator.ts","./node_modules/vitest/globals.d.ts"],"fileIdsList":[[101,164,172,176,179,181,182,183,195,512,513,514,515,861],[101,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,255,553,556,559,747,750,752,753,756,758,760,762,764,766,767,769,771,772,775,776,778,780,782,784,786,789,791,793,794,814,821,823,825,827,830,832,833,836,839,849,850,851,861,864],[101,164,172,176,179,181,182,183,195,512,513,514,515,864],[101,164,172,176,179,181,182,183,195,255,553,556,747,750,752,753,756,758,767,769,771,772,782,784,789,791,793,794,814,821,823,827,830,832,833,839,861,862,864],[101,164,172,176,179,181,182,183,195,557,558,559,861,864],[101,164,172,176,179,181,182,183,195,255,557,861,864],[101,164,172,176,179,181,182,183,195,800,861,864],[92,101,164,172,176,179,181,182,183,195,255,801,861,864],[101,164,172,176,179,181,182,183,195,802,861,864],[101,164,172,176,179,181,182,183,195,669,861,864],[101,164,172,176,179,181,182,183,195,666,667,668,669,670,673,674,675,676,677,678,679,680,861,864],[101,164,172,176,179,181,182,183,195,662,861,864],[101,164,172,176,179,181,182,183,195,672,861,864],[101,164,172,176,179,181,182,183,195,666,667,668,861,864],[101,164,172,176,179,181,182,183,195,666,667,861,864],[101,164,172,176,179,181,182,183,195,669,670,672,861,864],[101,164,172,176,179,181,182,183,195,667,861,864],[101,164,172,176,179,181,182,183,195,664,861,864],[101,164,172,176,179,181,182,183,195,663,861,864],[92,101,164,172,176,179,181,182,183,195,225,488,681,682,861,864],[101,164,172,176,179,181,182,183,195,624,625,861,864],[101,161,162,164,172,176,179,181,182,183,195,861,864],[101,163,164,172,176,179,181,182,183,195,861,864],[164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,203,861,864],[101,164,165,170,172,175,176,179,181,182,183,185,195,200,212,861,864],[101,164,165,166,172,175,176,179,181,182,183,195,861,864],[101,164,167,172,176,179,181,182,183,195,213,861,864],[101,164,168,169,172,176,179,181,182,183,186,195,861,864],[101,164,169,172,176,179,181,182,183,195,200,209,861,864],[101,164,170,172,175,176,179,181,182,183,185,195,861,864],[101,163,164,171,172,176,179,181,182,183,195,861,864],[101,164,172,173,176,179,181,182,183,195,861,864],[101,164,172,174,175,176,179,181,182,183,195,861,864],[101,163,164,172,175,176,179,181,182,183,195,861,864],[101,164,172,175,176,177,179,181,182,183,195,200,212,861,864],[101,164,172,175,176,177,179,181,182,183,195,200,203,861,864],[101,151,164,172,175,176,178,179,181,182,183,185,195,200,212,861,864],[101,164,172,175,176,178,179,181,182,183,185,195,200,209,212,861,864],[101,164,172,176,178,179,180,181,182,183,195,200,209,212,861,864],[99,100,101,102,103,104,105,106,107,108,109,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,861,864],[101,164,172,175,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,183,195,861,864],[101,164,172,176,179,181,182,183,184,195,212,861,864],[101,164,172,175,176,179,181,182,183,185,195,200,861,864],[101,164,172,176,179,181,182,183,186,195,861,864],[101,164,172,176,179,181,182,183,187,195,861,864],[101,164,172,175,176,179,181,182,183,190,195,861,864],[101,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,861,864],[101,164,172,176,179,181,182,183,192,195,861,864],[101,164,172,176,179,181,182,183,193,195,861,864],[101,164,169,172,176,179,181,182,183,185,195,203,861,864],[101,164,172,175,176,179,181,182,183,195,196,861,864],[101,164,172,176,179,181,182,183,195,197,213,216,861,864],[101,164,172,175,176,179,181,182,183,195,200,202,203,861,864],[101,164,172,176,179,181,182,183,195,201,203,861,864],[101,164,172,176,179,181,182,183,195,203,213,861,864],[101,164,172,176,179,181,182,183,195,204,861,864],[101,161,164,172,176,179,181,182,183,195,200,206,212,861,864],[101,164,172,176,179,181,182,183,195,200,205,861,864],[101,164,172,175,176,179,181,182,183,195,207,208,861,864],[101,164,172,176,179,181,182,183,195,207,208,861,864],[101,164,169,172,176,179,181,182,183,185,195,200,209,861,864],[101,164,172,176,179,181,182,183,195,210,861,864],[101,164,172,176,179,181,182,183,185,195,211,861,864],[101,164,172,176,178,179,181,182,183,193,195,212,861,864],[101,164,172,176,179,181,182,183,195,213,214,861,864],[101,164,169,172,176,179,181,182,183,195,214,861,864],[101,164,172,176,179,181,182,183,195,200,215,861,864],[101,164,172,176,179,181,182,183,184,195,216,861,864],[101,164,172,176,179,181,182,183,195,217,861,864],[101,164,167,172,176,179,181,182,183,195,861,864],[101,164,169,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,213,861,864],[101,151,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,212,861,864],[101,164,172,176,179,181,182,183,195,218,861,864],[101,164,172,176,179,181,182,183,190,195,861,864],[101,164,172,176,179,181,182,183,195,208,861,864],[101,151,164,172,175,176,177,179,181,182,183,190,195,200,203,212,215,216,218,861,864],[101,164,172,176,179,181,182,183,195,200,219,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,222,223,225,507,552,861,864],[92,101,164,172,176,179,181,182,183,195,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,222,223,224,488,507,552,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,222,224,225,507,552,861,864],[92,101,164,172,176,179,181,182,183,195,225,488,489,861,864],[92,101,164,172,176,179,181,182,183,195,225,488,861,864],[92,96,101,164,172,176,179,181,182,183,195,222,223,224,225,507,552,861,864],[92,96,101,164,172,176,179,181,182,183,195,221,223,224,225,507,552,861,864],[90,91,101,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,613,614,659,861,864],[101,164,172,176,179,181,182,183,195,617,618,619,622,623,626,861,864],[101,164,172,176,179,181,182,183,195,647,861,864],[101,164,172,176,179,181,182,183,195,647,648,861,864],[101,164,172,176,179,181,182,183,195,622,631,632,861,864],[101,164,172,176,179,181,182,183,195,622,631,861,864],[101,164,172,176,179,181,182,183,195,631,861,864],[101,164,172,176,179,181,182,183,195,620,631,635,636,861,864],[101,164,172,176,179,181,182,183,195,620,631,635,861,864],[101,164,172,176,179,181,182,183,195,616,861,864],[101,164,172,176,179,181,182,183,195,620,621,861,864],[101,164,172,176,179,181,182,183,195,620,861,864],[101,164,172,176,179,181,182,183,195,620,621,628,652,861,864],[101,164,172,176,179,181,182,183,195,628,861,864],[101,164,172,176,179,181,182,183,195,620,623,628,629,630,861,864],[101,164,172,176,179,181,182,183,195,689,690,861,864],[101,164,172,176,179,181,182,183,195,689,690,691,692,861,864],[101,164,172,176,179,181,182,183,195,689,691,861,864],[101,164,172,176,179,181,182,183,195,689,861,864],[101,164,172,176,179,181,182,183,195,841,861,864],[101,164,172,176,179,181,182,183,195,841,842,861,864],[101,164,172,176,179,181,182,183,195,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,861,864],[101,164,172,176,179,181,182,183,195,697,861,864],[101,164,172,176,179,181,182,183,195,697,707,861,864],[101,164,172,176,179,181,182,183,195,606,607,861,864],[101,164,172,176,179,181,182,183,195,743,861,864],[101,164,172,176,178,179,181,182,183,195,220,743,861,864],[101,164,172,176,179,181,182,183,195,734,741,861,864],[101,164,172,176,179,181,182,183,195,553,557,741,743,861,864],[101,164,172,176,179,181,182,183,195,696,730,737,739,740,861,864],[101,164,172,176,179,181,182,183,195,735,741,742,861,864],[101,164,172,176,179,181,182,183,195,553,557,738,743,861,864],[101,164,172,176,179,181,182,183,195,220,743,861,864],[101,164,172,176,179,181,182,183,195,735,737,743,861,864],[101,164,172,176,179,181,182,183,195,737,741,743,861,864],[101,164,172,176,179,181,182,183,195,737,861,864],[101,164,172,176,179,181,182,183,195,732,733,736,861,864],[101,164,172,176,179,181,182,183,195,729,730,731,737,743,861,864],[92,101,164,172,176,179,181,182,183,195,737,743,815,816,861,864],[92,101,164,172,176,179,181,182,183,195,737,743,861,864],[101,164,172,176,179,181,182,183,195,510,861,864],[101,164,172,176,179,181,182,183,195,512,513,514,515,861,864],[101,164,172,176,179,181,182,183,195,458,521,522,861,864],[101,164,172,176,179,181,182,183,195,230,231,233,245,269,384,395,503,861,864],[101,164,172,176,179,181,182,183,195,233,264,265,266,268,503,861,864],[101,164,172,176,179,181,182,183,195,233,401,403,405,406,408,503,505,861,864],[101,164,172,176,179,181,182,183,195,233,267,304,503,861,864],[101,164,172,176,179,181,182,183,195,231,233,244,245,251,257,262,383,384,385,394,503,505,861,864],[101,164,172,176,179,181,182,183,195,503,861,864],[101,164,172,176,179,181,182,183,195,240,246,265,285,380,861,864],[101,164,172,176,179,181,182,183,195,233,861,864],[101,164,172,176,179,181,182,183,195,226,240,246,861,864],[101,164,172,176,179,181,182,183,195,412,861,864],[101,164,172,176,179,181,182,183,195,409,410,412,861,864],[101,164,172,176,179,181,182,183,195,409,411,503,861,864],[101,164,172,176,178,179,181,182,183,195,285,482,500,861,864],[101,164,172,176,178,179,181,182,183,195,356,359,375,380,500,861,864],[101,164,172,176,178,179,181,182,183,195,328,500,861,864],[101,164,172,176,179,181,182,183,195,388,861,864],[101,164,172,176,179,181,182,183,195,387,388,389,861,864],[101,164,172,176,179,181,182,183,195,387,861,864],[98,101,164,172,176,178,179,181,182,183,195,226,233,245,251,257,263,265,269,270,283,284,351,381,382,395,503,507,861,864],[101,164,172,176,179,181,182,183,195,230,233,267,304,401,402,407,503,555,861,864],[101,164,172,176,179,181,182,183,195,267,555,861,864],[101,164,172,176,179,181,182,183,195,230,284,453,503,555,861,864],[101,164,172,176,179,181,182,183,195,555,861,864],[101,164,172,176,179,181,182,183,195,233,267,268,555,861,864],[101,164,172,176,179,181,182,183,195,404,555,861,864],[101,164,172,176,179,181,182,183,195,270,383,386,393,861,864],[92,101,164,172,176,179,181,182,183,195,458,861,864],[101,164,172,176,179,181,182,183,193,195,240,255,861,864],[101,164,172,176,179,181,182,183,195,240,255,861,864],[92,101,164,172,176,179,181,182,183,195,325,861,864],[92,101,164,172,176,179,181,182,183,195,255,861,864],[92,101,164,172,176,179,181,182,183,195,246,255,458,861,864],[101,164,172,176,179,181,182,183,195,240,311,325,326,537,544,861,864],[101,164,172,176,179,181,182,183,195,310,538,539,540,541,543,861,864],[101,164,172,176,179,181,182,183,195,361,861,864],[101,164,172,176,179,181,182,183,195,361,362,861,864],[101,164,172,176,179,181,182,183,195,244,246,313,314,861,864],[101,164,172,176,179,181,182,183,195,246,320,321,861,864],[101,164,172,176,179,181,182,183,195,246,315,323,861,864],[101,164,172,176,179,181,182,183,195,320,861,864],[101,164,172,176,179,181,182,183,195,238,246,313,314,315,316,317,318,319,320,323,861,864],[101,164,172,176,179,181,182,183,195,246,313,320,321,322,324,861,864],[101,164,172,176,179,181,182,183,195,246,314,316,317,861,864],[101,164,172,176,179,181,182,183,195,314,316,319,321,861,864],[101,164,172,176,179,181,182,183,195,542,861,864],[101,164,172,176,179,181,182,183,195,246,861,864],[92,101,164,172,176,179,181,182,183,195,234,531,861,864],[92,101,164,172,176,179,181,182,183,195,212,861,864],[92,101,164,172,176,179,181,182,183,195,267,302,861,864],[92,101,164,172,176,179,181,182,183,195,267,395,861,864],[101,164,172,176,179,181,182,183,195,300,305,861,864],[92,101,164,172,176,179,181,182,183,195,301,509,861,864],[101,164,172,176,179,181,182,183,195,810,861,864],[92,96,101,164,172,176,178,179,181,182,183,195,221,222,223,224,225,507,551,861,864],[101,164,172,176,178,179,181,182,183,195,246,861,864],[101,164,172,176,178,179,181,182,183,195,245,250,331,348,390,391,395,450,452,503,504,861,864],[101,164,172,176,179,181,182,183,195,283,392,861,864],[101,164,172,176,179,181,182,183,195,507,861,864],[101,164,172,176,179,181,182,183,195,232,861,864],[92,101,164,172,176,179,181,182,183,195,237,240,455,471,473,861,864],[101,164,172,176,179,181,182,183,193,195,240,455,470,471,472,554,861,864],[101,164,172,176,179,181,182,183,195,464,465,466,467,468,469,861,864],[101,164,172,176,179,181,182,183,195,466,861,864],[101,164,172,176,179,181,182,183,195,470,861,864],[101,164,172,176,179,181,182,183,195,255,419,420,422,861,864],[92,101,164,172,176,179,181,182,183,195,246,413,414,415,416,421,861,864],[101,164,172,176,179,181,182,183,195,419,421,861,864],[101,164,172,176,179,181,182,183,195,417,861,864],[101,164,172,176,179,181,182,183,195,418,861,864],[92,101,164,172,176,179,181,182,183,195,255,301,509,861,864],[92,101,164,172,176,179,181,182,183,195,255,508,509,861,864],[92,101,164,172,176,179,181,182,183,195,255,509,861,864],[101,164,172,176,179,181,182,183,195,348,349,861,864],[101,164,172,176,179,181,182,183,195,349,861,864],[101,164,172,176,178,179,181,182,183,195,504,509,861,864],[101,164,172,176,179,181,182,183,195,378,861,864],[101,163,164,172,176,179,181,182,183,195,377,861,864],[101,164,172,176,179,181,182,183,195,240,246,252,254,356,369,373,375,452,455,492,493,500,504,861,864],[101,164,172,176,179,181,182,183,195,246,295,317,861,864],[101,164,172,176,179,181,182,183,195,356,367,370,375,861,864],[92,101,164,172,176,179,181,182,183,195,237,240,356,359,375,378,412,459,460,461,462,463,474,475,476,477,478,479,480,481,555,861,864],[101,164,172,176,179,181,182,183,195,237,240,265,356,363,364,365,368,369,861,864],[101,164,172,176,179,181,182,183,195,200,246,265,367,374,455,456,500,861,864],[101,164,172,176,179,181,182,183,195,371,861,864],[101,164,172,176,178,179,181,182,183,193,195,234,246,250,260,292,293,296,348,351,416,450,451,492,503,504,505,507,555,861,864],[101,164,172,176,179,181,182,183,195,237,238,240,861,864],[101,164,172,176,179,181,182,183,195,356,861,864],[101,163,164,172,176,179,181,182,183,195,265,292,293,350,351,352,353,354,355,504,861,864],[101,164,172,176,179,181,182,183,195,375,861,864],[101,163,164,172,176,179,181,182,183,195,239,240,250,254,290,356,363,364,365,366,367,370,371,372,373,374,493,861,864],[101,164,172,176,178,179,181,182,183,195,290,291,363,504,505,861,864],[101,164,172,176,179,181,182,183,195,265,293,348,351,356,452,504,861,864],[101,164,172,176,178,179,181,182,183,195,503,505,861,864],[101,164,172,176,178,179,181,182,183,195,200,500,504,505,861,864],[101,164,172,176,178,179,181,182,183,193,195,226,240,245,252,254,257,260,267,287,292,293,294,295,296,331,332,334,337,339,342,343,344,345,347,395,450,452,500,503,504,505,861,864],[101,164,172,176,178,179,181,182,183,195,200,861,864],[101,164,172,176,179,181,182,183,195,233,234,235,263,500,501,502,507,509,555,861,864],[101,164,172,176,179,181,182,183,195,230,231,503,861,864],[101,164,172,176,179,181,182,183,195,424,861,864],[101,164,172,176,178,179,181,182,183,195,200,212,242,408,412,413,414,415,416,422,423,555,861,864],[101,164,172,176,179,181,182,183,193,195,212,226,240,242,254,257,293,332,337,347,348,401,428,429,430,436,439,440,450,452,500,503,861,864],[101,164,172,176,179,181,182,183,195,257,263,270,283,293,351,503,861,864],[101,164,172,176,178,179,181,182,183,195,212,234,245,254,293,434,500,503,861,864],[101,164,172,176,179,181,182,183,195,454,861,864],[101,164,172,176,178,179,181,182,183,195,424,437,438,447,861,864],[101,164,172,176,179,181,182,183,195,500,503,861,864],[101,164,172,176,179,181,182,183,195,353,493,861,864],[101,164,172,176,179,181,182,183,195,254,292,395,509,861,864],[101,164,172,176,178,179,181,182,183,193,195,232,337,397,401,430,436,439,442,500,861,864],[101,164,172,176,178,179,181,182,183,195,270,283,401,443,861,864],[101,164,172,176,179,181,182,183,195,233,294,395,445,503,505,861,864],[101,164,172,176,178,179,181,182,183,195,212,416,503,861,864],[101,164,172,176,178,179,181,182,183,195,267,294,395,396,397,406,424,444,446,503,861,864],[98,101,164,172,176,178,179,181,182,183,195,292,449,507,509,861,864],[101,164,172,176,179,181,182,183,195,346,450,861,864],[101,164,172,176,178,179,181,182,183,193,195,240,243,245,246,252,254,260,269,270,283,293,296,332,334,344,347,348,395,428,429,430,431,433,435,450,452,500,509,861,864],[101,164,172,176,178,179,181,182,183,195,200,270,436,441,447,500,861,864],[101,164,172,176,179,181,182,183,195,273,274,275,276,277,278,279,280,281,282,861,864],[101,164,172,176,179,181,182,183,195,287,338,861,864],[101,164,172,176,179,181,182,183,195,340,861,864],[101,164,172,176,179,181,182,183,195,338,861,864],[101,164,172,176,179,181,182,183,195,340,341,861,864],[101,164,172,176,178,179,181,182,183,195,244,245,246,250,251,504,861,864],[101,164,172,176,178,179,181,182,183,193,195,232,234,252,256,292,295,296,330,450,500,505,507,509,861,864],[101,164,172,176,178,179,181,182,183,193,195,212,236,243,244,254,256,293,448,493,499,504,861,864],[101,164,172,176,179,181,182,183,195,363,861,864],[101,164,172,176,179,181,182,183,195,364,861,864],[101,164,172,176,179,181,182,183,195,246,257,492,861,864],[101,164,172,176,179,181,182,183,195,365,861,864],[101,164,172,176,179,181,182,183,195,239,861,864],[101,164,172,176,179,181,182,183,195,241,253,861,864],[101,164,172,176,178,179,181,182,183,195,241,245,252,861,864],[101,164,172,176,179,181,182,183,195,248,253,861,864],[101,164,172,176,179,181,182,183,195,249,861,864],[101,164,172,176,179,181,182,183,195,241,242,861,864],[101,164,172,176,179,181,182,183,195,241,297,861,864],[101,164,172,176,179,181,182,183,195,241,861,864],[101,164,172,176,179,181,182,183,195,243,287,336,861,864],[101,164,172,176,179,181,182,183,195,335,861,864],[101,164,172,176,179,181,182,183,195,240,242,243,861,864],[101,164,172,176,179,181,182,183,195,243,333,861,864],[101,164,172,176,179,181,182,183,195,240,242,861,864],[101,164,172,176,179,181,182,183,195,292,395,861,864],[101,164,172,176,179,181,182,183,195,492,861,864],[101,164,172,176,178,179,181,182,183,195,212,252,254,258,292,395,449,452,455,456,457,483,484,487,491,493,500,504,861,864],[101,164,172,176,179,181,182,183,195,306,309,311,312,325,326,861,864],[92,101,164,172,176,179,181,182,183,195,223,225,255,485,486,861,864],[92,101,164,172,176,179,181,182,183,195,223,225,255,485,486,490,861,864],[101,164,172,176,179,181,182,183,195,379,861,864],[101,164,172,176,179,181,182,183,195,265,286,291,292,356,357,358,359,360,362,375,376,378,381,449,452,503,505,861,864],[101,164,172,176,179,181,182,183,195,325,861,864],[101,164,172,176,178,179,181,182,183,195,330,500,861,864],[101,164,172,176,179,181,182,183,195,330,861,864],[101,164,172,176,178,179,181,182,183,195,252,298,327,329,331,449,500,507,509,861,864],[101,164,172,176,179,181,182,183,195,306,307,308,309,311,312,325,326,508,861,864],[98,101,164,172,176,178,179,181,182,183,193,195,212,241,242,254,260,292,293,296,395,447,448,450,500,503,504,507,861,864],[101,164,172,176,179,181,182,183,195,237,240,247,861,864],[101,164,172,176,179,181,182,183,195,291,293,425,428,861,864],[101,164,172,176,179,181,182,183,195,291,426,494,495,496,497,498,861,864],[101,164,172,176,178,179,181,182,183,195,287,503,861,864],[101,164,172,176,178,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,290,375,861,864],[101,164,172,176,179,181,182,183,195,289,861,864],[101,164,172,176,179,181,182,183,195,291,344,861,864],[101,164,172,176,179,181,182,183,195,288,290,503,861,864],[101,164,172,176,178,179,181,182,183,195,236,291,425,426,427,500,503,504,861,864],[92,101,164,172,176,179,181,182,183,195,240,246,324,861,864],[92,101,164,172,176,179,181,182,183,195,238,861,864],[101,164,172,176,179,181,182,183,195,228,229,861,864],[92,101,164,172,176,179,181,182,183,195,234,861,864],[92,101,164,172,176,179,181,182,183,195,240,310,861,864],[92,98,101,164,172,176,179,181,182,183,195,292,296,507,509,861,864],[101,164,172,176,179,181,182,183,195,234,531,532,861,864],[92,101,164,172,176,179,181,182,183,195,305,861,864],[92,101,164,172,176,179,181,182,183,193,195,212,232,299,301,303,304,509,861,864],[101,164,172,176,179,181,182,183,195,240,267,504,861,864],[101,164,172,176,179,181,182,183,195,240,432,861,864],[92,101,164,172,176,178,179,181,182,183,193,195,230,232,305,403,507,508,861,864],[92,101,164,172,176,179,181,182,183,195,221,222,223,224,225,507,552,861,864],[92,93,94,95,96,101,164,172,176,179,181,182,183,195,861,864],[101,164,172,176,179,181,182,183,195,398,399,400,861,864],[101,164,172,176,179,181,182,183,195,398,861,864],[92,96,101,164,172,176,178,179,180,181,182,183,193,195,220,221,222,223,224,225,226,232,260,265,442,470,505,506,509,552,861,864],[101,164,172,176,179,181,182,183,195,517,861,864],[101,164,172,176,179,181,182,183,195,519,861,864],[101,164,172,176,179,181,182,183,195,523,861,864],[101,164,172,176,179,181,182,183,195,811,861,864],[101,164,172,176,179,181,182,183,195,525,861,864],[101,164,172,176,179,181,182,183,195,527,528,529,861,864],[101,164,172,176,179,181,182,183,195,533,861,864],[97,101,164,172,176,179,181,182,183,195,511,516,518,520,524,526,530,534,536,546,547,549,553,554,555,556,861,864],[101,164,172,176,179,181,182,183,195,535,861,864],[101,164,172,176,179,181,182,183,195,545,861,864],[101,164,172,176,179,181,182,183,195,301,861,864],[101,164,172,176,179,181,182,183,195,548,861,864],[101,163,164,172,176,179,181,182,183,195,291,425,426,428,494,495,497,498,550,552,861,864],[101,164,172,176,179,181,182,183,195,220,861,864],[101,164,169,172,176,178,179,180,181,182,183,195,212,213,220,729,861,864],[101,164,172,176,179,181,182,183,195,601,861,864],[101,164,172,176,179,181,182,183,195,599,601,861,864],[101,164,172,176,179,181,182,183,195,590,598,599,600,602,604,861,864],[101,164,172,176,179,181,182,183,195,588,861,864],[101,164,172,176,179,181,182,183,195,591,596,601,604,861,864],[101,164,172,176,179,181,182,183,195,587,604,861,864],[101,164,172,176,179,181,182,183,195,591,592,595,596,597,604,861,864],[101,164,172,176,179,181,182,183,195,591,592,593,595,596,604,861,864],[101,164,172,176,179,181,182,183,195,588,589,590,591,592,596,597,598,600,601,602,604,861,864],[101,164,172,176,179,181,182,183,195,604,861,864],[101,164,172,176,179,181,182,183,195,586,588,589,590,591,592,593,595,596,597,598,599,600,601,602,603,861,864],[101,164,172,176,179,181,182,183,195,586,604,861,864],[101,164,172,176,179,181,182,183,195,591,593,594,596,597,604,861,864],[101,164,172,176,179,181,182,183,195,595,604,861,864],[101,164,172,176,179,181,182,183,195,596,597,601,604,861,864],[101,164,172,176,179,181,182,183,195,589,599,861,864],[101,164,172,176,179,181,182,183,195,671,861,864],[92,101,164,172,176,179,181,182,183,195,843,861,864],[101,164,172,176,179,181,182,183,195,573,861,864],[101,164,172,176,179,181,182,183,195,565,567,573,861,864],[101,164,172,176,179,181,182,183,195,566,567,861,864],[101,164,172,176,179,181,182,183,195,567,573,577,861,864],[101,164,172,176,179,181,182,183,195,566,861,864],[101,164,172,176,179,181,182,183,195,567,573,861,864],[101,164,172,176,179,181,182,183,195,565,566,567,572,861,864],[101,164,172,176,179,181,182,183,195,565,567,861,864],[101,164,172,176,179,181,182,183,195,566,567,579,861,864],[101,164,172,176,179,181,182,183,195,568,569,570,861,864],[101,164,172,176,179,181,182,183,195,571,861,864],[101,164,172,176,179,181,182,183,195,200,220,861,864],[101,116,119,122,123,164,172,176,179,181,182,183,195,212,861,864],[101,119,164,172,176,179,181,182,183,195,200,212,861,864],[101,119,123,164,172,176,179,181,182,183,195,212,861,864],[101,164,172,176,179,181,182,183,195,200,861,864],[101,113,164,172,176,179,181,182,183,195,861,864],[101,117,164,172,176,179,181,182,183,195,861,864],[101,115,116,119,164,172,176,179,181,182,183,195,212,861,864],[101,164,172,176,179,181,182,183,185,195,209,861,864],[101,113,164,172,176,179,181,182,183,195,220,861,864],[101,115,119,164,172,176,179,181,182,183,185,195,212,861,864],[101,110,111,112,114,118,164,172,175,176,179,181,182,183,195,200,212,861,864],[101,119,128,136,164,172,176,179,181,182,183,195,861,864],[101,111,117,164,172,176,179,181,182,183,195,861,864],[101,119,145,146,164,172,176,179,181,182,183,195,861,864],[101,111,114,119,164,172,176,179,181,182,183,195,203,212,220,861,864],[101,119,164,172,176,179,181,182,183,195,861,864],[101,115,119,164,172,176,179,181,182,183,195,212,861,864],[101,110,164,172,176,179,181,182,183,195,861,864],[101,113,114,115,117,118,119,120,121,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,146,147,148,149,150,164,172,176,179,181,182,183,195,861,864],[101,119,138,141,164,172,176,179,181,182,183,195,861,864],[101,119,128,129,130,164,172,176,179,181,182,183,195,861,864],[101,117,119,129,131,164,172,176,179,181,182,183,195,861,864],[101,118,164,172,176,179,181,182,183,195,861,864],[101,111,113,119,164,172,176,179,181,182,183,195,861,864],[101,119,123,129,131,164,172,176,179,181,182,183,195,861,864],[101,123,164,172,176,179,181,182,183,195,861,864],[101,117,119,122,164,172,176,179,181,182,183,195,212,861,864],[101,111,115,119,128,164,172,176,179,181,182,183,195,861,864],[101,119,138,164,172,176,179,181,182,183,195,861,864],[101,131,164,172,176,179,181,182,183,195,861,864],[101,113,119,145,164,172,176,179,181,182,183,195,203,218,220,861,864],[101,164,172,176,179,181,182,183,195,562,861,864],[101,164,172,175,176,178,179,180,181,182,183,185,195,200,209,212,219,220,562,563,564,574,575,576,578,580,582,583,584,585,605,609,610,611,612,613,861,864],[101,164,172,176,179,181,182,183,195,562,563,564,581,861,864],[101,164,172,176,179,181,182,183,195,564,861,864],[101,164,172,176,179,181,182,183,195,608,861,864],[101,164,172,176,179,181,182,183,195,574,584,613,861,864],[101,164,172,176,179,181,182,183,195,574,613,861,864],[101,164,172,176,179,181,182,183,195,654,861,864],[101,164,172,176,179,181,182,183,195,627,659,684,861,864],[101,164,172,176,179,181,182,183,195,617,620,622,623,629,630,631,633,634,637,638,650,651,653,684,861,864],[101,164,172,176,179,181,182,183,195,633,644,645,684,861,864],[101,164,172,176,179,181,182,183,195,633,634,641,684,861,864],[101,164,172,176,179,181,182,183,195,620,622,633,634,637,684,861,864],[101,164,172,176,179,181,182,183,195,582,861,864],[101,164,172,176,179,181,182,183,195,620,627,633,634,637,646,684,861,864],[101,164,172,176,179,181,182,183,195,613,657,659,861,864],[101,164,167,172,176,179,181,182,183,195,200,613,620,622,627,631,633,634,637,638,641,642,643,646,649,650,651,655,656,659,684,861,864],[101,164,172,176,179,181,182,183,195,582,633,634,637,684,861,864],[101,164,172,176,179,181,182,183,195,633,644,645,646,684,861,864],[101,164,172,176,179,181,182,183,195,582,633,638,639,640,684,861,864],[101,164,167,172,176,179,181,182,183,195,200,582,613,620,622,627,631,633,634,637,638,639,640,641,642,643,644,645,646,649,650,651,655,656,657,658,659,684,861,864],[101,164,172,176,179,181,182,183,195,582,617,620,622,627,631,633,634,637,638,639,640,641,642,644,645,646,649,684,685,686,687,688,693,861,864],[101,164,172,176,179,181,182,183,195,620,622,633,634,637,638,644,645,646,684,686,861,864],[101,164,172,176,179,181,182,183,195,694,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,821,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,823,861,864],[101,164,172,176,179,181,182,183,195,255,536,748,749,797,819,820,861,864],[101,164,172,176,179,181,182,183,195,255,749,797,819,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,827,861,864],[101,164,172,176,179,181,182,183,195,255,748,749,797,819,820,826,861,864],[101,164,172,176,179,181,182,183,195,255,746,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,749,750,861,864],[101,164,172,176,179,181,182,183,195,255,553,749,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,753,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,752,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,749,756,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,758,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,760,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,762,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,764,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,767,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,772,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,771,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,769,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,775,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,778,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,780,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,782,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,784,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,786,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,766,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,789,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,749,791,861,864],[101,164,172,176,179,181,182,183,195,255,553,748,749,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,794,861,864],[101,164,172,176,179,181,182,183,195,255,694,749,793,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,830,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,820,829,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,833,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,820,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,748,797,832,861,864],[101,164,172,176,179,181,182,183,195,255,536,743,746,749,797,861,864],[92,101,164,172,176,179,181,182,183,195,255,554,557,812,813,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,839,861,864],[101,164,172,176,179,181,182,183,195,255,838,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,798,819,820,861,864],[101,164,172,176,179,181,182,183,195,255,749,797,819,848,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,861,864],[101,164,172,176,179,181,182,183,195,255,536,749,797,819,826,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,748,819,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,803,813,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,813,829,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,820,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,813,818,861,864],[92,101,164,172,176,179,181,182,183,195,255,536,748,818,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,838,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,837,861,864],[92,101,164,172,176,179,181,182,183,195,255,536,546,817,837,861,864],[101,164,172,176,179,181,182,183,195,255,817,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,845,861,864],[92,101,164,172,176,179,181,182,183,195,255,844,861,864],[101,164,172,176,179,181,182,183,195,255,748,861,864],[92,101,164,172,176,179,181,182,183,195,255,748,861,864],[92,101,164,172,176,179,181,182,183,195,255,748,799,803,845,846,847,861,864],[92,101,164,172,176,179,181,182,183,195,255,803,861,864],[92,101,164,172,176,179,181,182,183,195,255,546,748,803,861,864],[92,101,164,172,176,179,181,182,183,195,255,536,799,803,817,861,864],[92,101,164,172,176,179,181,182,183,195,255,694,749,861,864],[101,164,172,176,179,181,182,183,195,255,694,746,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,798,861,864],[101,164,172,176,179,181,182,183,195,255,683,694,748,803,804,861,864],[101,164,172,176,179,181,182,183,195,255,694,748,797,861,864],[92,101,164,172,176,179,181,182,183,195,255,554,743,746,748,861,864],[101,164,172,176,179,181,182,183,195,255,554,736,743,744,745,861,864],[101,164,172,176,179,181,182,183,195,255,748,797,861,864],[101,164,172,176,179,181,182,183,195,255,861,864],[101,164,172,176,179,181,182,183,195,255,748,803,861,864],[101,164,172,176,179,181,182,183,195,212,255,615,660,861,864],[92,101,164,172,176,179,181,182,183,195,255,683,694,861,864]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"1e9332c23e9a907175e0ffc6a49e236f97b48838cc8aec9ce7e4cec21e544b65","impliedFormat":1},{"version":"3753fbc1113dc511214802a2342280a8b284ab9094f6420e7aa171e868679f91","impliedFormat":1},{"version":"999ca32883495a866aa5737fe1babc764a469e4cde6ee6b136a4b9ae68853e4b","impliedFormat":1},{"version":"17f13ecb98cbc39243f2eee1f16d45cd8ec4706b03ee314f1915f1a8b42f6984","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"0bd714129fca875f7d4c477a1a392200b0bcd13fb2e80928cd334b63830ea047","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2c9037ae6cd2c52d80ceef0b3c5ffdb488627d71529cf4f63776daf11161c9a","affectsGlobalScope":true,"impliedFormat":1},{"version":"135d5cf4d345f59f1a9caadfafcd858d3d9cc68290db616cc85797224448cccc","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc238c3f81c2984751932b6aab223cd5b830e0ac6cad76389e5e9d2ffc03287d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4a07f9b76d361f572620927e5735b77d6d2101c23cdd94383eb5b706e7b36357","affectsGlobalScope":true,"impliedFormat":1},{"version":"7c4e8dc6ab834cc6baa0227e030606d29e3e8449a9f67cdf5605ea5493c4db29","affectsGlobalScope":true,"impliedFormat":1},{"version":"de7ba0fd02e06cd9a5bd4ab441ed0e122735786e67dde1e849cced1cd8b46b78","affectsGlobalScope":true,"impliedFormat":1},{"version":"6148e4e88d720a06855071c3db02069434142a8332cf9c182cda551adedf3156","affectsGlobalScope":true,"impliedFormat":1},{"version":"d63dba625b108316a40c95a4425f8d4294e0deeccfd6c7e59d819efa19e23409","affectsGlobalScope":true,"impliedFormat":1},{"version":"0568d6befee03dd435bed4fc25c4e46865b24bdcb8c563fdc21f580a2c301904","affectsGlobalScope":true,"impliedFormat":1},{"version":"30d62269b05b584741f19a5369852d5d34895aa2ac4fd948956f886d15f9cc0d","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"ffbe6d7b295306b2ba88030f65b74c107d8d99bdcf596ea99c62a02f606108b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"996fb27b15277369c68a4ba46ed138b4e9e839a02fb4ec756f7997629242fd9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"79b712591b270d4778c89706ca2cfc56ddb8c3f895840e477388f1710dc5eda9","affectsGlobalScope":true,"impliedFormat":1},{"version":"20884846cef428b992b9bd032e70a4ef88e349263f63aeddf04dda837a7dba26","affectsGlobalScope":true,"impliedFormat":1},{"version":"5fcab789c73a97cd43828ee3cc94a61264cf24d4c44472ce64ced0e0f148bdb2","affectsGlobalScope":true,"impliedFormat":1},{"version":"db59a81f070c1880ad645b2c0275022baa6a0c4f0acdc58d29d349c6efcf0903","affectsGlobalScope":true,"impliedFormat":1},{"version":"673294292640f5722b700e7d814e17aaf7d93f83a48a2c9b38f33cbc940ad8b0","affectsGlobalScope":true,"impliedFormat":1},{"version":"d786b48f934cbca483b3c6d0a798cb43bbb4ada283e76fb22c28e53ae05b9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"142efd4ce210576f777dc34df121777be89eda476942d6d6663b03dcb53be3ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"379bc41580c2d774f82e828c70308f24a005b490c25ba34d679d84bcf05c3d9d","affectsGlobalScope":true,"impliedFormat":1},{"version":"ed484fb2aa8a1a23d0277056ec3336e0a0b52f9b8d6a961f338a642faf43235d","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ffedae1d1c2d53fdbca1c96d3c7dda544281f7d262f99b6880634f8fd8d9820","affectsGlobalScope":true,"impliedFormat":1},{"version":"83a730b125d477dd264df8ba479afab27a3dae7152b005c214ab94dc7ee44fd3","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"dc0a7f107690ee5cd8afc8dbf05c4df78085471ce16bdd9881642ec738bc81fe","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"21da358700a3893281ce0c517a7a30cbd46be020d9f0c3f2834d0a8ad1f5fc75","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc2110f7decca6bfb9392e30421cfa1436479e4a6756e8fec6cbc22625d4f881","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"4137ebf04166f3a325f056aa56101adc75e9dceb30404a1844eb8604d89770e2","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"98498b101803bb3dde9f76a56e65c14b75db1cc8bec5f4db72be541570f74fc5","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2ae155afe8a01cc0ae612d99117cf8ef16692ba7c4366590156fdec1bcf2d8c","impliedFormat":1},{"version":"3f5e5d9be35913db9fea42a63f3df0b7e3c8703b97670a2125587b4dbbd56d7c","impliedFormat":1},{"version":"8caeb65fdc3bfe0d13f86f67324fcb2d858ed1c55f1f0cce892eb1acfb9f3239","impliedFormat":1},{"version":"57c23df0b5f7a8e26363a3849b0bc7763f6b241207157c8e40089d1df4116f35","affectsGlobalScope":true,"impliedFormat":1},{"version":"3b8bc0c17b54081b0878673989216229e575d67a10874e84566a21025a2461ee","impliedFormat":1},{"version":"5b0db5a58b73498792a29bfebc333438e61906fef75da898b410e24e52229e6f","impliedFormat":1},{"version":"dbe055b2b29a7bab2c1ca8f259436306adb43f469dca7e639a02cd3695d3f621","impliedFormat":1},{"version":"1678b04557dca52feab73cc67610918a7f5e25bfdba3e7fa081acd625d93106d","impliedFormat":1},{"version":"e3905f6902f0b69e5eefc230daa69fdd4ab707a973ec2d086d65af1b3ea47ef0","impliedFormat":1},{"version":"2ea729503db9793f2691162fec3dd1118cab62e96d025f8eeb376d43ec293395","impliedFormat":1},{"version":"9ec87fea42b92894b0f209931a880789d43c3397d09dd99c631ae40a2f7071d1","impliedFormat":1},{"version":"c68e88cdfadfb6c8ba5fc38e58a3a166b0beae77b1f05b7d921150a32a5ffb8d","impliedFormat":1},{"version":"2bc7aa4fba46df0bd495425a7c8201437a7d465f83854fac859df2d67f664df3","impliedFormat":1},{"version":"41d17e1ad9a002feb11c8cdd2777e5bbc0cdb1e3f595d237e4dded0b6949983b","impliedFormat":1},{"version":"07e4e61e946a9c15045539ecd5f5d2d02e7aab6fa82567826857e09cf0f37c2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c4714ccc29149efb8777a1da0b04b8d2258f5d13ddbf4cd3c3d361fb531ac86","impliedFormat":1},{"version":"3ff275f84f89f8a7c0543da838f9da9614201abc4ce74c533029825adfb4433d","impliedFormat":1},{"version":"0eb5d0cbf09de5d34542b977fd6a933bb2e0817bffe8e1a541b2f1ad1b9af1ff","impliedFormat":1},{"version":"f9713757bcdfa4d58b48c0fb249e752c94a3eee8bf4532b906094246ac49ef88","impliedFormat":1},{"version":"2c2bdaa1d8ead9f68628d6d9d250e46ee8e81aa4898b4769a36956ae15e060fe","impliedFormat":1},{"version":"c32c840c62d8bd7aeb3147aa6754cd2d922b990a6b6634530cb2ebdce5adc8e9","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"82b91e4e42e6c41bc7fc1b6c2dc5eba6a2ba98375eb1f210e6ff6bba2d54177e","impliedFormat":1},{"version":"6fe28249ac0c7bc19a79aa9264baf00efbd080e868dbe1d3052033ad1c64f206","affectsGlobalScope":true,"impliedFormat":1},{"version":"cbed824fec91efefc7bbdcb8b43d1a531fdbebd0e2ef19481501ff365a93cb70","impliedFormat":1},{"version":"d0716593b3f2b0451bcf0c24cfa86dec2235c325c89f201934248b7c742715fc","impliedFormat":1},{"version":"ec501101c2a96133a6c695f934c8f6642149cc728571b29cbb7b770984c1088e","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"2991bca2cc0f0628a278df2a2ccdb8d6cbcb700f3761abbed62bba137d5b1790","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"230763250f20449fa7b3c9273e1967adb0023dc890d4be1553faca658ee65971","impliedFormat":1},{"version":"c3e9078b60cb329d1221f5878e88cecfa3e74460550e605a58fcfb41a66029ff","impliedFormat":1},{"version":"a74edb3bab7394a9dbde529d60632be590def2f5f01024dbd85441587fbfbbe0","impliedFormat":1},{"version":"0ea59f7d3e51440baa64f429253759b106cfcbaf51e474cae606e02265b37cf8","impliedFormat":1},{"version":"bc18a1991ba681f03e13285fa1d7b99b03b67ee671b7bc936254467177543890","impliedFormat":1},{"version":"00049ccc87f3f37726db03c01ca68fe74fd9c0109b68c29eb9923ebec2c76b13","impliedFormat":1},{"version":"fa94bbf532b7af8f394b95fa310980d6e20bd2d4c871c6a6cb9f70f03750a44b","impliedFormat":1},{"version":"68d3f35108e2608b1f2f28b36d19d7055f31c4465cc5692cbd06c716a9fe7973","impliedFormat":1},{"version":"a6d543044570fbeed13a7f9925a868081cd2b14ef59cdd9da6ae76d41cab03d3","affectsGlobalScope":true,"impliedFormat":1},{"version":"7fa2214bb0d64701bc6f9ce8cde2fd2ff8c571e0b23065fa04a8a5a6beb91511","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"eab2f3179607acb3d44b2db2a76dd7d621c5039b145dc160a1ee733963f9d2f5","impliedFormat":1},{"version":"841983e39bd4cbb463be385e92fda11057cab368bf27100a801c492f1d86cbaa","impliedFormat":1},{"version":"6f5383b3df1cdf4ff1aa7fb0850f77042b5786b5e65ec9a9b6be56ebfe4d9036","impliedFormat":1},{"version":"62fc21ed9ccbd83bd1166de277a4b5daaa8d15b5fa614c75610d20f3b73fba87","impliedFormat":1},{"version":"e4156ddb25aa0e3b5303d372f26957b36778f0f6bbd4326359269873295e3058","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc1b433a84cae05ddc5672d4823170af78606ad21ecef60dbc4570190cbf1357","impliedFormat":1},{"version":"9d3821bc75c59577e52643324cec92fc2145642e8d17cf7ee07a3181f21d985d","impliedFormat":1},{"version":"7f78cfb2b343838612c192cb251746e3a7c62ac7675726a47e130d9b213f6580","impliedFormat":1},{"version":"201db9cf1687fab1adf5282fcba861f382b32303dc4f67c89d59655e78a25461","impliedFormat":1},{"version":"c77fb31bc17fd241d3922a9f88c59e3361cdf76d1328ba9412fc6bf7310b638d","impliedFormat":1},{"version":"0a20eaf2e4b1e3c1e1f87f7bccb0c936375b23b022baeea750519b7c9bc6ce83","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"a16b91b27bd6b706c687c88cbc8a7d4ee98e5ed6043026d6b84bda923c0aed67","impliedFormat":1},{"version":"694b812e0ed11285e8822cf8131e3ce7083a500b3b1d185fff9ed1089677bd0a","impliedFormat":1},{"version":"99ab6d0d660ce4d21efb52288a39fd35bb3f556980ec5463b1ae8f304a3bbc85","impliedFormat":1},{"version":"6eeded8c7e352be6e0efb83f4935ec752513c4d22043b52522b90849a49a3a11","impliedFormat":1},{"version":"6c1ad90050ffbb151cacc68e2d06ea1a26a945659391e32651f5d42b86fd7f2c","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"446a50749b24d14deac6f8843e057a6355dd6437d1fac4f9e5ce4a5071f34bff","impliedFormat":1},{"version":"182e9fcbe08ac7c012e0a6e2b5798b4352470be29a64fdc114d23c2bab7d5106","impliedFormat":1},{"version":"2f4e6b4d39426a1b85ecf4bdeb9dddbf4d9b3397d95d8555d46f925c9519ec7d","impliedFormat":1},{"version":"78a2869ad0cbf3f9045dda08c0d4562b7e1b2bfe07b19e0db072f5c3c56e9584","impliedFormat":1},{"version":"89d5d28d4f57e000b836ac273079be1b75710e28ce14750d081fb420d37e2ca5","impliedFormat":1},{"version":"fd4e24ccff3966390600d7f5d6aa1fed5a512e92ada735ea5fbc933d313ad3d3","impliedFormat":1},{"version":"b7cddfe1aa6b86b5fad3c9ccb30d05b3ccb165aebbf112f48d2d8a5f69dd98b1","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"bd2c7ada3dee03653d3f601011d30072194bc3970cd93208f9588fbdc0c69347","impliedFormat":1},{"version":"e480da45d32313e7174b265674da504f075f59ef326852f0c5a5d863b438ae85","impliedFormat":1},{"version":"ad54850f61fcf5d014e11be80d2f46fea9265cfa7e77456da876f7833ef81769","impliedFormat":1},{"version":"6f7c9e8bd2b5b6a080b07080065f94900bd3c7e5ebbd3047bc33fcce2fab1dd8","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"da5950ee2a90721df6f3fba45f5d05308f7e4c35835392215dd2cd404505e2de","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"f42d5fed19610d485c646a0c430e768115567d078c7fc855c57b0c578b3d6cd3","impliedFormat":1},{"version":"ee8df1cb8d0faaca4013a1b442e99130769ce06f438d18d510fed95890067563","impliedFormat":1},{"version":"d5630f2ad9b4541e5ce891648121022f9412ecdca1820baa1f0104f70fd7eff7","impliedFormat":1},{"version":"4d15375ab13497104bc8fe56fdef2b5fd6853f29255737d23a33fa306ff7fd69","impliedFormat":1},{"version":"2cd3fc1d0d6a1e85baffd2d4f50f5efb192b5446eef567e97c94765402f0aad4","impliedFormat":1},{"version":"e4cbf2f1e89ecccaddd2c045e600ae41b732295953fb06247c7dcbc2d281ed30","impliedFormat":1},{"version":"6dcedaef57dff0d79a05ab0ab602cde74db803d1e765468bf91263786a383e1b","impliedFormat":1},{"version":"8c1697d90c394a6fd955b98eae01238eff628e129b987a68aea10f898a48e7da","impliedFormat":1},{"version":"7580e62139cb2b44a0270c8d01abcbfcba2819a02514a527342447fa69b34ef1","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"d10d63718e1646c2279e3b33831f82c60e31f622b2b7020f1196409ca4c09242","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"148679c6d0f449210a96e7d2e562d589e56fcde87f843a92808b3ff103f1a774","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"02436d7e9ead85e09a2f8e27d5f47d9464bced31738dec138ca735390815c9f0","impliedFormat":1},{"version":"f8d5ff8eafd37499f2b6a98659dd9b45a321de186b8db6b6142faed0fea3de77","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"a22dd55aa4d39906252000ab8e8a1b83b195eef7f4274eb51e457c1f11cf6580","impliedFormat":1},{"version":"540cc83ab772a2c6bc509fe1354f314825b5dba3669efdfbe4693ecd3048e34f","impliedFormat":1},{"version":"121b0696021ab885c570bbeb331be8ad82c6efe2f3b93a6e63874901bebc13e3","impliedFormat":1},{"version":"612d9da66bb046a9c1e2e8d026245ded881fc4b9f98cbfae714415d57ee0ae0b","impliedFormat":1},{"version":"32c2ad9494dad5d11b0564a619fee18f388db6c1e9e2cd3c360b3122549691eb","impliedFormat":1},{"version":"6c301d40aec56a74ec7bd7324e31a728dadf9bfba3e96def02938d3d973534ec","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"aa14cee20aa0db79f8df101fc027d929aec10feb5b8a8da3b9af3895d05b7ba2","impliedFormat":1},{"version":"493c700ac3bd317177b2eb913805c87fe60d4e8af4fb39c41f04ba81fae7e170","impliedFormat":1},{"version":"aeb554d876c6b8c818da2e118d8b11e1e559adbe6bf606cc9a611c1b6c09f670","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"25a5f6fd3a2243c859eddc99ab5fba11d970af2fe7a5df9c32b7668f76f97b01","impliedFormat":1},{"version":"8d207e1f9d2c30d6f77dfa693f3827c3fbf0d89240297e10bdfe1041d433df68","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"8c70ddc0c22d85e56011d49fddfaae3405eb53d47b59327b9dd589e82df672e7","impliedFormat":1},{"version":"2f9c89cbb29d362290531b48880a4024f258c6033aaeb7e59fbc62db26819650","impliedFormat":1},{"version":"a365c4d3bed3be4e4e20793c999c51f5cd7e6792322f14650949d827fbcd170f","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"273782b8454e78f6a8b30d2cfbf6860499c930595095fcc1689637115f0eddda","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fbdd025f9d4d820414417eeb4107ffa0078d454a033b506e22d3a23bc3d9c41","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"9f9bb6755a8ce32d656ffa4763a8144aa4f274d6b69b59d7c32811031467216e","impliedFormat":1},{"version":"5c32bdfbd2d65e8fffbb9fbda04d7165e9181b08dad61154961852366deb7540","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"0c05e9842ec4f8b7bfebfd3ca61604bb8c914ba8da9b5337c4f25da427a005f2","impliedFormat":1},{"version":"faed7a5153215dbd6ebe76dfdcc0af0cfe760f7362bed43284be544308b114cf","impliedFormat":1},{"version":"7029e566b8df176f703fb59fd437a38670c7a0e02c58b2d66dfb5b2e2b2defdb","impliedFormat":1},{"version":"7f2aa4d4989a82530aaac3f72b3dceca90e9c25bee0b1a327e8a08a1262435ad","impliedFormat":1},{"version":"d96b39301d0ded3f1a27b47759676a33a02f6f5049bfcbde81e533fd10f50dcb","impliedFormat":1},{"version":"e9f147ecca73d9346a4c073432843c159ccbe50bdcb678a78f6da10eae2cecf4","impliedFormat":1},{"version":"de061f7d72bd65c06fc1419f841dfdcb29a8e22fe6fa527d1e6eb20b897d4de0","impliedFormat":1},{"version":"663beafc2446079574570cba86e9b15f986f908ddb1b01274509970126fee945","impliedFormat":1},{"version":"a3102887d5058bf4cb5b37fa6964c09e9527c42053b3b5c642b89878620748de","impliedFormat":1},{"version":"0aaaa1727edd29673d85c9b26d7ca4d54e5407a48586903c51b48b7f7d196f61","impliedFormat":1},{"version":"d35bca0b261bff02635758c48e8ab99c61c420d0dfabbcf467e847171d876b7d","impliedFormat":1},{"version":"3bc12c40d90c342ff88a3d876996c555ed5cbee5fe8c3308a240b321f401ee46","impliedFormat":1},{"version":"ba130768aae855a5477e9e148e5c879548e6e7ccbcc56fd1934c8a18ea5b7569","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d38530db0601215d6d767f280e3a3c54b2a83b709e8d9001acb6f61c67e965fc","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"b499af2054a037a162b3b72cd886f48bbf32a3502c865c6e29fac7d2ab3ce0b5","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"48773ca557b0319c2ee62ae249cf52a81709e8be139920d6479a66274de7c4ed","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"4cceef18d7f088e797a463e90b7a9dad10c6bc667724b7686e3e740ae00122be","impliedFormat":1},{"version":"7ee86fbb3754388e004de0ef9e6505485ddfb3be7640783d6d015711c03d302d","impliedFormat":1},{"version":"cc1954b539604b1e562319119ac7e888172208b32ca873f9a357a92c826bd046","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"bb8f2dbc03533abca2066ce4655c119bff353dd4514375beb93c08590c03e023","impliedFormat":1},{"version":"706dd95827e7ebaabda91d5db2b755233e0952d98570e9c032b0f066a15c1177","affectsGlobalScope":true,"impliedFormat":1},{"version":"0b103e9abfe82d14c0ad06a55d9f91d6747154ef7cacc73cf27ecad2bfb3afcf","impliedFormat":1},{"version":"990b8fad2327b77e6920cc792af320e8867e68f02ce849b12c0a6ab9a1aebb09","impliedFormat":1},{"version":"5eb8cd1cb0c9143d74a8190b577c522720878c31aef67d866fcd29973f83e955","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"a6805fcafed712aea7759f8bc731014f9d22738c1d6ef9d43b8091d1d48346d5","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"d88ea80a6447d7391f52352ec97e56b52ebec934a4a4af6e2464cfd8b39c3ba8","impliedFormat":1},{"version":"142617b3cdf902b69c6464c9fbd942b60ab3e733ca18c032b19e0f7e2adbefe8","impliedFormat":1},{"version":"0b603555f1881f87256ffd6344d3e3ed6d466c2e701eabf381f28be8c2125892","impliedFormat":1},{"version":"897e4f7662488e3ecc79e743bdd3b78f13bdb69a97851afa5b440c4211e32ea9","impliedFormat":1},{"version":"e2e1c6d3b2d93add5200bd7bc1a8cccb4e446836b2111ece45db8683a2c765de","impliedFormat":1},{"version":"251b03d5cd243854ce870d9a9a39f491faf69898c5d6b5eee28cc7649c57417b","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"2c4de79f406d137390608e8c0a44fba2ff8e00bacfcae7c9d1781fef10e9440d","impliedFormat":1},{"version":"07ba23a10465791be5d22deaf5ef7de7658774ddff53721e5ea17fedea1bc721","impliedFormat":1},{"version":"dca8c645c5afeb03b1ecedbf16323f33e7d0afaa6256c8e047e6e38087a97f53","impliedFormat":1},{"version":"775f181bd4a533d6f8b5e55ec1d9f1624559720ae8a70e9432258da26b38d27c","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"9109a1291dd4b9f1541bea81ee11c247a2ca9e1ea89f87f13aa1811c3c069616","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"622694a8522b46f6310c2a9b5d2530dde1e2854cb5829354e6d1ff8f371cf469","impliedFormat":1},{"version":"cd8ce8d68567f62dd580b3c3c37777ac3f5b81944c7417f5ea83030eab533385","impliedFormat":1},{"version":"e374d1eaa05b7dc38580062942ac8351ce79cbe11f6dbce4946a582a5680582d","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9e6c0ff3f8186fccd05752cf75fc94e147c02645087ac6de5cc16403323d870","impliedFormat":1},{"version":"49af4b52f0d4d2304c5f2c6fe5fab3e153e0acc38830d0202821b877c097dd02","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"e68b8e5a1df7c1be2bc105141456ecba70215806e1c28bfbc5c12bfce4be6e68","impliedFormat":1},{"version":"511c8f02329808d47d00b859c532ae9115590048b17325a946c74dac48428650","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"b5f9e66625783eefcbe3d2da074b2e7ba2066d61ce3fc6ef4f22805ad946cab4","impliedFormat":1},{"version":"e37115962d284b9f7a37c2bdd2add50f88365dde41f5e0ff591ffc48a8ec7575","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"f89488602bec98a142072fae7ea5ba99431a569ff580c64b7be39896474799d8","impliedFormat":1},{"version":"bbbc47961f39a57df103cf4ca3bb8f8732b4b6678a18225a0aa76d59c466956c","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"2ffb043dc5163458e473b7010859f86e01dc4edffcae0a93d885d028b426a546","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"05c7280d72f3ed26f346cbe7cbbbb002fb7f15739197cbbee6ab3fd1a6cb9347","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"04b7b2e0832dfd3c31e81df3975e8d8fda28e7ff999b0aa2932608a8f6661d5c","impliedFormat":1},{"version":"ca2d34c6ed5cbd3070b8b6f32f42ae54adcc6499c1e4b99f0a5798b3f27cc653","impliedFormat":1},{"version":"9ec68995e66dd6b9dac834bf5ae85fde802714ea2e82151a5d1d53ef01b463ef","impliedFormat":1},{"version":"5c4d626b4902f2ef8a1cc146d761d276cef988016dc674e3b98fbad70e64bc9f","impliedFormat":1},{"version":"fdfaa0aad899524962e2955287b5b991ffe3be50f64e02eb60c933ca44644a94","impliedFormat":1},{"version":"53c972a0f9bc3a4ec70fff7314123ea8cfcf75b3703046f767d2dc1eea87b2fb","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"d130c5f73768de51402351d5dc7d1b36eaec980ca697846e53156e4ea9911476","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"7303b45138d2511035056a5901a1490ebdcbf055cbb1276f8629c5121cbe733e","impliedFormat":1},{"version":"27f874cd5327507eeff699a74567f60c1215b94509f4308633a7b01922471ed2","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"2c6cf04bc525caf6546e859e8ef10bfb9573837ec0bc5ec7b53a7b1b8ca72781","impliedFormat":1},{"version":"8695dec09ad439b0ceef3776ea68a232e381135b516878f0901ed2ea114fd0fe","impliedFormat":1},{"version":"304b44b1e97dd4c94697c3313df89a578dca4930a104454c99863f1784a54357","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"87cc05fe13108f02e12da7e3efd8e360fef78d96a0c9e11408ea1b1b9fb3e03d","impliedFormat":1},{"version":"1abbf67c218d23c2ce76887caac2df6c7dab3d97ba2b65348432b876f510002a","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"c06ef3b2569b1c1ad99fcd7fe5fba8d466e2619da5375dfa940a94e0feea899b","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"8bd496cf710d4873d15e4891a5dbf945673e3321ca74cf75187e347fd5ed295e","impliedFormat":1},{"version":"a6dba407fc287f1e25454e75028c91bbc00675f2d1c4e8b3edcc36c08611a486","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"c0723195c85e19656d6b5b9fdb81d3f3403c1ae4679e722c6ea058c516b38d12","impliedFormat":1},{"version":"186eea74805194f04e41038fc5eca653788b9dedbab7c2d7d17e10139622dd92","impliedFormat":1},{"version":"71d9eb4c4e99456b78ae182fb20a5dfc20eb1667f091dbb9335b3c017dd1c783","impliedFormat":1},{"version":"cfa846a7b7847a1d973605fbb8c91f47f3a0f0643c18ac05c47077ebc72e71c7","impliedFormat":1},{"version":"1594da19968752a22b2ac48c2d0e60575700e745c577a8a4a676b841238ad5bb","impliedFormat":1},{"version":"e0cee12109e0a10a4c3d6769fcc7644b7c1ea7f52365bea51728f5af29f8a137","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"3536968defef8a75514f547ead5e2e9c1e984820290ec9b00c5fdfb6ef786535","impliedFormat":1},{"version":"d83773870080c30a230e322ce13a9c6f3398e8dacea4ea8a83e26370f3bac23e","impliedFormat":1},{"version":"dcfeaf98d66314fec29a9076c4290e45d0b196a65827becc19138e9c7b855f37","impliedFormat":1},{"version":"6849fe9210fe4946d5f085bfed36758f33dc6ae15a751338d178dd4daa017c46","impliedFormat":1},{"version":"888cda0fa66d7f74e985a3f7b1af1f64b8ff03eb3d5e80d051c3cbdeb7f32ab7","impliedFormat":1},{"version":"60681e13f3545be5e9477acb752b741eae6eaf4cc01658a25ec05bff8b82a2ef","impliedFormat":1},{"version":"ffae4e1e06aa848a1e4bcef162cd1c48e5909b26223515981310af9c036bdfc7","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"34e16eb7c31768a11a08aebcfb3d70d7b8f0b016197e98d8419e566ceae6d6c8","impliedFormat":1},{"version":"f94ec1f7e4b709d26960306c9082a7a1b728a6e13089346aa48ba57c74cbf47e","impliedFormat":1},{"version":"9a11cb4033405e96c247cd5aa29790212aaffdd127869e8a5219103f0b389fd5","impliedFormat":1},{"version":"01479d9d5a5dda16d529b91811375187f61a06e74be294a35ecce77e0b9e8d6c","impliedFormat":1},{"version":"aff5213585cb72e94054dfe17250ff315f3569b3919d1ef1ad235f37c4ee894e","impliedFormat":1},{"version":"fb2ea35e1be6388d722d7725e2b49c697d34d9c890c3b96758faaeb86d35cef8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"1a4dc28334a926d90ba6a2d811ba0ff6c22775fcc13679521f034c124269fd40","impliedFormat":1},{"version":"f05315ff85714f0b87cc0b54bcd3dde2716e5a6b99aedcc19cad02bf2403e08c","impliedFormat":1},{"version":"5fad3b31fc17a5bc58095118a8b160f5260964787c52e7eb51e3d4fcf5d4a6f0","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"456006a6975b26c0a1785feddae165f6d307e2d601ffde27e21fc4a790e448a4","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"1fe0d18b111e1145a7e7601855bccd4ca20f24e3b9a5aba6bb1fa9d1a7059170","impliedFormat":1},{"version":"5632c3c26d420c063eebe64c45b1248b9492a67bf44f1d0c57e9dc8f6cf449bb","impliedFormat":1},{"version":"0df5aa619ab12993a39ea6dae062ee46eadbb4d738916460e636ada52bced75b","impliedFormat":1},{"version":"8fca3039857709484e5893c05c1f9126ab7451fa6c29e19bb8c2411a2e937345","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"10ab7be91f87ebe8916b62cf28af2e45b5601fc7b0e311adf838f912c6b31dd8","impliedFormat":1},{"version":"bc636fbc08e0979ceb7eb0731a33000283d77a33b62e1f71ee65be50394e40ba","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"75bbd3be047d539988a0ff0b56384ef7a6a25f3b676ad96bee547d44c31622a7","impliedFormat":1},{"version":"42960001a776b089ade681ab5cfddc936e0afb0615133ec1841f3dee89d3e1bf","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"da47712b394d944328245482603bc6f416d3949b67c9392279caab595076b510","affectsGlobalScope":true,"impliedFormat":1},{"version":"37d0071d8f0a06dc55c2c5e0ec3391affd4fd107c53410bf358196ec0bf3923f","impliedFormat":1},{"version":"b213dad76ca37fd552274c9499056e1c0d9c1bd38a55bb7f68b22ba6b84c3ad7","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"5a49adaef698b7ad7e6127949fa1b0bbd3d46b7cbd11c54e392a4dcdd51f5190","impliedFormat":1},{"version":"96171c03c2e7f314d66d38acd581f9667439845865b7f85da8df598ff9617476","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"2489bf04d77dc025ba67f49f1a56eb24b9db477d5ff88123d887e163ed1776aa","impliedFormat":1},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"0b77b819b5417775fccb20c678293cf614c054a5b1a65421a5b933a9124ba998","impliedFormat":1},{"version":"e1f6076688a95bd82deaac740fccbe3cdea0d8a22057cccc9c5bce4398bdd33b","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"b1f1d57fde8247599731b24a733395c880a6561ec0c882efaaf20d7df968c5af","impliedFormat":1},{"version":"6715dc4eb59c8ea9abe2b78c235ed331dc710a06fe56798868dbc4d40cd1b707","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"5a3ea721d03a361ccbdd7390ccd75f6e84cbca3a3f01f4b331ecc9af31890c49","impliedFormat":1},{"version":"e7dfaee4af38d45b1cab8a1ee0b3bc1f85ddcf64545ed391d675d78ae6526274","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8daa443eaf9a27fd382cc1f8ebe30330c0f4d89511cfb469166874806751d35","impliedFormat":1},{"version":"af48e58339188d5737b608d41411a9c054685413d8ae88b8c1d0d9bfabdf6e7e","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"1de8c302fd35220d8f29dea378a4ae45199dc8ff83ca9923aca1400f2b28848a","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"332248ee37cca52903572e66c11bef755ccc6e235835e63d3c3e60ddda3e9b93","impliedFormat":1},{"version":"94e8cc88ae2ef3d920bb3bdc369f48436db123aa2dc07f683309ad8c9968a1e1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"07ed3ddab975995eea41b22f3010506fb9f5fb301d04820b07d7a1aee5477d7c","impliedFormat":1},{"version":"969d8b0965849f4bae7cab0ba90bd1e1220e95999c2c6f01117fa7500901c017","impliedFormat":1},{"version":"6ec840ee5e2bc103f557fe38b1d585ee250540468713d7634ee066de372bf332","impliedFormat":1},{"version":"b0309e1eda99a9e76f87c18992d9c3689b0938266242835dd4611f2b69efe456","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"6ceb10ca57943be87ff9debe978f4ab73593c0c85ee802c051a93fc96aaf7a20","impliedFormat":1},{"version":"1de3ffe0cc28a9fe2ac761ece075826836b5a02f340b412510a59ba1d41a505a","impliedFormat":1},{"version":"e46d6cc08d243d8d0d83986f609d830991f00450fb234f5b2f861648c42dc0d8","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"ff863d17c6c659440f7c5c536e4db7762d8c2565547b2608f36b798a743606ca","impliedFormat":1},{"version":"5412ad0043cd60d1f1406fc12cb4fb987e9a734decbdd4db6f6acf71791e36fe","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"e297c0a524edee7677939122f90027bfbe5f2698939d9a85728e5044b39c7124","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"b62381cae176db34f003cc6172ee8f3e0122014889d66391aa73698105cf4934","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"41eb514d9ce0a6e87957f08a4b7af70d93f87637f37dee706e2d92a6601c25a9","impliedFormat":1},{"version":"e7765aa8bcb74a38b3230d212b4547686eb9796621ffb4367a104451c3f9614f","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"7bd01f0f28cd3aeb2046274d85208e245965f6f2948edf4f7b2057bcf9f22ccc","impliedFormat":99},{"version":"d2f2cf2b8cc92bea913cda4a076e0f790b23a21e84f989d12f0116a7fe3906e0","impliedFormat":99},{"version":"6de125ea94866c736c6d58d68eb15272cf7d1020a5b459fea1c660027eca9a90","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5b20bc288ee49989c95b20847fc93b96bf61cc0845598897a6a53a967dd7d07","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"d3b315763d91265d6b0e7e7fa93cfdb8a80ce7cdd2d9f55ba0f37a22db00bdb8","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},{"version":"bb6d9c2b075a5c675259c75950c5f2359b17bf031b8cbdff8c04aee66a6a514f","affectsGlobalScope":true},"7ad303e40d4fddf44f156129e397511953a71481c5cfd86b1862649aaaf240cc",{"version":"168aa44771e55cda628eeb5fbb6f626f6f263d827967b119e8c06abd7f1362ec","signature":"435a1e418e8338be3f39614b96b81a9aa2700bc8c27bc6b98f064ff9ce17c363"},{"version":"3b89216a7e38a454985ad17bb2ff85792837dc812f2a89fa5f60ad0a2e216fa7","impliedFormat":99},{"version":"16fe60bb544cfedfd2b5bb2f7d0b3957be7978706d57d9f06edc9c0c8dbdba23","impliedFormat":99},{"version":"82179358c2d9d7347f1602dc9300039a2250e483137b38ebf31d4d2e5519c181","impliedFormat":99},{"version":"c73fdf42528325dd17940937ed787b15ae3445c6a2dae1a2b74bc4d87d337ca2","impliedFormat":99},{"version":"e8e17dfef3cfa9f0847ac93dd535a9896af7fb57c1a1b164484bb1b0ee4a25d8","impliedFormat":99},{"version":"51d2ffea2d1ee4a81c775938588c1e16620281adb60cbc26579a2fc6baa10bd2","impliedFormat":99},{"version":"148debd12783ded0a60d115daeacd8136f77757ae89a05c4e18de6dd77646fd2","impliedFormat":99},{"version":"0088b02dca63c47b273a140d0a3944bdc6dc2eb765fff0ca98e3c3a2786b3a5a","impliedFormat":99},{"version":"a651d06b780fa354231f19b040cbcde484bede3218885752b4f9e9a8f72d3b5f","impliedFormat":99},{"version":"06e26f75bed4c8389a8a63f0e6d6a9068038873dc95d8d1338e8c370a0ae8bc3","impliedFormat":99},{"version":"a2155e2675fd1af52b0b70779371c28611cdd1076b29d0f68bf93b983e5ddce0","impliedFormat":99},{"version":"a413e4b0b99280e1e58f5fe7b2b585e8a9be4996df8c58585399c9e2ca8a683e","impliedFormat":99},{"version":"609ab2c225766bc0851251c1db0fd5492673e190074045d21dc5dc7c3c46d785","impliedFormat":99},{"version":"c074e054c9db79055d37d7d70131e9a3234b8186773b3edb617c13f80bcf8774","impliedFormat":99},{"version":"7d3e062a778b8f5ea4f0cac7e925e31f88e6739812ebc5f827474324a4048f14","impliedFormat":99},{"version":"7f3857dc5cfe1e5e977edb14e931d9939a952e8e41997263a927f8f0299ea652","impliedFormat":99},{"version":"3559624d0102d10d7765c292c60ccbc229541534db32061e06df88bfe1064636","impliedFormat":99},{"version":"5a9834c603c65aee5cba0c1d6b3c7aee85cdc7862832a23165c6aa4139c165f2","impliedFormat":99},{"version":"a7d7b5fa83cd7b3b4c2aa73bc29e7cbd53d5690b74f6fb39a5558af0a94967ba","impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","impliedFormat":99},{"version":"605450898939e8abce51e8085a41b60640278337a969c33cd6b169e7c4f9c3f2","impliedFormat":99},{"version":"e0864480ea083087d705f9405bd6bf59b795e8474c3447f0d6413b2bce535a09","impliedFormat":99},{"version":"e67cbea16f1994af89efd700542dbf3828a46a52b29e4d67e801bd7869dc103c","impliedFormat":99},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":99},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"e3cf0611709328b449ec13f8c436712d62003620ce480139fae46ce001c2ee9f","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"3fd8a5aefd8c3feb3936ca66f5aa89dff7bf6e6537b4158dbd0f6e0d65ed3b9e","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"41c41c6e90133bb2a14f7561f29944771886e5535945b2b372e2f6ed6987746e","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":99},{"version":"f093d4bd6a9267be5f8ecbfbca19f4f3359b3839883206150c5d833606569e84","impliedFormat":99},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":99},{"version":"4a13397dffad4475c45c70fde584c925fe8c9218b3c7ab94397b68fc434f63b6","impliedFormat":99},{"version":"2faebfa830ae4cfbfb58e48b0ec20a2a63882d776f0ca36ec7155d45cf1b7f2d","impliedFormat":99},{"version":"b478fad6cb2c66bfbfc027983240b416a7733013f878056ba92cf809020018a0","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"9443967db823b66d1682be7fc66392be7c7924e10c3e54900f456341e94591a6","impliedFormat":99},{"version":"424f71d1fae96ac2e878af92345bb87bea1d29f757228fbc190133b305643f2c","impliedFormat":99},{"version":"61bb64660ee150f3ab618340e15cca0a81664801bede7c966ca0eca3a952fe63","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"61fd6c17235d530c40f543dd7c40afab091d91c1ef890baeed30db6d82b04b28","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"091767bc841f937654ed597d49e023ed59850355e746ae1a6f20ab31076ee1fb","impliedFormat":99},{"version":"19c6d6135af59693698d384050b45a8a049493500add442f58e4bd7c8a255ab6","impliedFormat":99},{"version":"6a0dba12d55314638a8c51108b20fe2f68f1364a619d098918bda91c22dec154","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"ed9bb55ddcbebd5cb3eee991f57ff21438546ee40ee1c310281bd12a6c7cf65b","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"437b7613a30a2fcde463f7b707c6d5567a8823fbc51de50b8641bf5b1d126fad","impliedFormat":99},{"version":"63ea959e28c110923f495576e614fb8b36c09b6828b467b2c7cd7f03b03ccf9f","impliedFormat":99},{"version":"1601a95dbb33059fc3d12638ed2a9aecff899e339c5c0f3a0b28768866d385b4","impliedFormat":99},{"version":"56fc978580577d30f4c2cdb5b1eb9217b66ed66537dd27141256f426e4b8dd68","impliedFormat":99},{"version":"2c5413050a2580becf9d82dd7e3006b95623e96f145356bf73230cd635352f70","impliedFormat":99},{"version":"860bedc71ead192ea4a0ea5ef4686e65724d14b391ebd1a6671a7044e6bd8e15","impliedFormat":99},{"version":"7c0a845bee4a084cbb8654709f48e5f13e2f6d45e5e2dde7c57cadf79fd9e3d5","impliedFormat":99},{"version":"07ad8a597ac75084e3dd9f9fadf5e8d7ccdcfe2f0c94ea0cf1cd8aa027a6c46e","impliedFormat":99},{"version":"94ddb4a2bb0c69e8efea22c58c2b6f84017eba469a4e433f5396ea8619d051cb","impliedFormat":99},{"version":"064499a671b662b25675beccdd04fb0bdebb6bd49bdb90d448e4b1ce3db20526","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"ae7d986f19db00cd62ce8573307f910ec2103d7fc30df09cedeec3cabec13082","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"ae77d81a5541a8abb938a0efedf9ac4bea36fb3a24cc28cfa11c598863aba571","impliedFormat":1},{"version":"f329dfad7970297cbf07ddc8fce2ad4a24e2a3855917c661922ef86eb24dd1f1","impliedFormat":1},{"version":"841784cfa9046a2b3e453d638ea5c3e53680eb8225a45db1c13813f6ea4095e5","affectsGlobalScope":true,"impliedFormat":1},{"version":"646ef1cff0ec3cf8e96adb1848357788f244b217345944c2be2942a62764b771","impliedFormat":1},{"version":"3cfb7c0c642b19fb75132154040bb7cd840f0002f9955b14154e69611b9b3f81","impliedFormat":1},{"version":"8387ec1601cf6b8948672537cf8d430431ba0d87b1f9537b4597c1ab8d3ade5b","impliedFormat":1},{"version":"d16f1c460b1ca9158e030fdf3641e1de11135e0c7169d3e8cf17cc4cc35d5e64","impliedFormat":1},{"version":"a934063af84f8117b8ce51851c1af2b76efe960aa4c7b48d0343a1b15c01aedf","impliedFormat":1},{"version":"e3c5ad476eb2fca8505aee5bdfdf9bf11760df5d0f9545db23f12a5c4d72a718","impliedFormat":1},{"version":"462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","impliedFormat":1},{"version":"5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","impliedFormat":1},{"version":"d0570ce419fb38287e7b39c910b468becb5b2278cf33b1000a3d3e82a46ecae2","impliedFormat":1},{"version":"3aca7f4260dad9dcc0a0333654cb3cde6664d34a553ec06c953bce11151764d7","impliedFormat":1},{"version":"a0a6f0095f25f08a7129bc4d7cb8438039ec422dc341218d274e1e5131115988","impliedFormat":1},{"version":"b58f396fe4cfe5a0e4d594996bc8c1bfe25496fbc66cf169d41ac3c139418c77","impliedFormat":1},{"version":"45785e608b3d380c79e21957a6d1467e1206ac0281644e43e8ed6498808ace72","impliedFormat":1},{"version":"bece27602416508ba946868ad34d09997911016dbd6893fb884633017f74e2c5","impliedFormat":1},{"version":"2a90177ebaef25de89351de964c2c601ab54d6e3a157cba60d9cd3eaf5a5ee1a","impliedFormat":1},{"version":"82200e963d3c767976a5a9f41ecf8c65eca14a6b33dcbe00214fcbe959698c46","impliedFormat":1},{"version":"b4966c503c08bbd9e834037a8ab60e5f53c5fd1092e8873c4a1c344806acdab2","impliedFormat":1},{"version":"3d3208d0f061e4836dd5f144425781c172987c430f7eaee483fadaa3c5780f9f","impliedFormat":1},{"version":"34a8a5b4c21e7a6d07d3b6bce72371da300ec1aed58961067e13f1f4dc849712","impliedFormat":1},{"version":"4ffba3c5848b4fe62ee59b754fd5f256ad9656a0db6d37b9a2a8cb40dfc7ac21","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"59e5e964b84fdb2378e9455e4e59405030e4ed2b4c6f891ce395f17796af3cbb","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"90ba95a763101bb61b8a799731a2ed60b5016b8135c1a2d5186862d4b534d4a1","impliedFormat":99},{"version":"ad763fa0c24ede2b818eb6598c12dd581451f94688fb9ed963beba20d513a7ec","signature":"90ec9100c29e008c3d9194acd818e2cfa6dc6e177154bc8e10c5959aa35619ed"},{"version":"b7ca2f47522d4ea41e65ff92c4c6dd9c4c8260da7c456a7631a9c88dc056b4d0","impliedFormat":1},{"version":"4f01e4d0959f9125b89e5737eb1ca2bfa69fd6b7d6126eba22feb8b505b00cde","impliedFormat":1},{"version":"4363a1adb9c77f2ed1ca383a41fbab1afadd35d485c018b2f84e834edde6a2c7","impliedFormat":1},{"version":"1d6458533adb99938d041a93e73c51d6c00e65f84724e9585e3cc8940b25523f","impliedFormat":1},{"version":"b0878fbd194bdc4d49fc9c42bfeeb25650842fe1412c88e283dc80854b019768","impliedFormat":1},{"version":"a892ea0b88d9d19281e99d61baba3155200acced679b8af290f86f695b589b16","impliedFormat":1},{"version":"03b42e83b3bcdf5973d28641d72b81979e3ce200318e4b46feb8347a1828cd5d","impliedFormat":1},{"version":"8a3d57426cd8fb0d59f6ca86f62e05dde8bfd769de3ba45a1a4b2265d84bac5a","impliedFormat":1},{"version":"afc6e1f323b476fdf274e61dab70f26550a1be2353e061ab34e6eed180d349b6","impliedFormat":1},{"version":"7c14483430d839976481fe42e26207f5092f797e1a4190823086f02cd09c113c","impliedFormat":1},{"version":"828a3bea78921789cbd015e968b5b09b671f19b1c14c4bbf3490b58fbf7d6841","impliedFormat":1},{"version":"69759c42e48938a714ee2f002fe5679a7ab56f0b5f29d571e4c31a5398d038fe","impliedFormat":1},{"version":"6e5e666fa6adeb60774b576084eeff65181a40443166f0a46ae9ba0829300fcb","impliedFormat":1},{"version":"1a4d43bdc0f2e240395fd204e597349411c1141dd08f5114c37d6268c3c9d577","impliedFormat":1},{"version":"874e58f8d945c7ac25599128a40ec9615aa67546e91ca12cbf12f97f6baf54ff","impliedFormat":1},{"version":"da2627da8d01662eb137ccd84af7ffa8c94cf2b2547d4970f17802324e54defc","impliedFormat":1},{"version":"07af06b740c01ed0473ebdd3f2911c8e4f5ebf4094291d31db7c1ab24ff559aa","impliedFormat":1},{"version":"ba1450574b1962fcf595fc53362b4d684c76603da5f45b44bc4c7eeed5de045b","impliedFormat":1},{"version":"b7903668ee9558d758c64c15d66a89ed328fee5ac629b2077415f0b6ca2f41bc","impliedFormat":1},{"version":"c7628425ee3076c4530b4074f7d48f012577a59f5ddade39cea236d6405c36ba","impliedFormat":1},{"version":"28c8aff998cc623ab0864a26e2eb1a31da8eb04e59f31fa80f02ec78eb225bcd","impliedFormat":1},{"version":"78d542989bdf7b6ba5410d5a884c0ab5ec54aa9ce46916d34267f885fcf65270","impliedFormat":1},{"version":"4d95060af2775a3a86db5ab47ca7a0ed146d1f6f13e71d96f7ac3b321718a832","impliedFormat":1},{"version":"6708cd298541a89c2abf66cceffc6c661f8ee31c013f98ddb58d2ec4407d0876","impliedFormat":1},{"version":"2e90928c29c445563409d89a834662c2ba6a660204fb3d4dc181914e77f8e29d","impliedFormat":1},{"version":"84be1b8b8011c2aab613901b83309d017d57f6e1c2450dfda11f7b107953286a","impliedFormat":1},{"version":"d7af890ef486b4734d206a66b215ebc09f6743b7fb2f3c79f2fb8716d1912d27","impliedFormat":1},{"version":"7e82c1d070c866eaf448ac7f820403d4e1b86112de582901178906317efc35ad","impliedFormat":1},{"version":"c5c4f547338457f4e8e2bec09f661af14ee6e157c7dc711ccca321ab476dbc6d","impliedFormat":1},{"version":"223e233cb645b44fa058320425293e68c5c00744920fc31f55f7df37b32f11ad","impliedFormat":1},{"version":"1394fe4da1ab8ab3ea2f2b0fcbfd7ccbb8f65f5581f98d10b037c91194141b03","impliedFormat":1},{"version":"086d9e59a579981bdf4f3bfa6e8e893570e5005f7219292bf7d90c153066cdfc","impliedFormat":1},{"version":"1ea59d0d71022de8ea1c98a3f88d452ad5701c7f85e74ddaa0b3b9a34ed0e81c","impliedFormat":1},{"version":"cd66a32437a555f7eb63490509a038d1122467f77fe7a114986186d156363215","impliedFormat":1},{"version":"f53d243499acfacc46e882bbf0bf1ae93ecea350e6c22066a062520b94055e47","impliedFormat":1},{"version":"65522e30a02d2720811b11b658c976bff99b553436d99bafd80944acba5b33b4","impliedFormat":1},{"version":"76b3244ec0b2f5b09b4ebf0c7419260813820f128d2b592b07ea59622038e45c","impliedFormat":1},{"version":"66eb7e876b49beff61e33f746f87b6e586382b49f3de21d54d41313aadb27ee6","impliedFormat":1},{"version":"69e8dc4b276b4d431f5517cd6507f209669691c9fb2f97933e7dbd5619fd07b7","impliedFormat":1},{"version":"361a647c06cec2e7437fa5d7cdf07a0dcce3247d93fbf3b6de1dc75139ff5700","impliedFormat":1},{"version":"fe5726291be816d0c89213057cd0c411bb9e39e315ed7e1987adc873f0e26856","impliedFormat":1},{"version":"1b76990de23762eb038e8d80b3f9c810974a7ed2335caa97262c5b752760f11a","impliedFormat":1},{"version":"5e050e05fe99cd06f2d4ad70e73aa4a72961d0df99525e9cad4a78fa588f387b","impliedFormat":1},{"version":"4ff327e8b16da9d54347b548f85675e35a1dc1076f2c22b2858e276771010dd2","impliedFormat":1},{"version":"f767787945b5c51c0c488f50b3b3aeb2804dfd2ddafcb61125d8d8857c339f5a","impliedFormat":1},{"version":"14ab21a9aeff5710d1d1262459a6d49fb42bed835aa0f4cfc36b75aa36faddcd","impliedFormat":1},{"version":"ba3c4682491b477c63716864a035b2cfdd727e64ec3a61f2ca0c9af3c0116cfd","affectsGlobalScope":true,"impliedFormat":1},{"version":"b222d32836d745e1e021bb10f6a0f4a562dd42206203060a8539a6b9f16523f0","impliedFormat":1},{"version":"a3f6d8995864820a0207b7ef4ce1ed6a8dd2fccc7e70d015da15034807c38e1c","impliedFormat":1},{"version":"651df11341eff0b769fb83af75b1872e6cedf406674c5eaa2650551aceb5a816","impliedFormat":1},{"version":"774a466295d26eddab911b9f567040364e7b7d0eb8003ad3bfc92b97eeecf066","signature":"f15b4a91c10bf30ff3708a5e3968c0a52ca4e86a4b9a5b1c4fc8e9b5f1292f21"},{"version":"1e9d4c3b066e0228765cef074a9bf49d0b8d3af461c97f2c511e8f7110b56235","signature":"e59faabf094dd75dcf08847ca1b8ae16daf269ca02c744c521b38ed5d297578c"},{"version":"48d3d3a869cf85c67c62d87c031946cb9ae89fc59d3d0b274d17c88097e9847d","signature":"16e6aa6706cf2bfdef5a587057b3b672099a2dd478584f26aacc4dd07336d0a0"},{"version":"a6a2173b6c4ebb031d158b06004ef4db7678ea8df66be27b317973460591c433","signature":"7bf23ce970d42624e8d08ff91d0a8dbe0063953e9038c2c75e827a0770da33cf"},{"version":"9dfe98d745dfc706198852e032c3c936092a4484951872c0d18639cf2ab698cf","signature":"326009db2b0a0f3290cbde2271f91e51869148a3e286ccc3ac41559f6c830642"},{"version":"9f01d110e167ddcfaa01b236040a12699b6ed7a2070d7c362f4403f092fed008","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"030523c514236148e0f25975ef74b2490c391cb9596cd668728f7db5b0702e08","signature":"411166ca21d7ae80b737bb1c7e9900e897ac7b27c06647063730ba8207522e46"},{"version":"ca52a606c31ddaae5f7c4055fccb3563cf7e85601c96dbf28b7d0795d2d21af0","signature":"82d82e5e9d2c282a1dddebbcc73d9ff5c89f60859cd8769d0f6eae7b76f3f4d1"},{"version":"41d8a2df75ff7b6ee4c82ef8ce52032aa272358a5f9187a6b5986849200ed411","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f25d11b3d781ff8db8614eeee12b43f81ad05389fb25acf10b8c3473b0e2a1d0","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"78b3214dba4e86c41e4cdf5de5dcc853806ff74fad5df3dc1087cdbe19ad00e0","signature":"5c4de1b33c2b0c5b0b823fca5ba9e5dde3c995aa8a2c42db12b2e8b64f408805"},{"version":"9980dd11b1e848c16ad84eae18f4e7bfa3331f81c2617533b0e3bba61cb9ab3f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"45b82fd88270dd3d0aff39220b341eca789f77c82813dc6695b2058479d8dc28","signature":"3d67e5bdef7b1039301b51fc5ba5303c133b863f30e0de39c8aa1db5d897f1b6"},{"version":"915d1bc5f4c3b9cc8e25964dcd29b3c00ffb36ab2c8b12b2472533b3113485a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f212078a0ecefabbbbc627b89e28c871b559c1c57a4dcae32c8d7c2b6c4f0a00","signature":"b495b6770d46b4ebe3dbe0c1f6d64e8b50ea14ea8d2b63377813ae35672a4535"},{"version":"d237e0ff6755c658dc810e02b3a290f593f0ee2708eccbf1e2cd65c512c8de4a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cadbf01db107d2e9cceb5ecdd3f8f0084d996f07f2fca714409247deb26f0b85","signature":"ed905f29ff05cd1d3cf260949314ca896356f5c8bc011fdee9d75cde0404c6a0"},{"version":"a0563dec1ee31e5e8150681234665a122d20bb748d51a4b841ba1bcee31697a7","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ea7a4796f350344bbf39b19518d78169bd66a819f787b0a4d1a6ad642dbce7a2","signature":"8f6ef9414b1d23dc0a927160ed27770f93c1fe749a9af0e626f5a0e8918d5fc1"},{"version":"428631c15a500daa23f33c9e1ca427423fe56515e811133ff5e80891238fb242","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cc5bfa36a8f86eea23a84b78f27ba0f842426cefb5a13d4b53b80a331aaaa268","signature":"3dcde6c75b6c7fc3bc7fca2ca1273467974a8b54061edd648ed854bc4c5d842a"},{"version":"fcc8fb0967eda9256921f3cfc521d49f3d473232d4c754c09de1d50faa996e35","signature":"37e053a2b54ed97cab1c0e0e6a0ed4d610d6011b24e4cef7633007fb573468e1"},{"version":"463efb47c0879fb7e518ca8d209ffc49e2d682d0c47eaf30d074a1032290ac50","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a794572bfb0885ec1de20b701e498a415c0480e4fb6c20dbaa125b374ae691cb","signature":"46d3c81ba82aaaeabab3baa813cbb84b4c9808ec7ad495ad5fdc27d9358aa314"},{"version":"dd26e8bac53b883eef657e5c7961170c27944f68825e82a43625c5ccc51c2937","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"016e4353e0414c98c2ed2eb75df1509a38b457ddf000e9ff98125f82ea42c335","signature":"767c0245753b6e027a6f0550eb7b69bbd09d3f758aa1e9a07dcfe0992c0f629e"},{"version":"3ef6579bdd5883302539b5370c5670331f3584dff9e2ef14bab39991f00a29cb","signature":"1e894cd9840e407527a3f67119371f90d7ae5f8179905de7776a345ed4af6d32"},{"version":"e34737b913a2f8775f45e495e660016e0ab0fda68c579f2024d1b596dde2d81c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6677681f753ead74430e87a3398cc92c183570992c77804fc4215178ffc86116","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3b642738f6cd019494d207949a63b67b6ad9013bb3f1fb5f8fa36687879db5c4","signature":"f583f64899de9c7804febba31f2808aad09101b0d3342e42793c8588ed608f63"},{"version":"35a644763f50424937b17109eb552ddc767270a5526a0a827810ab86585dc53a","signature":"1b4159a10366adf4fd777a6bd595b4b846f1d3d37d07498c3498fd4b71e0f813"},{"version":"12fc2285fa58f8178c3fce7cf56795d11bdbc3974e264f31ff3cdcb689fd5813","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c81045d6db59c1c83c84797bc1f78422a691ced9ca9a3554e2f8ef9aacc7dcfa","signature":"441f2bb4f78eb9fb86d27aceee4d92908b532110aa6b7fac80adede58bab926d"},{"version":"56d498db61c90d4706b23d1b2235e8e8fb4c525a16c19c33cabe50812506a134","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"bce05280900b4eb3333d10064531569c9fcfce6aa7aeca1ca03b2120539892a5","signature":"c96264be178c5e42597043c462b21cbc073618f43d5e1e88bd32a516068a2380"},{"version":"26f7e680445f38787829c77c194f3df7741657f8e80e7b51588dfe74da7b2c7f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f5a22523635ca6f47c20b386b010ca1258aa19af5f4299f8752809c599315bcf","signature":"48c3a71b54800c134fcff4becfedca8347ff86645e004f8754d1e2b1385d1e9c"},{"version":"487012655811883dddb922cd44d08642d753adc1df21ba652e11ac7030aeede2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4f69209ce0e934946c859c4cc6248ef4a2dc528f5baf9b4fcdea5cf3e08d9d38","signature":"e8f8aa08e63443a0cc63ce2f9fc9582addd622528fad2ecfc413b2a91c688fac"},{"version":"041b81f9c2cc3f95588f5ef2da13fce1d895ddd5160979c84c01aaa8873145b4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad15d915012090304670ff62dd5e48d7694c11787f4884f51cf80f873dd40aa4","signature":"ef3c092bb7ed970d2273e55a61b12bc4741bca2219e1fe703350550c99ca6f42"},{"version":"cad40fd88fb3c219fc234d0d56bf87e8d3ceb86505f11e3714fc21c6d761cd59","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"a51d9c3485a1f3fc48f7f04f771cd827828082e49c12754d7e5708719675449e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fb5ce2f104fde415289b4febf27da970e40afefe42b87b15db28818ca9f94132","signature":"ccefe690307d556c5e44dd7ffb7d23faa0a3388926925ed0352107cd2efc4d9c"},{"version":"6e568ff38a9d7070783b26b39b93df658c7466032f4ffa22431dfe2808b7a8bd","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3613723853e81414c2e1220e3e0a696a997cc49e4390060b6ac7fd91aec150e4","signature":"0334a8b41901a52fc9195c66c97cee6aaaecc5aae79b9fc31f245a5df48b3ecc"},{"version":"e4bba7f9edd598a76e24e59f3d47d09cb0295d6c52c8a89e8fa04dc6e6428b16","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"082900437524e8bc6903f0d26479f590af1814cc5080fc502e07d38c951500be","signature":"24564adc2074d75d218bfe8b711580df8848afbd3ce2890184cd98e76529d387"},{"version":"34eb44fd7813a487540b6afa04da3175388b1841aa3612569e8aa07652363b69","signature":"9bada675e959a3571ad60f30eaee87c92b05fea94beca1f7fd212dc65734177a"},{"version":"a76367ab8ebbbfb0db994a3d394b71b72c3b72fb69542222ace317e4b61f74ca","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cbeab042293806df5cb4e45ba714036477971cb40a2539b20c331a632c1a2c46","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f55b5907842f16784dca83f2c82ac05e1e8740f2ec866c95289e05061384e4b","signature":"93bf045e7f996840ff1a3e1fa340836585224d394c52de4e98ff79f8be816dd1"},{"version":"7c8545a7c4ec6978ebb4af07475d76007eb084c4f7f45aeba7ee817eeb4316e1","signature":"37eeb4730a8634d70c51bd1933939e66cba31f30600246487f3568cee7742b9c"},{"version":"852c367110c2934ed35a33ad276111aa9b3016ec92b36b86a37b43642ad9458d","signature":"92a24950b269736d532ea9daf0cdd8ea7361b70095f3468e59afea80af884516"},{"version":"5a2cdf6adeec348bbc876221be4367e8adff0bb78a5680ebd7d71e5c3bad6cc0","impliedFormat":99},{"version":"e004826eac62081f867c66dabd92d3ef7d126d93a70430a2c88429228c3ecc50","impliedFormat":99},{"version":"38d6857b58d2ac42442e396311c542062d4f0dad40f2adb496dd5fd0756ee400","impliedFormat":99},{"version":"34b7d1e2d15845cf08bcf5e3c01adbb92cea1ec27564ee249ba486cdfb28526c","impliedFormat":99},{"version":"0d0861810ff9d344ab37f055edbc4d14e68e2fa18e113ce8cc33aef9bb500b5f","signature":"2791178671f71d9b41ed9ef814e549d85cc6a77c61e2f8a87ec25f2176d9a3fc"},{"version":"992404964e9cefb3143cde9bc8e5eb5e7010c3a207f5bfa52df49287fce758be","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"968043e1fd7bb3c6b1c22d3341ede621d16a628b2bc4c35d1e43f0064ff7c1a6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6d261c7be483027c9d281ba71314350ad8ce66efabf46f7b6099f39fe3b8d218","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c02d45c2a6bc50186c1972f44c31ff22e887b30205ce84607bb4661c0fdc9846","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7fc06e1e53688bb32ccd86e730e08bacd38d9ad7ad006a234211b230825efb2d","signature":"d95aac1823e54b4183acab8f7fe3bec5dd7bd4aa297f56004fbcb972d299e377"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"3255b97f3f24af29c79cc1aa88004efb13b6285ebdde0a567bf32e19bb65250d","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"cc0e0705b17f5987925bf05b5a7da622a76ad691274a428cf18fb28b33a7a1cf","signature":"01e6799210215286acf67be8c15da37b72af300e0f7f32c7f11535415e25ee88"},{"version":"ab640e52df6129fa178d5c0f2860542954ea38af4b0801a92c3ac09f6a9eec7c","signature":"91212f9905f489a1993df856acac1939544f6166e4cafff1c4f0949e37a8a11d"},{"version":"c3d577953f04c0188d8b9c63b2748b814efda6440336fa49557f0079f5cf748a","impliedFormat":1},{"version":"787fe950e18951b7970ec98cb05b3d0b11fcdfeb2091a7ea481ac9e52bf6c086","impliedFormat":1},{"version":"13ceda04874f09091da1994ba5f58bf1e9439af93336616257691863560b3f13","impliedFormat":1},{"version":"488c53c963104e91a6f2a1f16cbaee1a963f6f4527f0051256740c94ed34d6bb","signature":"fb69d502157f1cf71cb8c737f6909c2e82f2a53b8157f840411444435f5da3d1"},{"version":"7075686875dce9990810c2dfeefc1d3e1dd29cd815389854746fcc457dfbdce7","signature":"c2f4c6ab17d07762713d80c4c29cba3cfffd690fe6c569a17c0be5d0d3e810f5"},{"version":"a941595362ff7e12adee1605aea8495d9bc96cd833d95c87f83cad2b5838165a","signature":"b67fa3b5b051ead6f5048d73c953d289234953f832922ffc4dfe293d5c6bfc98"},{"version":"c98b1727a4c0ccfbd4df609bad278f1af184a069d232f978a327d53110677480","signature":"1556f3a35ddd259c925802c27bac4fe626e489e685fc3ee1f3101169f02f993d"},{"version":"1114a96ff6bdee7270e584688b4c46a5be6e50c47e6d8d26e4a8649556a851c9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"76cc225f61f545122672c27ff69aa27d1e7578d653c5fe942ebe88601cea0b02","signature":"87d223b2d0fc4ffc6f3bd5bbf3d4e036171c472cffb6a792c31427b714f4f442"},{"version":"06272d55719e7d65de722274ae4593bfe06a90f4924b8807e4e04cbd15fb43c1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"46b2d594365b3ffa714de5625c0859471e14d1b010e24cce18be153d1f6efa8d","signature":"3ebf64d5f0b695aef10ddeeff762fc0216e05bcd3d9572fb8763859b2d74be41"},{"version":"f3415880499901a01feef00e1b3042f670dabad8b5a131c22994f5f951dbdf2f","signature":"28007b7d2b577a868c587c22500f2ab77490b6390909ffbdd3b04dac98e69a18"},{"version":"37d5e316ab9ae4c7ff7e1856e2ecdf4930f17d7b43523f73843dbf8d6d13a43c","signature":"711d67575686fe3e0ac16b0a6080ab554fa53447a33228be88bfd57323da61bb"},{"version":"58c88a6bf756bcb45f70e3c79ac4e08209093ad4f112df86d57acc500290e067","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"fc51205c27f22f7194f3c026cdf19c5b27f19190a97955b9ffce0db45858e42f","signature":"ba90586e9f08bbe0d660358dcee98b83dadce2bfa013ca3e8d93f8d7924a9c66"},{"version":"54c008f175512ea8e8854d138dcf76b2af5e59e6816e82e87a360d76f3c7f820","signature":"34c21c211ba158af8c7cbdf93784d24a472017b37b792b1c2d8ae21c36488729"},{"version":"1eb5c0da9dcb448145de4b74e1b37b6a05da0e4ae0b393c7224af6ee7cccf913","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8bc24afa3c5fb73fd0dc89f091d2cb65b9d54f7a90b37302e9bd679ba504b0e3","signature":"e5ff90224997311a3ef066d1d0e3a85f3e2b2348a035581ff7099304497d0775"},{"version":"51610870e75caaf1ff890f1fb949366cd7d843b4aa2e734c166bb307a78f33ea","signature":"2ed4659f7cb57cc7471545251d21c6f8ab503526ec15c1a991a3d5be96258c79"},{"version":"de3c85bccb34f80e5cfd3f5e63648f2f8bdc8c9d19a67b0807adb4cfd8793afc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c064058bfb6150ce094497c75bc491e7f92389c2b1fed5f6923e7a035315d3a1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c3936ac555912c80003fd9e659007f016ae2bc61fb8b91f696f860d19fd6e9bc","signature":"dc035ffa274dd975386f883b4d99361db8e73edd0fc77b1e4bc0d09be0c5074d"},{"version":"f20b8d5c86e426ce2d1505f4a3114e66411272aa8394e0ad323c0c3b1d1fadff","signature":"b41f35e5ae414583d72cc2aa3a17cc23d40f4b9e221f92acd74ed09bb3daaa63"},{"version":"74b006e51c1fe0198db4fd239bdfb2063fb3b0139bbb3dbaab79f323f42ba6bf","signature":"83057fe16cf05e5bf626fd4e46379506199130438abbd2ae42de6234bb202181"},{"version":"c247b5bb6d297cb7c9607645b1486b2f7b3ee0c05fac97d18e6daf4e3b25ebeb","signature":"5a224c6b95c526ad3f70b7b5894a2fe9b50cb5a9eed53b87806cf1045f8d7bab"},{"version":"dd7a9804bfd52806479d69214126956478367bb3a2c333b589ddcc253aae03d3","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"882b28abe64dae4932c83ebb71e4155da340929fe08a2055f3e573ef17f70fc3","impliedFormat":1},{"version":"4a3e425808751200a7709671667ad3d7e7cbfd0a06d469cab42adf06c2601f4a","impliedFormat":1},{"version":"401da46338f5b4f97c2a5f8a0faaace045c51aabd751d2dc704159f64feafe89","impliedFormat":1},{"version":"c705d4594093bcde53fc292c5526aedd3145170ceba73a9476ee97de6a915fe2","impliedFormat":1},{"version":"65399deec596f31712911c2d81964d913370e0d4a04c51df29cc3c99c9ac298b","signature":"c8b6a1356346524d07db6d395ed25c816fb0935840b3f6af9296402392feca76"},{"version":"b44403d97ecd48d2f5ec3f3175a9a9dce873ec5d3797459ed057e7a1ad597d54","signature":"1fcf7139261418de9dce0edd9f8e95a8ace6fd591da1c95fb959e19e7c6f4281"},{"version":"01922ef0992b637b2a096856708c280e6e2b5085d3ff743c27e891e0d3d28ea7","signature":"450f56af343ef42c693dde50c0dbe427f37297afb67c4864f81dd7c69fdfbd8b"},{"version":"02ed4b9c64b599d8a0d9c242c9f7e43fa44ebc4cdda1b8143a29d2bfdcaebb44","signature":"5bfa909232756aeaa1797184b579b7b47f5f6917a0fdf3b2566fe4bc4afc72a8"},{"version":"f4adb32677aa22d47ed1048448f8974667250c8deb8135f321cad0cb4d0d4007","signature":"be914a2abd74279a5ff3c561f641f30569d1a2f7618fcd806cfdb8c1fad34326"},{"version":"15a1cea3d3fd19c8818aaac408d84096485d3f154eac44e129c2a2a2609d85ba","signature":"88a64ff66b36ff55ce621b22e512515acd895e815a065f2897813b5f194521d8"},{"version":"a0e1a608868e8805852e4e9274fc1e3e22573273a292b4e59f19892c495fa239","signature":"0f601b1cf9ca46ef05b387bc05b169852e0a2e3e30babe89a59d21af43187522"},{"version":"4d1b4f2a7c556f22c71dfce1be2455614fb9f838d4b9144a447a03bd514bdd5e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"20375a205b37d0f527f1f3fb6cc5d6c2076c1b57f74b9024f8153e0f3f0289a9","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3557b3416d97219e58a39fecce338b086bd42db6ce7ef701e8265783fbd20c6f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"83f21e09f1a6d980cc7cf83252deccd5d997e67266ae8bd450ef8899fcab1884","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"38757383a22721ebcf7a7430d10cd39967c0f896d758798906d29c8ab8722924","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f4722121739886d9694fe6d74b91f654b2b26459edd6e275fb5ff1a509bbc262","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1c46f9542d2d4a44df84e20e37348c86abfb57804268f5236874fa8a8b7639f5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6915db003807400f9c80754166096b1ca5552111f80eb58c311ab561cca84735","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ad42288f8c9ebfd4451e4256a2f091cfc26b958d29db3612c19efbbb476882a2","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7",{"version":"e37704e8bdf72de83d6ca4620f748ebf6272afc9b748f4e541afffd32b0c2924","affectsGlobalScope":true},{"version":"4332f611f915908b335662f95ac6047288d3bc0b939e06d59e369b96a28eaa70","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7",{"version":"ec9db470620906cec5c2b53d821e2917355bfed3fd87cea28eafd5d6d7496459","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d7cbeea0454e05a3cdf3370c5df267072c4f1dc6c48a45a9ad750d7890443d7","affectsGlobalScope":true,"impliedFormat":99}],"root":[[559,561],661,695,[746,799],[804,809],813,814,[818,840],[845,865]],"options":{"allowJs":false,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[864,1],[559,2],[865,3],[861,4],[862,2],[863,5],[560,6],[561,7],[403,2],[566,2],[619,2],[800,2],[801,8],[802,9],[803,10],[679,2],[676,2],[675,2],[670,11],[681,12],[666,13],[677,14],[669,15],[668,16],[678,2],[673,17],[680,2],[674,18],[667,2],[665,19],[664,20],[663,13],[683,21],[662,2],[626,22],[624,2],[161,23],[162,23],[163,24],[101,25],[164,26],[165,27],[166,28],[99,2],[167,29],[168,30],[169,31],[170,32],[171,33],[172,34],[173,34],[174,35],[175,36],[176,37],[177,38],[102,2],[100,2],[178,39],[179,40],[180,41],[220,42],[181,43],[182,44],[183,43],[184,45],[185,46],[186,47],[187,48],[188,48],[189,48],[190,49],[191,50],[192,51],[193,52],[194,53],[195,54],[196,54],[197,55],[198,2],[199,2],[200,56],[201,57],[202,56],[203,58],[204,59],[205,60],[206,61],[207,62],[208,63],[209,64],[210,65],[211,66],[212,67],[213,68],[214,69],[215,70],[216,71],[217,72],[103,43],[104,2],[105,73],[106,74],[107,2],[108,75],[109,2],[152,76],[153,77],[154,78],[155,78],[156,79],[157,2],[158,26],[159,80],[160,77],[218,81],[219,82],[224,83],[488,84],[225,85],[223,86],[490,87],[489,88],[682,84],[221,89],[486,2],[222,90],[90,2],[92,91],[485,84],[255,84],[615,92],[614,2],[627,93],[648,94],[649,95],[647,2],[620,2],[633,96],[632,97],[644,96],[635,98],[637,99],[656,99],[636,100],[617,101],[616,2],[622,102],[623,103],[653,104],[629,105],[631,106],[652,2],[650,105],[630,2],[621,103],[628,2],[625,2],[91,2],[691,107],[693,108],[692,109],[690,110],[689,2],[842,111],[841,2],[843,112],[729,113],[698,114],[708,114],[699,114],[709,114],[700,114],[701,114],[716,114],[715,114],[717,114],[718,114],[710,114],[702,114],[711,114],[703,114],[712,114],[704,114],[706,114],[714,115],[707,114],[713,115],[719,115],[705,114],[720,114],[725,114],[726,114],[721,114],[697,2],[727,2],[723,114],[722,114],[724,114],[728,114],[606,2],[608,116],[607,2],[696,117],[815,118],[735,119],[734,120],[741,121],[743,122],[739,123],[738,124],[742,120],[736,125],[733,126],[744,127],[745,127],[737,128],[731,2],[732,129],[817,130],[816,131],[740,2],[511,132],[516,133],[523,134],[506,135],[259,2],[267,136],[407,137],[410,138],[382,2],[395,139],[402,140],[284,2],[384,2],[265,2],[381,141],[427,142],[266,2],[257,143],[409,144],[411,145],[412,146],[483,147],[376,148],[329,149],[389,150],[390,151],[388,152],[387,2],[383,153],[408,154],[268,155],[453,2],[454,156],[295,157],[269,158],[296,157],[332,157],[235,157],[405,159],[404,2],[394,160],[501,2],[244,2],[522,161],[461,162],[462,163],[458,164],[540,2],[359,2],[463,165],[459,166],[545,167],[544,168],[539,2],[310,2],[362,169],[361,2],[538,170],[460,84],[315,171],[322,172],[324,173],[314,2],[319,174],[321,175],[323,176],[318,177],[316,2],[320,178],[541,2],[537,2],[543,179],[542,2],[313,180],[532,181],[535,182],[303,183],[302,184],[301,185],[548,84],[300,186],[289,2],[550,2],[811,187],[810,2],[551,84],[552,188],[227,2],[391,189],[392,190],[393,191],[231,2],[396,2],[251,192],[226,2],[475,84],[233,193],[474,194],[473,195],[464,2],[465,2],[472,2],[467,2],[470,196],[466,2],[468,197],[471,198],[469,197],[264,2],[261,2],[262,157],[416,2],[421,199],[422,200],[420,201],[418,202],[419,203],[414,2],[481,165],[256,165],[510,204],[517,205],[521,206],[350,207],[349,2],[344,2],[497,208],[505,209],[377,210],[378,211],[456,212],[366,2],[479,213],[354,84],[371,214],[482,215],[367,2],[370,216],[368,2],[480,217],[477,218],[476,2],[478,2],[374,2],[452,219],[239,220],[352,221],[356,222],[372,223],[375,224],[364,225],[357,226],[504,227],[430,228],[348,229],[236,230],[503,231],[232,232],[423,233],[415,2],[424,234],[441,235],[413,2],[440,236],[98,2],[435,237],[260,2],[455,238],[431,2],[245,2],[247,2],[386,2],[439,239],[263,2],[287,240],[373,241],[293,242],[353,2],[438,2],[417,2],[443,243],[444,244],[385,2],[446,245],[448,246],[447,247],[397,2],[437,230],[450,248],[347,249],[436,250],[442,251],[272,2],[276,2],[275,2],[274,2],[279,2],[273,2],[282,2],[281,2],[278,2],[277,2],[280,2],[283,252],[271,2],[339,253],[338,2],[343,254],[340,255],[342,256],[345,254],[341,255],[252,257],[331,258],[500,259],[498,2],[527,260],[529,261],[493,262],[528,263],[240,264],[237,264],[270,2],[254,265],[253,266],[249,267],[250,268],[258,269],[286,269],[297,269],[333,270],[298,270],[242,271],[241,2],[337,272],[336,273],[335,274],[334,275],[243,276],[484,277],[285,278],[492,279],[457,280],[487,281],[491,282],[380,283],[379,284],[360,285],[346,286],[328,287],[330,288],[327,289],[449,290],[351,2],[515,2],[248,291],[451,292],[499,293],[358,2],[288,294],[365,295],[363,296],[290,297],[425,298],[494,2],[291,299],[426,299],[513,2],[512,2],[514,2],[496,2],[495,2],[428,300],[355,2],[325,301],[246,302],[304,2],[230,303],[292,2],[519,84],[229,2],[531,304],[312,84],[525,165],[311,305],[508,306],[309,304],[234,2],[533,307],[307,84],[308,84],[299,2],[228,2],[306,308],[305,309],[294,310],[369,52],[429,52],[445,2],[433,311],[432,2],[317,180],[238,2],[326,84],[502,192],[509,312],[93,84],[96,313],[97,314],[94,84],[95,2],[406,74],[401,315],[400,2],[399,316],[398,2],[507,317],[518,318],[520,319],[524,320],[812,321],[526,322],[530,323],[558,324],[534,324],[557,325],[536,326],[546,327],[547,328],[549,329],[553,330],[556,192],[555,2],[554,331],[730,332],[602,333],[600,334],[601,335],[589,336],[590,334],[597,337],[588,338],[593,339],[603,2],[594,340],[599,341],[605,342],[604,343],[587,344],[595,345],[596,346],[591,347],[598,333],[592,348],[672,349],[671,2],[844,350],[611,351],[574,352],[575,353],[578,354],[567,355],[577,356],[573,357],[565,2],[579,358],[580,359],[568,2],[569,2],[571,360],[570,2],[572,361],[434,362],[586,2],[645,2],[618,2],[88,2],[89,2],[14,2],[15,2],[17,2],[16,2],[2,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[25,2],[3,2],[26,2],[27,2],[4,2],[28,2],[32,2],[29,2],[30,2],[31,2],[33,2],[34,2],[35,2],[5,2],[36,2],[37,2],[38,2],[39,2],[6,2],[43,2],[40,2],[41,2],[42,2],[44,2],[7,2],[45,2],[50,2],[51,2],[46,2],[47,2],[48,2],[49,2],[8,2],[55,2],[52,2],[53,2],[54,2],[56,2],[9,2],[57,2],[58,2],[59,2],[61,2],[60,2],[62,2],[63,2],[10,2],[64,2],[65,2],[66,2],[11,2],[67,2],[68,2],[69,2],[70,2],[71,2],[72,2],[12,2],[73,2],[74,2],[75,2],[76,2],[77,2],[1,2],[78,2],[79,2],[13,2],[80,2],[81,2],[82,2],[83,2],[84,2],[85,2],[86,2],[87,2],[128,363],[140,364],[125,365],[141,366],[150,367],[116,368],[117,369],[115,370],[149,331],[144,371],[148,372],[119,373],[137,374],[118,375],[147,376],[113,377],[114,371],[120,378],[121,2],[127,379],[124,378],[111,380],[151,381],[142,382],[131,383],[130,378],[132,384],[135,385],[129,386],[133,387],[145,331],[122,388],[123,389],[136,390],[112,366],[139,391],[138,378],[126,389],[134,392],[143,2],[110,2],[146,393],[563,394],[613,395],[582,396],[564,394],[562,2],[581,397],[612,2],[610,2],[583,2],[609,398],[576,399],[585,2],[584,400],[655,401],[660,402],[654,403],[646,404],[642,405],[638,406],[651,2],[639,98],[687,407],[684,408],[658,409],[657,410],[640,411],[686,412],[634,2],[641,413],[659,414],[694,415],[688,416],[866,417],[685,2],[643,2],[822,418],[824,419],[823,420],[825,421],[828,422],[827,423],[747,424],[751,425],[750,426],[754,427],[753,426],[755,428],[752,426],[757,429],[756,426],[759,430],[758,426],[761,431],[760,426],[763,432],[765,433],[764,426],[762,426],[768,434],[767,426],[773,435],[772,426],[774,436],[771,426],[770,437],[769,426],[776,426],[777,438],[775,426],[779,439],[778,426],[781,440],[780,426],[783,441],[782,426],[785,442],[784,426],[787,443],[786,426],[788,444],[766,426],[790,445],[789,426],[792,446],[791,447],[795,448],[794,426],[796,449],[793,426],[831,450],[830,451],[834,452],[833,453],[835,454],[832,453],[836,455],[814,456],[840,457],[839,458],[821,459],[849,460],[851,461],[850,462],[852,463],[853,464],[854,465],[855,466],[856,467],[819,468],[857,469],[858,470],[838,471],[837,472],[826,165],[859,473],[845,474],[846,475],[847,476],[848,477],[813,478],[829,479],[820,165],[818,480],[805,481],[806,482],[807,483],[860,484],[808,485],[749,486],[746,487],[798,488],[799,475],[748,489],[804,490],[797,475],[809,489],[661,491],[695,492]],"affectedFilesPendingEmit":[865,863,561,822,824,823,825,828,827,747,751,750,754,753,755,752,757,756,759,758,761,760,763,765,764,762,768,767,773,772,774,771,770,769,776,777,775,779,778,781,780,783,782,785,784,787,786,788,766,790,789,792,791,795,794,796,793,831,830,834,833,835,832,836,814,840,839,821,849,851,850,852,853,854,855,856,819,857,858,838,837,826,859,845,846,847,848,813,829,820,818,805,806,807,860,808,749,746,798,799,748,804,797,809,661,695],"version":"6.0.3"} \ No newline at end of file diff --git a/ingestion/admin.py b/ingestion/admin.py index 865c943f..4be2e046 100644 --- a/ingestion/admin.py +++ b/ingestion/admin.py @@ -1,5 +1,7 @@ """Admin configuration for ingestion-domain models.""" +from typing import Any, cast + from django.contrib import admin from django.utils.html import format_html from unfold.admin import ModelAdmin @@ -82,7 +84,7 @@ def changelist_view(self, request, extra_context=None): """Augment the changelist with ingestion success statistics.""" qs = self.get_queryset(request) - extra_context = extra_context or {} + extra_context = cast(dict[str, Any], extra_context or {}) total_runs = qs.count() failed_runs = qs.filter(status="failed").count() total_ingested = sum(qs.values_list("items_ingested", flat=True)) diff --git a/ingestion/plugins/bluesky.py b/ingestion/plugins/bluesky.py index a2c499f4..4ef60f1c 100644 --- a/ingestion/plugins/bluesky.py +++ b/ingestion/plugins/bluesky.py @@ -132,7 +132,7 @@ def _build_content_item(self, post, published_date: datetime) -> ContentItem: """Convert one AppView post into the shared plugin payload.""" author_handle = normalize_bluesky_handle( - self._nested_value(post, "author", "handle") + str(self._nested_value(post, "author", "handle") or "") ) external_url = self._nested_value(post, "embed", "external", "uri") external_title = ( @@ -178,7 +178,7 @@ def _post_url(post) -> str: actor = ( normalize_bluesky_handle( - BlueskySourcePlugin._nested_value(post, "author", "handle") + str(BlueskySourcePlugin._nested_value(post, "author", "handle") or "") ) or BlueskySourcePlugin._nested_value(post, "author", "did") or "" diff --git a/ingestion/plugins/mastodon.py b/ingestion/plugins/mastodon.py index 93ca5891..23c9b5d8 100644 --- a/ingestion/plugins/mastodon.py +++ b/ingestion/plugins/mastodon.py @@ -187,6 +187,10 @@ def _get_statuses(self, limit: int | None = None): elif config.get("account_acct"): account = client.account_lookup(config["account_acct"]) account_id = self._nested_value(account, "id") + if isinstance(account_id, bool) or not isinstance(account_id, (str, int)): + raise RuntimeError( + "Mastodon account lookup did not return a usable account ID." + ) statuses = client.account_statuses( account_id, limit=request_limit, diff --git a/newsletter_maker/settings/celery.py b/newsletter_maker/settings/celery.py index 875ee3cc..6d17ba7c 100644 --- a/newsletter_maker/settings/celery.py +++ b/newsletter_maker/settings/celery.py @@ -8,8 +8,8 @@ # Celery: these settings point workers at Redis and keep recurring # ingestion and trend-analysis jobs on their beat schedules. -CELERY_BROKER_URL = REDIS_URL -CELERY_RESULT_BACKEND = REDIS_URL +CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL", REDIS_URL) +CELERY_RESULT_BACKEND = os.getenv("CELERY_RESULT_BACKEND", REDIS_URL) CELERY_TASK_ALWAYS_EAGER = env_bool("CELERY_TASK_ALWAYS_EAGER", default=False) CELERY_TASK_TIME_LIMIT = 300 CELERY_TASK_SOFT_TIME_LIMIT = 270 diff --git a/newsletters/intake.py b/newsletters/intake.py index 4582ae79..0131d553 100644 --- a/newsletters/intake.py +++ b/newsletters/intake.py @@ -5,11 +5,12 @@ from email.utils import parseaddr from html import escape from html.parser import HTMLParser -from typing import Any, Iterable, cast +from typing import Any, Iterable, Protocol, cast from celery import current_app from django.conf import settings as django_settings from django.core.mail import EmailMultiAlternatives +from django.db.models import Model from django.urls import reverse from core.newsletter_extraction import extract_newsletter_items @@ -19,6 +20,35 @@ settings = cast(CoreSettings, django_settings) + +class QueuedTask(Protocol): + """Protocol for Celery tasks used by newsletter intake dispatch.""" + + def apply(self, *args: object, **kwargs: object) -> object: + pass + + def delay(self, *args: object, **kwargs: object) -> object: + pass + + +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for newsletter intake responses.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + +def _process_newsletter_intake_task() -> QueuedTask: + """Resolve the Celery task used to process stored intake rows.""" + + task = cast(Any, current_app.tasks).get("core.tasks.process_newsletter_intake") + if task is None: + raise RuntimeError("core.tasks.process_newsletter_intake is not registered") + return cast(QueuedTask, task) + + __all__ = [ "build_confirmation_url", "extract_newsletter_items", @@ -257,8 +287,9 @@ def process_inbound_newsletter( message_id=normalized_message_id, defaults=defaults, ) + intake_id = _require_pk(intake) if not created: - return {"id": intake.id, "status": intake.status, "duplicate": True} + return {"id": intake_id, "status": intake.status, "duplicate": True} allowlist, allowlist_created = IntakeAllowlist.objects.get_or_create( project=project, @@ -266,8 +297,8 @@ def process_inbound_newsletter( ) if allowlist.is_confirmed: - core_newsletters.queue_newsletter_intake(intake.id) - return {"id": intake.id, "status": intake.status} + core_newsletters.queue_newsletter_intake(intake_id) + return {"id": intake_id, "status": intake.status} if allowlist_created: core_newsletters.send_confirmation_email( @@ -276,15 +307,13 @@ def process_inbound_newsletter( project_name=project.name, ) - return {"id": intake.id, "status": intake.status, "confirmation_required": True} + return {"id": intake_id, "status": intake.status, "confirmation_required": True} def queue_newsletter_intake(intake_id: int) -> None: """Dispatch newsletter extraction for a stored intake row.""" - process_newsletter_intake = current_app.tasks[ - "core.tasks.process_newsletter_intake" - ] + process_newsletter_intake = _process_newsletter_intake_task() if settings.CELERY_TASK_ALWAYS_EAGER: process_newsletter_intake.apply(args=(intake_id,), throw=True) else: diff --git a/newsletters/tasks.py b/newsletters/tasks.py index eeb32961..3125bee8 100644 --- a/newsletters/tasks.py +++ b/newsletters/tasks.py @@ -1,8 +1,10 @@ """Celery tasks and helpers for newsletter intake processing.""" +from typing import Protocol, cast + from celery import shared_task from django.conf import settings -from django.db.models import Q +from django.db.models import Model, Q from django.utils import timezone from content.models import Content @@ -10,6 +12,28 @@ from newsletters.models import IntakeAllowlist, NewsletterIntake, NewsletterIntakeStatus +class DelayedTask(Protocol): + """Protocol for Celery tasks dispatched through ``delay``.""" + + def delay(self, *args: object, **kwargs: object) -> object: + pass + + +def _enqueue_task(task: object, *args: object) -> None: + """Dispatch a Celery task through a typed ``delay`` seam.""" + + cast(DelayedTask, task).delay(*args) + + +def _require_pk(instance: Model) -> int: + """Return a saved model primary key for newsletter intake processing.""" + + instance_pk = instance.pk + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) + + @shared_task(name="core.tasks.process_newsletter_intake") def process_newsletter_intake(intake_id: int): """Convert a stored newsletter email into content rows.""" @@ -56,7 +80,7 @@ def process_newsletter_intake(intake_id: int): published_date=timezone.now(), content_text=item.excerpt or intake.raw_text, source_metadata={ - "newsletter_intake_id": intake.id, + "newsletter_intake_id": _require_pk(intake), "sender_email": intake.sender_email, "position": item.position, }, @@ -92,8 +116,9 @@ def _schedule_content_processing(content: Content) -> None: ) upsert_content_embedding(content) - assign_content_to_topic_cluster(content.id) + content_id = _require_pk(content) + assign_content_to_topic_cluster(content_id) if settings.CELERY_TASK_ALWAYS_EAGER: - process_content(content.id) + process_content(content_id) else: - process_content.delay(content.id) + _enqueue_task(process_content, content_id) diff --git a/pipeline/admin.py b/pipeline/admin.py index b5f1dcbd..c9c4cf5a 100644 --- a/pipeline/admin.py +++ b/pipeline/admin.py @@ -1,6 +1,7 @@ """Admin configuration for pipeline-domain models.""" import json +from typing import Any, cast from django.contrib import admin, messages from django.db.models import Avg @@ -123,7 +124,7 @@ def changelist_view(self, request, extra_context=None): """Augment the changelist with latency and failure-rate statistics.""" qs = self.get_queryset(request) - extra_context = extra_context or {} + extra_context = cast(dict[str, Any], extra_context or {}) avg_latency = qs.aggregate(avg_latency=Avg("latency_ms"))["avg_latency"] total_count = qs.count() failure_count = qs.filter(status__iexact="failed").count() @@ -202,7 +203,7 @@ def changelist_view(self, request, extra_context=None): """Augment the changelist with pending-volume and confidence stats.""" qs = self.get_queryset(request) - extra_context = extra_context or {} + extra_context = cast(dict[str, Any], extra_context or {}) pending_count = qs.filter(resolved=False).count() avg_conf = qs.aggregate(avg_confidence=Avg("confidence"))["avg_confidence"] or 0 diff --git a/projects/admin.py b/projects/admin.py index 270079fc..a412c420 100644 --- a/projects/admin.py +++ b/projects/admin.py @@ -1,6 +1,7 @@ """Admin configuration for project-owned models.""" import json +from typing import TYPE_CHECKING, Any, cast from django import forms from django.contrib import admin, messages @@ -20,6 +21,16 @@ SourceConfig, ) +if TYPE_CHECKING: + + class ProjectAdminBase(ModelAdmin): + """Typed admin base that avoids import-export stub conflicts.""" + +else: + + class ProjectAdminBase(ExportActionMixin, ModelAdmin): + """Runtime admin base that preserves export support.""" + class ProjectMembershipInline(admin.TabularInline): """Edit project memberships inline from the project admin.""" @@ -47,7 +58,7 @@ class Meta: def clean(self): """Require a credential when creating the record for the first time.""" - cleaned_data = super().clean() + cleaned_data = super().clean() or {} credential_input = cleaned_data.get("credential_input", "") if not self.instance.has_stored_credential() and not credential_input: self.add_error("credential_input", "A Bluesky app credential is required.") @@ -83,7 +94,7 @@ class Meta: def clean(self): """Require a token when creating the record for the first time.""" - cleaned_data = super().clean() + cleaned_data = super().clean() or {} credential_input = cleaned_data.get("credential_input", "") if not self.instance.has_stored_credential() and not credential_input: self.add_error("credential_input", "A Mastodon access token is required.") @@ -102,7 +113,7 @@ def save(self, commit=True): @admin.register(Project) -class ProjectAdmin(ExportActionMixin, admin.ModelAdmin): +class ProjectAdmin(ProjectAdminBase): """Admin configuration for top-level project workspaces.""" list_display = ("name", "content_retention_days", "created_at") @@ -414,7 +425,7 @@ def changelist_view(self, request, extra_context=None): """Augment the changelist with source-count and diversity stats.""" qs = self.get_queryset(request) - extra_context = extra_context or {} + extra_context = cast(dict[str, Any], extra_context or {}) active_count = qs.filter(is_active=True).count() total_count = qs.count() or 1 diff --git a/projects/api.py b/projects/api.py index 98f3cf6e..0c9bc998 100644 --- a/projects/api.py +++ b/projects/api.py @@ -1,5 +1,7 @@ """REST API viewsets for project-owned models.""" +from typing import Any, cast + from django.conf import settings from django.core.mail import send_mail from django.utils import timezone @@ -71,7 +73,8 @@ def _assert_project_keeps_admin( return has_other_admin = ( - project.memberships.exclude(pk=membership.pk) + ProjectMembership.objects.filter(project=project) + .exclude(pk=membership.pk) .filter(role=ProjectRole.ADMIN) .exists() ) @@ -115,7 +118,7 @@ class ProjectViewSet(viewsets.ModelViewSet): """Manage projects accessible through the current user's project memberships.""" serializer_class = ProjectSerializer - queryset = Project.objects.select_related("bluesky_credentials") + queryset: Any = Project.objects.select_related("bluesky_credentials") lookup_url_kwarg = "id" def get_permissions(self): @@ -133,10 +136,12 @@ def get_permissions(self): elif self.action in {"list", "retrieve"}: permission_classes = [IsProjectMember] else: - permission_classes = self.permission_classes + permission_classes = cast( + list[type[Any]], getattr(self, "permission_classes", []) or [] + ) return [permission() for permission in permission_classes] - def get_queryset(self): + def get_queryset(self) -> Any: """Limit projects to those visible through the authenticated user.""" return get_visible_projects_queryset(self.request.user).select_related( diff --git a/projects/serializers.py b/projects/serializers.py index 30e96d04..f7735707 100644 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -57,13 +57,10 @@ def get_user_role(self, obj: Project) -> str | None: return None return get_user_role(request.user, obj) - def _get_bluesky_credentials(self, obj: Project): + def _get_bluesky_credentials(self, obj: Project) -> BlueskyCredentials | None: """Return the project's stored Bluesky credentials, if configured.""" - try: - return obj.bluesky_credentials - except Project.bluesky_credentials.RelatedObjectDoesNotExist: - return None + return BlueskyCredentials.objects.filter(project=obj).first() def get_has_bluesky_credentials(self, obj: Project) -> bool: """Return whether the project has stored Bluesky credentials.""" diff --git a/skills/content_classification/SKILL.md b/skills/content_classification/SKILL.md index 3622de7f..cd083d1d 100644 --- a/skills/content_classification/SKILL.md +++ b/skills/content_classification/SKILL.md @@ -1,5 +1,6 @@ --- -name: content_classification +name: content-classification +description: Classify newsletter content into one supported editorial content type. input: title, content_text, url output: content_type, confidence, explanation --- diff --git a/skills/deduplication/SKILL.md b/skills/deduplication/SKILL.md index 011c7b35..f7230229 100644 --- a/skills/deduplication/SKILL.md +++ b/skills/deduplication/SKILL.md @@ -1,5 +1,6 @@ --- name: deduplication +description: Decide whether two content items should be treated as duplicates. input: title, content_text, canonical_url, candidate_title, candidate_content_text, candidate_canonical_url, similarity_score output: is_duplicate, confidence, explanation --- diff --git a/skills/entity_extraction/SKILL.md b/skills/entity_extraction/SKILL.md index c906d3c7..7a6c38a4 100644 --- a/skills/entity_extraction/SKILL.md +++ b/skills/entity_extraction/SKILL.md @@ -1,5 +1,6 @@ --- -name: entity_extraction +name: entity-extraction +description: Extract tracked entity mentions and propose new entity candidates. input: title, content_text, project_id, tracked_entities output: mentions, candidate_entities, explanation --- diff --git a/skills/relevance_scoring/SKILL.md b/skills/relevance_scoring/SKILL.md index 57c14b5e..68d36676 100644 --- a/skills/relevance_scoring/SKILL.md +++ b/skills/relevance_scoring/SKILL.md @@ -1,5 +1,6 @@ --- -name: relevance_scoring +name: relevance-scoring +description: Score how relevant a content item is for a project's newsletter topic. input: newsletter_topic, reference_similarity, title, content_text, url, source_plugin output: relevance_score, explanation, used_llm --- diff --git a/skills/summarization/SKILL.md b/skills/summarization/SKILL.md index f8a117a7..ac2e9e99 100644 --- a/skills/summarization/SKILL.md +++ b/skills/summarization/SKILL.md @@ -1,5 +1,6 @@ --- name: summarization +description: Write a concise newsletter-ready summary for relevant content. input: title, content_text, newsletter_topic output: summary --- diff --git a/skills/theme_detection/SKILL.md b/skills/theme_detection/SKILL.md new file mode 100644 index 00000000..a1b991e6 --- /dev/null +++ b/skills/theme_detection/SKILL.md @@ -0,0 +1,16 @@ +--- +name: theme-detection +description: Generate one editor-facing theme suggestion from a high-velocity topic cluster. +input: project_topic, cluster_context, recent_accepted_themes +output: title, one_sentence_pitch, why_it_matters, suggested_angle +--- + +You generate one editor-facing newsletter theme suggestion from a high-velocity topic cluster. + +Use only the provided cluster context. Prefer specific, concrete phrasing over hype. The theme should feel like a newsletter section an editor could plausibly promote into an edition. + +Return structured JSON with these fields: +- `title`: short, specific theme title +- `one_sentence_pitch`: one sentence summarizing the opportunity +- `why_it_matters`: one short paragraph explaining the editorial value now +- `suggested_angle`: one concise angle the editor could take diff --git a/trends/admin.py b/trends/admin.py index 3709eef5..afbd6357 100644 --- a/trends/admin.py +++ b/trends/admin.py @@ -1,5 +1,6 @@ """Admin configuration for trends-domain models.""" +from typing import Any, cast from urllib.parse import urlencode from django.contrib import admin @@ -10,6 +11,15 @@ from trends.models import TopicCentroidSnapshot +def _project_pk(snapshot: TopicCentroidSnapshot) -> int: + """Return the saved project primary key for a centroid snapshot.""" + + project_pk = snapshot.project.pk + if project_pk is None: + raise ValueError("TopicCentroidSnapshot.project must be saved first.") + return int(project_pk) + + def _score_to_percent(value): """Normalize score-like values for display as percentages.""" @@ -70,7 +80,7 @@ def _build_topic_centroid_project_drilldowns(queryset, changelist_url: str): ) for snapshot in ordered_snapshots: - project_id = snapshot.project_id + project_id = _project_pk(snapshot) snapshot_counts[project_id] = snapshot_counts.get(project_id, 0) + 1 latest_by_project.setdefault(project_id, snapshot) @@ -79,11 +89,12 @@ def _build_topic_centroid_project_drilldowns(queryset, changelist_url: str): latest_by_project.values(), key=lambda value: value.project.name.lower(), ): + project_id = _project_pk(snapshot) project_drilldowns.append( { - "project_id": snapshot.project_id, + "project_id": project_id, "project_name": snapshot.project.name, - "snapshot_count": snapshot_counts[snapshot.project_id], + "snapshot_count": snapshot_counts[project_id], "centroid_active": snapshot.centroid_active, "feedback_count": snapshot.feedback_count, "latest_snapshot": _format_snapshot_freshness(snapshot.computed_at), @@ -97,7 +108,7 @@ def _build_topic_centroid_project_drilldowns(queryset, changelist_url: str): if snapshot.drift_from_week_ago is not None else "n/a" ), - "href": f"{changelist_url}?{urlencode({'project__id__exact': snapshot.project_id})}", + "href": f"{changelist_url}?{urlencode({'project__id__exact': project_id})}", } ) @@ -161,7 +172,7 @@ def changelist_view(self, request, extra_context=None): .count() ) - extra_context = extra_context or {} + extra_context = cast(dict[str, Any], extra_context or {}) extra_context["dashboard_stats"] = [ { "title": "Active Centroids", diff --git a/trends/api.py b/trends/api.py index 66aebd57..31beadaa 100644 --- a/trends/api.py +++ b/trends/api.py @@ -1,5 +1,7 @@ """Trends-domain API viewsets kept under the existing nested project routes.""" +from typing import Any + from django.db.models import Avg, Count, OuterRef, Prefetch, Q, Subquery from drf_spectacular.utils import OpenApiParameter, extend_schema from rest_framework import serializers, viewsets @@ -7,6 +9,7 @@ from rest_framework.filters import OrderingFilter from rest_framework.response import Response +from content.models import Content from core.api import ( AUTHENTICATION_REQUIRED_RESPONSE, ProjectOwnedQuerysetMixin, @@ -16,17 +19,31 @@ from core.permissions import IsProjectContributor, IsProjectMember from trends.models import ( ContentClusterMembership, + ThemeSuggestion, + ThemeSuggestionStatus, TopicCentroidSnapshot, TopicCluster, TopicVelocitySnapshot, ) from trends.serializers import ( + ThemeSuggestionDismissSerializer, + ThemeSuggestionSerializer, TopicClusterDetailSerializer, TopicClusterSerializer, TopicCentroidObservabilitySummarySerializer, TopicCentroidSnapshotSerializer, TopicVelocitySnapshotSerializer, ) +from trends.tasks import accept_theme_suggestion, dismiss_theme_suggestion + + +def _require_pk(instance: Any) -> int: + """Return a saved model primary key for trends API response payloads.""" + + instance_pk = getattr(instance, "pk", None) + if instance_pk is None: + raise ValueError(f"{instance.__class__.__name__} must be saved first.") + return int(instance_pk) @document_project_owned_viewset( @@ -91,7 +108,7 @@ def get_queryset(self): ) return queryset - def get_serializer_class(self): + def get_serializer_class(self) -> Any: """Return the detail serializer for cluster drill-down responses.""" if self.action == "retrieve": @@ -144,6 +161,120 @@ def velocity_history(self, request, *args, **kwargs): return Response(serializer.data) +@document_project_owned_viewset( + resource_plural="theme suggestions", + resource_singular="theme suggestion", + create_description="Theme suggestions are pipeline-managed rows and are exposed read-only aside from editorial workflow actions.", + tag="Trend Analysis", + action_overrides=build_crud_action_overrides( + ThemeSuggestionSerializer, + resource_plural="theme suggestions for the selected project", + resource_singular="theme suggestion", + ), +) +class ThemeSuggestionViewSet(ProjectOwnedQuerysetMixin, viewsets.ReadOnlyModelViewSet): + """Inspect and resolve project-scoped theme suggestions.""" + + serializer_class = ThemeSuggestionSerializer + filter_backends = [OrderingFilter] + ordering_fields = ["created_at", "velocity_at_creation", "novelty_score", "status"] + ordering = ["status", "-velocity_at_creation", "-created_at"] + queryset = ThemeSuggestion.objects.select_related( + "project", "cluster", "decided_by" + ) + + def get_queryset(self): + """Annotate suggestion clusters with their latest velocity when present.""" + + latest_snapshot_queryset = TopicVelocitySnapshot.objects.filter( + cluster_id=OuterRef("cluster_id") + ).order_by("-computed_at") + return ( + super() + .get_queryset() + .select_related("cluster__dominant_entity") + .prefetch_related( + Prefetch( + "promoted_contents", + queryset=Content.objects.order_by( + "-newsletter_promotion_at", "-published_date" + ), + ) + ) + .annotate( + cluster__velocity_score=Subquery( + latest_snapshot_queryset.values("velocity_score")[:1] + ) + ) + ) + + def get_permissions(self): + """Allow members to read suggestions and contributors to resolve them.""" + + if self.action in {"accept", "dismiss"}: + return [IsProjectContributor()] + return [IsProjectMember()] + + @extend_schema( + summary="Accept theme suggestion", + description="Mark a pending theme suggestion as accepted by the current editor.", + request=None, + responses={ + 200: ThemeSuggestionSerializer, + 403: AUTHENTICATION_REQUIRED_RESPONSE, + }, + tags=["Trend Analysis"], + ) + @action(detail=True, methods=["post"], url_path="accept") + def accept(self, request, *args, **kwargs): + """Accept the selected pending theme suggestion.""" + + suggestion = self.get_object() + try: + accept_theme_suggestion(suggestion, user_id=request.user.id) + except ValueError as exc: + raise serializers.ValidationError( + {"status": "Unable to accept this theme suggestion."} + ) from exc + suggestion = self.get_queryset().get(pk=suggestion.pk) + serializer = self.get_serializer(suggestion) + return Response(serializer.data) + + @extend_schema( + summary="Dismiss theme suggestion", + description="Dismiss a pending theme suggestion and persist the editor's reason.", + request=ThemeSuggestionDismissSerializer, + responses={ + 200: ThemeSuggestionSerializer, + 400: ThemeSuggestionDismissSerializer, + 403: AUTHENTICATION_REQUIRED_RESPONSE, + }, + tags=["Trend Analysis"], + ) + @action(detail=True, methods=["post"], url_path="dismiss") + def dismiss(self, request, *args, **kwargs): + """Dismiss the selected pending theme suggestion.""" + + suggestion = self.get_object() + serializer = ThemeSuggestionDismissSerializer( + data=request.data, + context=self.get_serializer_context(), + ) + serializer.is_valid(raise_exception=True) + try: + dismiss_theme_suggestion( + suggestion, + user_id=request.user.id, + reason=serializer.validated_data["reason"], + ) + except ValueError as exc: + raise serializers.ValidationError( + {"status": "Unable to dismiss this theme suggestion."} + ) from exc + response_serializer = self.get_serializer(suggestion) + return Response(response_serializer.data) + + @document_project_owned_viewset( resource_plural="topic centroid snapshots", resource_singular="topic centroid snapshot", @@ -165,7 +296,6 @@ class TopicCentroidSnapshotViewSet( def get_permissions(self): """Restrict centroid observability to project contributors.""" - return [IsProjectContributor()] @extend_schema( @@ -194,7 +324,7 @@ def summary(self, request, *args, **kwargs): ) serializer = TopicCentroidObservabilitySummarySerializer( { - "project": self.get_project().id, + "project": _require_pk(self.get_project()), "snapshot_count": metrics["snapshot_count"], "active_snapshot_count": metrics["active_snapshot_count"], "avg_drift_from_previous": metrics["avg_drift_from_previous"], diff --git a/trends/api_urls.py b/trends/api_urls.py index 6b5c17a1..97d69cba 100644 --- a/trends/api_urls.py +++ b/trends/api_urls.py @@ -2,7 +2,11 @@ from rest_framework_nested.routers import NestedSimpleRouter -from trends.api import TopicCentroidSnapshotViewSet, TopicClusterViewSet +from trends.api import ( + ThemeSuggestionViewSet, + TopicCentroidSnapshotViewSet, + TopicClusterViewSet, +) def register_project_routes(project_router: NestedSimpleRouter) -> None: @@ -13,6 +17,11 @@ def register_project_routes(project_router: NestedSimpleRouter) -> None: TopicClusterViewSet, basename="project-topic-cluster", ) + project_router.register( + r"themes", + ThemeSuggestionViewSet, + basename="project-theme-suggestion", + ) project_router.register( r"topic-centroid-snapshots", TopicCentroidSnapshotViewSet, diff --git a/trends/migrations/0003_theme_suggestion.py b/trends/migrations/0003_theme_suggestion.py new file mode 100644 index 00000000..75ed0894 --- /dev/null +++ b/trends/migrations/0003_theme_suggestion.py @@ -0,0 +1,92 @@ +import django.db.models.deletion + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("trends", "0002_topic_cluster_models"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="ThemeSuggestion", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=255)), + ("pitch", models.TextField()), + ("why_it_matters", models.TextField()), + ("suggested_angle", models.TextField(blank=True)), + ("velocity_at_creation", models.FloatField()), + ("novelty_score", models.FloatField()), + ( + "status", + models.CharField( + choices=[ + ("pending", "Pending"), + ("accepted", "Accepted"), + ("dismissed", "Dismissed"), + ("used", "Used"), + ], + default="pending", + max_length=16, + ), + ), + ("dismissal_reason", models.TextField(blank=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("decided_at", models.DateTimeField(blank=True, null=True)), + ( + "cluster", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="theme_suggestions", + to="trends.topiccluster", + ), + ), + ( + "decided_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="decided_theme_suggestions", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "project", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="theme_suggestions", + to="projects.project", + ), + ), + ], + options={ + "ordering": ["-created_at", "id"], + "db_table": "core_themesuggestion", + "indexes": [ + models.Index( + fields=["project", "status", "-created_at"], + name="core_themes_project_c0ab5f_idx", + ), + models.Index( + fields=["project", "-velocity_at_creation"], + name="core_themes_project_33bd29_idx", + ), + ], + }, + ), + ] diff --git a/trends/models.py b/trends/models.py index aebc59ab..7f09b03c 100644 --- a/trends/models.py +++ b/trends/models.py @@ -2,6 +2,7 @@ import uuid +from django.conf import settings from django.db import models @@ -103,7 +104,8 @@ class Meta: ] def __str__(self) -> str: - return f"Velocity snapshot for cluster {self.cluster_id}" + cluster_pk = self.cluster.pk + return f"Velocity snapshot for cluster {cluster_pk}" class ContentClusterMembership(models.Model): @@ -142,4 +144,64 @@ class Meta: ] def __str__(self) -> str: - return f"Content {self.content_id} in cluster {self.cluster_id}" + content_pk = self.content.pk + cluster_pk = self.cluster.pk + return f"Content {content_pk} in cluster {cluster_pk}" + + +class ThemeSuggestionStatus(models.TextChoices): + """Workflow states for generated theme suggestions.""" + + PENDING = "pending", "Pending" + ACCEPTED = "accepted", "Accepted" + DISMISSED = "dismissed", "Dismissed" + USED = "used", "Used" + + +class ThemeSuggestion(models.Model): + """Persist one editor-facing theme suggestion derived from a topic cluster.""" + + project = models.ForeignKey( + "projects.Project", + on_delete=models.CASCADE, + related_name="theme_suggestions", + ) + cluster = models.ForeignKey( + TopicCluster, + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="theme_suggestions", + ) + title = models.CharField(max_length=255) + pitch = models.TextField() + why_it_matters = models.TextField() + suggested_angle = models.TextField(blank=True) + velocity_at_creation = models.FloatField() + novelty_score = models.FloatField() + status = models.CharField( + max_length=16, + choices=ThemeSuggestionStatus.choices, + default=ThemeSuggestionStatus.PENDING, + ) + dismissal_reason = models.TextField(blank=True) + created_at = models.DateTimeField(auto_now_add=True) + decided_at = models.DateTimeField(null=True, blank=True) + decided_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="decided_theme_suggestions", + ) + + class Meta: + ordering = ["-created_at", "id"] + db_table = "core_themesuggestion" + indexes = [ + models.Index(fields=["project", "status", "-created_at"]), + models.Index(fields=["project", "-velocity_at_creation"]), + ] + + def __str__(self) -> str: + return self.title diff --git a/trends/serializers.py b/trends/serializers.py index 5469e775..f55f72c3 100644 --- a/trends/serializers.py +++ b/trends/serializers.py @@ -2,8 +2,12 @@ from rest_framework import serializers +from content.models import Content +from core.serializer_mixins import ProjectScopedSerializerMixin from trends.models import ( ContentClusterMembership, + ThemeSuggestion, + ThemeSuggestionStatus, TopicCentroidSnapshot, TopicCluster, TopicVelocitySnapshot, @@ -104,6 +108,83 @@ class Meta(TopicClusterSerializer.Meta): ] +class ThemeSuggestionClusterSummarySerializer(serializers.Serializer): + """Serialize the cluster summary embedded in theme suggestions.""" + + id = serializers.IntegerField() + label = serializers.CharField(allow_blank=True) + member_count = serializers.IntegerField() + velocity_score = serializers.FloatField(read_only=True, allow_null=True) + + +class ThemeSuggestionPromotedContentSerializer(serializers.ModelSerializer): + """Serialize one content row marked for newsletter promotion by a theme.""" + + class Meta: + model = Content + fields = [ + "id", + "url", + "title", + "published_date", + "source_plugin", + "newsletter_promotion_at", + ] + read_only_fields = fields + + +class ThemeSuggestionSerializer(serializers.ModelSerializer): + """Serialize one editor-facing theme suggestion.""" + + cluster = ThemeSuggestionClusterSummarySerializer(read_only=True) + promoted_contents = ThemeSuggestionPromotedContentSerializer( + many=True, read_only=True + ) + decided_by_username = serializers.CharField( + source="decided_by.username", + read_only=True, + allow_null=True, + ) + + class Meta: + model = ThemeSuggestion + fields = [ + "id", + "project", + "cluster", + "title", + "pitch", + "why_it_matters", + "suggested_angle", + "velocity_at_creation", + "novelty_score", + "status", + "dismissal_reason", + "created_at", + "decided_at", + "decided_by", + "decided_by_username", + "promoted_contents", + ] + read_only_fields = fields + + +class ThemeSuggestionDismissSerializer( + ProjectScopedSerializerMixin, serializers.Serializer +): + """Validate dismissal requests for pending theme suggestions.""" + + reason = serializers.CharField(max_length=500) + + def validate_reason(self, value: str) -> str: + """Reject blank dismissal reasons.""" + + normalized = value.strip() + if not normalized: + raise serializers.ValidationError("Dismissal reason cannot be blank.") + return normalized + + class TopicCentroidSnapshotSerializer(serializers.ModelSerializer): """Serialize one persisted topic-centroid recomputation for a project.""" diff --git a/trends/tasks.py b/trends/tasks.py index 3b4ad902..f6c589ca 100644 --- a/trends/tasks.py +++ b/trends/tasks.py @@ -3,13 +3,14 @@ from collections import Counter import math from datetime import datetime, timedelta -from typing import Protocol, cast +from typing import Any, Protocol, cast from celery import shared_task +from django.contrib.auth import get_user_model from django.conf import settings from django.core.cache import cache from django.db import transaction -from django.db.models import Model +from django.db.models import Model, OuterRef, Prefetch, Subquery from django.utils import timezone from core.embeddings import ( @@ -18,12 +19,15 @@ embed_text, upsert_topic_centroid, ) +from core.llm import build_skill_user_prompt, get_skill_definition, openrouter_chat_json from content.models import Content, FeedbackType, UserFeedback from entities.models import Entity, EntityMention from projects.models import Project from .models import ( ContentClusterMembership, + ThemeSuggestion, + ThemeSuggestionStatus, TopicCentroidSnapshot, TopicCluster, TopicVelocitySnapshot, @@ -39,6 +43,11 @@ TOPIC_CLUSTER_MIN_MEMBERS = 3 TOPIC_VELOCITY_TRAILING_DAYS = 7 TOPIC_VELOCITY_EMA_ALPHA = 0.5 +THEME_DETECTION_SKILL_NAME = "theme_detection" +THEME_SUGGESTION_DAILY_CAP = 5 +THEME_NOVELTY_LOOKBACK_DAYS = 30 +THEME_NOVELTY_MIN_SCORE = 0.6 +THEME_CLUSTER_CONTEXT_LIMIT = 5 class DelayedTask(Protocol): @@ -299,7 +308,11 @@ def recompute_topic_velocity(project_id: int) -> dict[str, int]: ) snapshot_count = 0 for cluster in clusters: - memberships = list(cluster.memberships.all()) + memberships = list( + ContentClusterMembership.objects.filter(cluster=cluster).select_related( + "content" + ) + ) published_dates = [ membership.content.published_date for membership in memberships @@ -324,7 +337,11 @@ def recompute_topic_velocity(project_id: int) -> dict[str, int]: trailing_stddev = _population_stddev(trailing_counts, trailing_mean) z_score = _capped_z_score(window_count, trailing_mean, trailing_stddev) normalized_score = (z_score + 3.0) / 6.0 - previous_snapshot = cluster.velocity_snapshots.first() + previous_snapshot = ( + TopicVelocitySnapshot.objects.filter(cluster=cluster) + .order_by("-computed_at") + .first() + ) velocity_score = _smooth_velocity_score( normalized_score, previous_snapshot.velocity_score if previous_snapshot is not None else None, @@ -339,6 +356,10 @@ def recompute_topic_velocity(project_id: int) -> dict[str, int]: velocity_score=velocity_score, ) snapshot_count += 1 + if settings.CELERY_TASK_ALWAYS_EAGER: + generate_theme_suggestions(project_id) + else: + _enqueue_task(generate_theme_suggestions, project_id) return { "project_id": project_id, "clusters_evaluated": len(clusters), @@ -346,6 +367,87 @@ def recompute_topic_velocity(project_id: int) -> dict[str, int]: } +@shared_task(name="core.tasks.generate_theme_suggestions") +def generate_theme_suggestions(project_id: int) -> dict[str, int]: + """Generate pending editor-facing theme suggestions for one project.""" + + now = timezone.now() + clusters = list(_clusters_with_latest_velocity(project_id)) + accepted_history = list( + ThemeSuggestion.objects.filter( + project_id=project_id, + status=ThemeSuggestionStatus.ACCEPTED, + created_at__gte=now - timedelta(days=THEME_NOVELTY_LOOKBACK_DAYS), + ) + .only("title", "pitch", "why_it_matters") + .order_by("-created_at") + ) + created_today = ThemeSuggestion.objects.filter( + project_id=project_id, + status=ThemeSuggestionStatus.PENDING, + created_at__date=now.date(), + ).count() + remaining_slots = max(0, THEME_SUGGESTION_DAILY_CAP - created_today) + created_count = 0 + updated_count = 0 + skipped_count = 0 + + for cluster in clusters: + latest_velocity = float(getattr(cluster, "velocity_score", 0.0) or 0.0) + existing_pending = ( + ThemeSuggestion.objects.filter( + project_id=project_id, + cluster=cluster, + status=ThemeSuggestionStatus.PENDING, + ) + .order_by("-created_at") + .first() + ) + if existing_pending is not None: + existing_pending.velocity_at_creation = latest_velocity + existing_pending.save(update_fields=["velocity_at_creation"]) + updated_count += 1 + continue + if remaining_slots <= 0: + skipped_count += 1 + continue + + cluster_context = _build_theme_cluster_context(cluster) + theme_payload = _synthesize_theme_payload( + cluster=cluster, + cluster_context=cluster_context, + ) + novelty_score = _score_theme_novelty( + project=cluster.project, + theme_payload=theme_payload, + recent_accepted_themes=accepted_history, + ) + if novelty_score < THEME_NOVELTY_MIN_SCORE: + skipped_count += 1 + continue + + ThemeSuggestion.objects.create( + project_id=project_id, + cluster=cluster, + title=str(theme_payload["title"]), + pitch=str(theme_payload["one_sentence_pitch"]), + why_it_matters=str(theme_payload["why_it_matters"]), + suggested_angle=str(theme_payload.get("suggested_angle", "")), + velocity_at_creation=latest_velocity, + novelty_score=novelty_score, + ) + created_count += 1 + remaining_slots -= 1 + + return { + "project_id": project_id, + "clusters_considered": len(clusters), + "created": created_count, + "updated": updated_count, + "skipped": skipped_count, + } + + @shared_task(name="core.tasks.assign_content_to_topic_cluster") def assign_content_to_topic_cluster(content_id: int) -> dict[str, object]: """Assign one content item to the nearest active cluster when it fits.""" @@ -367,9 +469,12 @@ def assign_content_to_topic_cluster(content_id: int) -> dict[str, object]: if content is None: return {"content_id": content_id, "assigned": False, "reason": "missing"} + project_id = _require_pk(content.project) + content_pk = _require_pk(content) + memberships = ContentClusterMembership.objects.filter( - project_id=content.project_id, - content_id=content.id, + project_id=project_id, + content_id=content_pk, ) if not content.is_active or content.published_date < timezone.now() - timedelta( days=TOPIC_CLUSTER_LOOKBACK_DAYS @@ -381,7 +486,7 @@ def assign_content_to_topic_cluster(content_id: int) -> dict[str, object]: return {"content_id": content_id, "assigned": False, "reason": "outside_window"} active_clusters = list( - TopicCluster.objects.filter(project_id=content.project_id, is_active=True) + TopicCluster.objects.filter(project_id=project_id, is_active=True) .prefetch_related("memberships__content") .only( "id", @@ -398,13 +503,21 @@ def assign_content_to_topic_cluster(content_id: int) -> dict[str, object]: vector_cache: dict[int, list[float]] = {} content_vector = _content_vector(content, vector_cache) + memberships_by_cluster: dict[int, list[ContentClusterMembership]] = {} + for membership in ContentClusterMembership.objects.filter( + cluster__in=active_clusters + ).select_related("content"): + memberships_by_cluster.setdefault(_require_pk(membership.cluster), []).append( + membership + ) best_cluster: TopicCluster | None = None best_similarity = -1.0 for cluster in active_clusters: + cluster_pk = _require_pk(cluster) member_contents = [ membership.content - for membership in cluster.memberships.all() - if membership.content_id != content.id + for membership in memberships_by_cluster.get(cluster_pk, []) + if _require_pk(membership.content) != content_pk ] centroid_vector = _cluster_centroid_for_contents(member_contents, vector_cache) if not centroid_vector: @@ -428,17 +541,18 @@ def assign_content_to_topic_cluster(content_id: int) -> dict[str, object]: ContentClusterMembership.objects.create( content=content, cluster=best_cluster, - project_id=content.project_id, + project_id=project_id, similarity=best_similarity, ) cluster_ids_to_refresh = set(removed_cluster_ids) - cluster_ids_to_refresh.add(best_cluster.id) + best_cluster_pk = _require_pk(best_cluster) + cluster_ids_to_refresh.add(best_cluster_pk) for cluster_id in cluster_ids_to_refresh: _refresh_cluster_rollup(cluster_id) return { "content_id": content_id, "assigned": True, - "cluster_id": best_cluster.id, + "cluster_id": best_cluster_pk, "similarity": best_similarity, } @@ -603,21 +717,22 @@ def _sync_topic_clusters( """Persist the current cluster rebuild and retire stale active clusters.""" existing_clusters = list( - TopicCluster.objects.filter(project_id=project_id) - .prefetch_related("memberships") - .order_by("id") + TopicCluster.objects.filter(project_id=project_id).order_by("id") ) - existing_memberships = { - cluster.id: set(cluster.memberships.values_list("content_id", flat=True)) - for cluster in existing_clusters + existing_memberships: dict[int, set[int]] = { + _require_pk(cluster): set() for cluster in existing_clusters } + for cluster_id, content_id in ContentClusterMembership.objects.filter( + cluster__in=existing_clusters + ).values_list("cluster_id", "content_id"): + existing_memberships.setdefault(int(cluster_id), set()).add(int(content_id)) matched_cluster_ids: set[int] = set() clusters_updated = 0 with transaction.atomic(): for group in cluster_groups: contents = cast(list[Content], group["contents"]) - member_ids = {content.id for content in contents} + member_ids = {_require_pk(content) for content in contents} cluster = _match_existing_cluster( existing_clusters, existing_memberships, @@ -635,7 +750,7 @@ def _sync_topic_clusters( ) existing_clusters.append(cluster) else: - matched_cluster_ids.add(cluster.id) + matched_cluster_ids.add(_require_pk(cluster)) cluster.first_seen_at = min( cluster.first_seen_at, min(content.published_date for content in contents), @@ -656,7 +771,7 @@ def _sync_topic_clusters( ] ) - matched_cluster_ids.add(cluster.id) + matched_cluster_ids.add(_require_pk(cluster)) ContentClusterMembership.objects.filter(cluster=cluster).delete() centroid_vector = _cluster_centroid_for_contents(contents, {}) ContentClusterMembership.objects.bulk_create( @@ -676,7 +791,7 @@ def _sync_topic_clusters( clusters_updated += 1 for cluster in existing_clusters: - if cluster.id in matched_cluster_ids: + if _require_pk(cluster) in matched_cluster_ids: continue ContentClusterMembership.objects.filter(cluster=cluster).delete() if cluster.is_active or cluster.member_count != 0: @@ -702,9 +817,10 @@ def _match_existing_cluster( best_overlap_count = 0 best_overlap_ratio = 0.0 for cluster in existing_clusters: - if cluster.id in matched_cluster_ids: + cluster_id = _require_pk(cluster) + if cluster_id in matched_cluster_ids: continue - prior_member_ids = existing_memberships.get(cluster.id, set()) + prior_member_ids = existing_memberships.get(cluster_id, set()) overlap_count = len(prior_member_ids & member_ids) if overlap_count <= 0: continue @@ -755,14 +871,74 @@ def _refresh_cluster_rollup(cluster_id: int) -> None: ) +def accept_theme_suggestion( + theme_suggestion: ThemeSuggestion, *, user_id: int +) -> ThemeSuggestion: + """Mark a pending theme suggestion as accepted.""" + + if theme_suggestion.status != ThemeSuggestionStatus.PENDING: + raise ValueError("Only pending theme suggestions can be accepted.") + accepted_at = timezone.now() + with transaction.atomic(): + theme_suggestion.status = ThemeSuggestionStatus.ACCEPTED + theme_suggestion.decided_at = accepted_at + theme_suggestion.decided_by = get_user_model()._default_manager.get(pk=user_id) + theme_suggestion.dismissal_reason = "" + theme_suggestion.save( + update_fields=["status", "decided_at", "decided_by", "dismissal_reason"] + ) + cluster = theme_suggestion.cluster + project_id = _require_pk(theme_suggestion.project) + if cluster is not None: + cluster_id = _require_pk(cluster) + promoted_content_ids = list( + ContentClusterMembership.objects.filter( + project_id=project_id, + cluster_id=cluster_id, + ).values_list("content_id", flat=True) + ) + if promoted_content_ids: + Content.objects.filter( + project_id=project_id, + id__in=promoted_content_ids, + ).update( + newsletter_promotion_at=accepted_at, + newsletter_promotion_by_id=user_id, + newsletter_promotion_theme=theme_suggestion, + ) + return theme_suggestion + + +def dismiss_theme_suggestion( + theme_suggestion: ThemeSuggestion, + *, + user_id: int, + reason: str, +) -> ThemeSuggestion: + """Mark a pending theme suggestion as dismissed with editorial feedback.""" + + if theme_suggestion.status != ThemeSuggestionStatus.PENDING: + raise ValueError("Only pending theme suggestions can be dismissed.") + theme_suggestion.status = ThemeSuggestionStatus.DISMISSED + theme_suggestion.decided_at = timezone.now() + theme_suggestion.decided_by = get_user_model()._default_manager.get(pk=user_id) + theme_suggestion.dismissal_reason = reason.strip() + theme_suggestion.save( + update_fields=["status", "decided_at", "decided_by", "dismissal_reason"] + ) + return theme_suggestion + + def _resolve_dominant_entity(contents: list[Content]) -> Entity | None: """Return the most frequently referenced entity across clustered content.""" if not contents: return None - content_ids = [content.id for content in contents] + content_ids = [_require_pk(content) for content in contents] entity_counts: Counter[int] = Counter( - content.entity_id for content in contents if content.entity_id is not None + _require_pk(content.entity) + for content in contents + if content.entity is not None ) entity_counts.update( entity_id @@ -778,17 +954,272 @@ def _resolve_dominant_entity(contents: list[Content]) -> Entity | None: return Entity.objects.filter(pk=dominant_entity_id).first() +def _clusters_with_latest_velocity(project_id: int): + """Return active clusters annotated with their latest velocity metrics.""" + + latest_snapshot_queryset = TopicVelocitySnapshot.objects.filter( + cluster_id=OuterRef("pk") + ).order_by("-computed_at") + return ( + TopicCluster.objects.filter( + project_id=project_id, + is_active=True, + member_count__gte=TOPIC_CLUSTER_MIN_MEMBERS, + ) + .select_related("project", "dominant_entity") + .annotate( + velocity_score=Subquery( + latest_snapshot_queryset.values("velocity_score")[:1] + ), + z_score=Subquery(latest_snapshot_queryset.values("z_score")[:1]), + ) + .prefetch_related( + Prefetch( + "memberships", + queryset=ContentClusterMembership.objects.select_related( + "content" + ).order_by("-similarity", "-assigned_at"), + ) + ) + .order_by("-velocity_score", "-last_seen_at") + ) + + +def _build_theme_cluster_context(cluster: TopicCluster) -> dict[str, Any]: + """Serialize the most relevant cluster context for theme generation.""" + + memberships = list( + ContentClusterMembership.objects.filter(cluster=cluster).select_related( + "content" + )[:THEME_CLUSTER_CONTEXT_LIMIT] + ) + recent_feedback = _recent_feedback_signals( + [_require_pk(membership.content) for membership in memberships] + ) + cluster_id = _require_pk(cluster) + dominant_entity = cluster.dominant_entity + dominant_entity_id = ( + _require_pk(dominant_entity) if dominant_entity is not None else None + ) + return { + "cluster_id": cluster_id, + "dominant_entity": ( + { + "id": dominant_entity_id, + "name": dominant_entity.name, + "type": dominant_entity.type, + } + if dominant_entity_id is not None and dominant_entity is not None + else None + ), + "velocity_score": float(getattr(cluster, "velocity_score", 0.0) or 0.0), + "z_score": float(getattr(cluster, "z_score", 0.0) or 0.0), + "member_count": cluster.member_count, + "latest_members": [ + { + "content_id": _require_pk(membership.content), + "title": membership.content.title, + "url": membership.content.url, + "source_plugin": membership.content.source_plugin, + "published_date": membership.content.published_date.isoformat(), + "summary": membership.content.content_text[:400], + "similarity": membership.similarity, + "feedback_signals": recent_feedback.get( + _require_pk(membership.content), {} + ), + } + for membership in memberships + ], + } + + +def _synthesize_theme_payload( + *, + cluster: TopicCluster, + cluster_context: dict[str, Any], +) -> dict[str, str]: + """Generate one editor-facing theme suggestion payload.""" + + if settings.OPENROUTER_API_KEY: + try: + response = openrouter_chat_json( + model=settings.AI_SUMMARIZATION_MODEL, + system_prompt=get_skill_definition( + THEME_DETECTION_SKILL_NAME + ).instructions_markdown, + user_prompt=build_skill_user_prompt( + THEME_DETECTION_SKILL_NAME, + { + "project_topic": cluster.project.topic_description, + "cluster_context": cluster_context, + "recent_accepted_themes": [], + }, + ), + ) + payload = response.payload + return { + "title": str(payload.get("title", "")).strip() + or _fallback_theme_title(cluster_context), + "one_sentence_pitch": str(payload.get("one_sentence_pitch", "")).strip() + or _fallback_theme_pitch(cluster_context), + "why_it_matters": str(payload.get("why_it_matters", "")).strip() + or _fallback_theme_why(cluster_context), + "suggested_angle": str(payload.get("suggested_angle", "")).strip(), + } + except Exception: + pass + + return { + "title": _fallback_theme_title(cluster_context), + "one_sentence_pitch": _fallback_theme_pitch(cluster_context), + "why_it_matters": _fallback_theme_why(cluster_context), + "suggested_angle": _fallback_theme_angle(cluster_context), + } + + +def _score_theme_novelty( + *, + project: Project, + theme_payload: dict[str, str], + recent_accepted_themes: list[ThemeSuggestion], +) -> float: + """Estimate how novel a generated theme is versus recent accepted themes.""" + + heuristic_score = ( + _heuristic_theme_novelty_score(theme_payload, recent_accepted_themes) + if recent_accepted_themes + else 1.0 + ) + if settings.OPENROUTER_API_KEY: + try: + response = openrouter_chat_json( + model=settings.AI_RELEVANCE_MODEL, + system_prompt=( + "Score the novelty of a proposed newsletter theme against recent accepted themes. " + "Return JSON with fields novelty_score and explanation. novelty_score must be between 0 and 1, where 1 is highly novel." + ), + user_prompt=( + f"project_topic:\n{project.topic_description}\n\n" + f"candidate_theme:\n{theme_payload}\n\n" + f"recent_accepted_themes:\n{[{'title': theme.title, 'pitch': theme.pitch, 'why_it_matters': theme.why_it_matters} for theme in recent_accepted_themes[:10]]}\n\n" + "Return only a JSON object with fields novelty_score and explanation." + ), + ) + payload = response.payload + novelty_score = float(payload.get("novelty_score", heuristic_score)) + return max(0.0, min(1.0, novelty_score)) + except Exception: + pass + return heuristic_score + + +def _recent_feedback_signals(content_ids: list[int]) -> dict[int, dict[str, int]]: + """Summarize recent feedback counts for the supplied content rows.""" + + if not content_ids: + return {} + feedback_counts: dict[int, dict[str, int]] = { + content_id: {"upvotes": 0, "downvotes": 0} for content_id in content_ids + } + for content_id, feedback_type in UserFeedback.objects.filter( + content_id__in=content_ids + ).values_list("content_id", "feedback_type"): + if feedback_type == FeedbackType.UPVOTE: + feedback_counts[content_id]["upvotes"] += 1 + elif feedback_type == FeedbackType.DOWNVOTE: + feedback_counts[content_id]["downvotes"] += 1 + return feedback_counts + + +def _fallback_theme_title(cluster_context: dict[str, Any]) -> str: + """Build a deterministic fallback theme title from cluster context.""" + + dominant_entity = cluster_context.get("dominant_entity") or {} + if dominant_entity.get("name"): + return f"Why {dominant_entity['name']} keeps surfacing right now" + latest_members = cluster_context.get("latest_members", []) + if latest_members: + return str(latest_members[0]["title"])[:255] + return "Emerging theme" + + +def _fallback_theme_pitch(cluster_context: dict[str, Any]) -> str: + """Build a deterministic fallback pitch from cluster velocity context.""" + + latest_members = cluster_context.get("latest_members", []) + if latest_members: + return ( + f"This cluster is accelerating across {cluster_context.get('member_count', 0)} recent items, " + f"with '{latest_members[0]['title']}' leading the current signal." + ) + return "This cluster is accelerating across the project's recent content." + + +def _fallback_theme_why(cluster_context: dict[str, Any]) -> str: + """Explain why the fallback theme matters now.""" + + return ( + f"The cluster's current velocity score is {cluster_context.get('velocity_score', 0.0):.2f}, " + f"which indicates a faster-than-baseline increase in related coverage." + ) + + +def _fallback_theme_angle(cluster_context: dict[str, Any]) -> str: + """Suggest a deterministic editorial angle when the LLM is unavailable.""" + + dominant_entity = cluster_context.get("dominant_entity") or {} + if dominant_entity.get("name"): + return f"Explain what changed around {dominant_entity['name']} and why it matters for the project topic." + return "Identify the common thread across the latest members and explain why it is accelerating now." + + +def _heuristic_theme_novelty_score( + theme_payload: dict[str, str], + recent_accepted_themes: list[ThemeSuggestion], +) -> float: + """Estimate novelty using simple token overlap with accepted themes.""" + + candidate_tokens = _normalized_theme_tokens( + f"{theme_payload.get('title', '')} {theme_payload.get('one_sentence_pitch', '')}" + ) + if not candidate_tokens: + return 0.0 + max_overlap = 0.0 + for theme in recent_accepted_themes: + prior_tokens = _normalized_theme_tokens(f"{theme.title} {theme.pitch}") + if not prior_tokens: + continue + overlap = len(candidate_tokens & prior_tokens) / len( + candidate_tokens | prior_tokens + ) + max_overlap = max(max_overlap, overlap) + return max(0.0, min(1.0, 1.0 - max_overlap)) + + +def _normalized_theme_tokens(text: str) -> set[str]: + """Normalize free text into a small token set for novelty heuristics.""" + + return { + token + for token in "".join( + char.lower() if char.isalnum() else " " for char in text + ).split() + if len(token) > 2 + } + + def _content_vector( content: Content, vector_cache: dict[int, list[float]], ) -> list[float]: """Return one content embedding, caching repeated lookups within a task.""" - vector = vector_cache.get(content.id) + content_pk = _require_pk(content) + vector = vector_cache.get(content_pk) if vector is not None: return vector vector = embed_text(build_content_embedding_text(content)) - vector_cache[content.id] = vector + vector_cache[content_pk] = vector return vector @@ -868,7 +1299,7 @@ def _create_topic_velocity_snapshot( snapshot = TopicVelocitySnapshot.objects.create( cluster=cluster, - project_id=cluster.project_id, + project_id=_require_pk(cluster.project), window_count=window_count, trailing_mean=trailing_mean, trailing_stddev=trailing_stddev, diff --git a/users/admin.py b/users/admin.py index cb349749..e6e3cc83 100644 --- a/users/admin.py +++ b/users/admin.py @@ -1,5 +1,7 @@ """Django admin registration for the custom application user model.""" +from typing import Any, cast + from django.contrib import admin from django.contrib.auth.admin import UserAdmin @@ -21,27 +23,35 @@ class AppUserAdmin(UserAdmin): "is_staff", "is_active", ) - fieldsets = UserAdmin.fieldsets + ( - ( - "Profile", - { - "fields": ( - "display_name", - "avatar", - "bio", - "timezone", - ) - }, - ), + fieldsets = cast( + Any, + [ + *(UserAdmin.fieldsets or []), + ( + "Profile", + { + "fields": ( + "display_name", + "avatar", + "bio", + "timezone", + ) + }, + ), + ], ) - add_fieldsets = UserAdmin.add_fieldsets + ( - ( - "Profile", - { - "fields": ( - "display_name", - "email", - ) - }, - ), + add_fieldsets = cast( + Any, + [ + *(UserAdmin.add_fieldsets or []), + ( + "Profile", + { + "fields": ( + "display_name", + "email", + ) + }, + ), + ], ) diff --git a/users/api.py b/users/api.py index a839cd73..2c06a128 100644 --- a/users/api.py +++ b/users/api.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Any, Protocol, cast + from django.conf import settings from django.utils import timezone from drf_spectacular.utils import extend_schema @@ -21,6 +23,25 @@ from users.tasks import generate_avatar_thumbnail +class DelayedTask(Protocol): + """Protocol for Celery tasks dispatched through ``delay``.""" + + def delay(self, *args: object, **kwargs: object) -> object: + pass + + +def _enqueue_task(task: object, *args: object) -> None: + """Dispatch a Celery task through a typed ``delay`` seam.""" + + cast(DelayedTask, task).delay(*args) + + +def _validated_data(serializer: Any) -> dict[str, Any]: + """Return validated avatar-upload serializer data with a concrete mapping type.""" + + return cast(dict[str, Any], serializer.validated_data) + + def _delete_avatar_assets(user: AppUser) -> None: """Delete the user's stored avatar and generated thumbnail files.""" @@ -73,13 +94,13 @@ def post(self, request): user = request.user _delete_avatar_assets(user) - user.avatar = serializer.validated_data["avatar"] + user.avatar = _validated_data(serializer)["avatar"] user.save(update_fields=["avatar"]) if settings.CELERY_TASK_ALWAYS_EAGER: generate_avatar_thumbnail(user.id) else: - generate_avatar_thumbnail.delay(user.id) + _enqueue_task(generate_avatar_thumbnail, user.id) return Response(ProfileSerializer(user).data, status=status.HTTP_200_OK) diff --git a/users/models.py b/users/models.py index 6f7a3cfc..91753a07 100644 --- a/users/models.py +++ b/users/models.py @@ -3,7 +3,7 @@ from __future__ import annotations import secrets -from typing import ClassVar +from typing import Any, ClassVar from django.contrib.auth.models import AbstractUser, Group, Permission from django.db import models @@ -72,9 +72,9 @@ class AppUser(AbstractUser): db_table="auth_user_user_permissions", ) - objects: ClassVar[AppUserManager] = AppUserManager() + objects: ClassVar[Any] = AppUserManager() - class Meta: + class Meta(AbstractUser.Meta): db_table = "auth_user" verbose_name = "User" verbose_name_plural = "Users" diff --git a/users/tests/test_profile_api.py b/users/tests/test_profile_api.py index e49a6670..1d44ab79 100644 --- a/users/tests/test_profile_api.py +++ b/users/tests/test_profile_api.py @@ -3,6 +3,7 @@ from __future__ import annotations from io import BytesIO +from typing import Any, cast import pytest from django.core.files.uploadedfile import SimpleUploadedFile @@ -15,6 +16,12 @@ pytestmark = pytest.mark.django_db +def _response(value: object) -> Any: + """Return a typed API response for response assertions in tests.""" + + return cast(Any, value) + + def make_avatar_file( *, filename: str = "avatar.png", @@ -41,7 +48,7 @@ def test_profile_get_returns_the_authenticated_user(tmp_path): client.force_authenticate(user) with override_settings(MEDIA_ROOT=tmp_path): - response = client.get("/api/v1/profile/") + response = _response(client.get("/api/v1/profile/")) assert response.status_code == 200 assert response.json()["username"] == "profile-reader" @@ -59,14 +66,16 @@ def test_profile_patch_updates_profile_fields(tmp_path): client.force_authenticate(user) with override_settings(MEDIA_ROOT=tmp_path): - response = client.patch( - "/api/v1/profile/", - { - "display_name": "Profile Editor", - "bio": "Owns the editorial calendar.", - "timezone": "America/New_York", - }, - format="json", + response = _response( + client.patch( + "/api/v1/profile/", + { + "display_name": "Profile Editor", + "bio": "Owns the editorial calendar.", + "timezone": "America/New_York", + }, + format="json", + ) ) user.refresh_from_db() @@ -85,10 +94,12 @@ def test_profile_avatar_upload_returns_avatar_and_thumbnail_urls(tmp_path): client.force_authenticate(user) with override_settings(MEDIA_ROOT=tmp_path): - response = client.post( - "/api/v1/profile/avatar/", - {"avatar": make_avatar_file()}, - format="multipart", + response = _response( + client.post( + "/api/v1/profile/avatar/", + {"avatar": make_avatar_file()}, + format="multipart", + ) ) user.refresh_from_db() @@ -112,14 +123,16 @@ def test_profile_avatar_delete_clears_avatar_and_thumbnail(tmp_path): client.force_authenticate(user) with override_settings(MEDIA_ROOT=tmp_path): - upload_response = client.post( - "/api/v1/profile/avatar/", - {"avatar": make_avatar_file()}, - format="multipart", + upload_response = _response( + client.post( + "/api/v1/profile/avatar/", + {"avatar": make_avatar_file()}, + format="multipart", + ) ) assert upload_response.status_code == 200 - response = client.delete("/api/v1/profile/avatar/") + response = _response(client.delete("/api/v1/profile/avatar/")) user.refresh_from_db() thumbnail_path = tmp_path / f"avatars/{user.id}/thumb.webp"