From 2dbb2433bb5dd7cc02b2ad52c7bff6acbb4eb66b Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 14:15:52 +0700 Subject: [PATCH 01/17] docs(cache): add research cache design --- ...26-08-26-supabase-research-cache-design.md | 534 ++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md diff --git a/docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md b/docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md new file mode 100644 index 0000000..f95bc59 --- /dev/null +++ b/docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md @@ -0,0 +1,534 @@ +# Supabase Research Cache Design + +**Date:** 2026-08-26 + +**Status:** Draft for final review + +**Scope:** Shared, non-expiring research cache backed by Supabase + +## 1. Goal + +Return previously completed company research without running Serper, scraping, +profile synthesis, or analysis again. When no safe cache match exists, run the +current research pipeline and persist a complete snapshot for later requests. + +Cache entries do not expire. A user refreshes a cached company explicitly when +new research is required. + +## 2. Current Context + +The research endpoint streams a LangGraph workflow over SSE. The workflow +currently loads and writes versioned `CompanyProfile` and `ProfileDiff` records +through `StorageAdapter`, but it still executes all source and LLM work before +loading the previous profile. Supabase stores profiles and diffs; it does not +store `AnalysisReport`. Company identity currently falls back to +`slugify(input.name)`, which is not safe as a canonical identifier. + +Relevant code: + +- `src/app/api/research/route.ts` +- `src/app/hooks/use-research.ts` +- `src/app/page.tsx` +- `src/modules/workflow/index.ts` +- `src/adapters/storage/types.ts` +- `src/adapters/storage/supabase.ts` +- `src/adapters/storage/memory.ts` +- `src/config/index.ts` +- `src/lib/types.ts` +- `src/observability/langfuse.ts` + +## 3. Architectural Decision + +Use a read-through flow at the research route with a dedicated research-cache +module. The cache module is the seam between request identity, Supabase lookup, +and complete research snapshots. + +The route performs cache resolution before constructing the LLM, search, +scraper, registry, profile, analyst, or workflow modules. A cache hit therefore +does not require provider credentials and cannot accidentally call a paid +provider. + +The cache module exposes the minimum interface needed by callers: + +- Resolve an input into a hit, suggestions, a miss, or an identity conflict. +- Resolve or create the canonical identity used by a pipeline run. +- Persist one complete profile/report/diff snapshot. + +Supabase remains the persistent shared store. No browser `localStorage` cache +and no additional in-process LRU cache are part of this change. + +## 4. Data Model + +### 4.1 `company_identities` + +Add one canonical identity row per known company: + +| Column | Type | Rules | +|---|---|---| +| `id` | `text` | Primary key. Existing IDs are retained; new IDs are UUID strings. | +| `tax_id` | `text` | Nullable normalized tax ID. | +| `normalized_domain` | `text` | Nullable normalized hostname. Not unique. | +| `normalized_name` | `text` | Required normalized company name. Not unique. | +| `created_at` | `timestamptz` | Creation time. | +| `updated_at` | `timestamptz` | Last identity metadata change, not research freshness. | + +Required indexes and constraints: + +- Primary key on `id`. +- Partial unique index on `tax_id` where `tax_id IS NOT NULL`. +- Non-unique index on `normalized_domain`. +- Non-unique index on `normalized_name`. + +`normalized_domain` is intentionally non-unique because multiple legal entities +may use one corporate domain. `normalized_name` is never used as an automatic +cache hit. + +### 4.2 `company_profiles` + +Keep versioned rows keyed by `(id, version)` and add: + +- `analysis_report JSONB NULL`. +- Foreign key `id → company_identities(id)` after existing data is backfilled. +- Partial lookup index `(id, version DESC) WHERE analysis_report IS NOT NULL`. + +`company_profiles.updated_at` is the research synchronization time exposed to +the client as `lastSyncedAt`. It is distinct from +`company_identities.updated_at`. + +A row is cacheable only when `analysis_report IS NOT NULL`. The current analyst +produces one structured report after a single `completeStructured` call; partial +report streaming is not supported. Therefore a separate completion-status +column is unnecessary. + +### 4.3 `company_diffs` + +Keep the existing table and add a foreign key from `company_id` to +`company_identities(id)`. A cached snapshot includes only the diff whose +`to_version` equals the selected profile version; version 1 returns `null`. + +### 4.4 Backfill + +Backfill one `company_identities` row for each existing distinct profile ID. +Derive identity fields from the latest profile version. Preserve every existing +profile ID so existing profile/diff references remain valid. New companies use +`crypto.randomUUID()` converted to a string; `slugify(name)` is no longer an +identity source. + +## 5. Normalization Rules + +Normalization happens at the cache trust boundary before lookup or persistence. +Input and pipeline-derived identity values use the same functions. + +### Tax ID + +- Trim surrounding whitespace. +- Remove spaces, dots, and hyphens. +- Accept only 10 or 13 decimal digits after normalization. +- Return `null` for an absent value; reject a present malformed value. + +### Domain + +- Parse the already URL-validated website with the platform `URL` class. +- Use `hostname`, lowercase it, remove a trailing dot, and remove one leading + `www.`. +- Ignore scheme, credentials, port, path, query, and fragment. +- Return `null` when no website is supplied. + +### Company name + +- Apply Unicode NFKC normalization. +- Trim, lowercase using the Vietnamese locale, and collapse consecutive + whitespace. +- Retain Vietnamese diacritics, punctuation, and legal suffixes such as `TNHH` + and `CP`. +- Never use a name match as an automatic cache hit. + +## 6. Lookup Rules + +Lookup uses the following order: + +1. `taxId` +2. normalized domain +3. normalized name + +Decision rules: + +| Condition | Result | +|---|---| +| Tax ID resolves to one identity and supplied domain is compatible | Automatic hit | +| Domain resolves to exactly one identity and no tax ID conflicts | Automatic hit | +| Domain resolves to multiple identities | Suggestions requiring confirmation | +| Name resolves to one or more identities | Suggestions requiring confirmation | +| No layer resolves | Miss; run the pipeline immediately | +| Supplied tax ID and domain resolve to different identities | `identity_conflict` | +| Cached profile/report/diff fails runtime validation | `cache_invalid`; treat as miss | + +When a tax ID resolves to identity A, a supplied domain is compatible when it +has no candidates or when every domain candidate set used for this request +contains A. A domain candidate set that excludes A is an identity conflict. A +multi-candidate domain without a tax ID remains a suggestion result. + +No automatic identity merge is permitted. Conflicts require corrected user +input or later manual/admin resolution. + +## 7. Request Contract + +The request is a discriminated union: + +```typescript +type ResearchRequest = + | { input: CompanyInput; cache?: undefined } + | { + input: CompanyInput; + cache: { action: "select"; companyId: string }; + } + | { + input: CompanyInput; + cache: { action: "refresh"; companyId: string }; + } + | { + input: CompanyInput; + cache: { action: "bypass" }; + }; +``` + +`select` validation recomputes the full suggestion candidate set from the +supplied input, including ambiguous-domain and normalized-name candidates, and +accepts the requested `companyId` only when it belongs to that set. Merely +checking that the ID exists is insufficient. + +`refresh` resolves the supplied input through the same tax ID → domain → name +chain. The selected `companyId` must be compatible with the result. A mismatch +returns an explicit conflict; it never silently falls back to bypass. + +`bypass` is used only after a user rejects non-empty suggestions. A normal +zero-result lookup starts the pipeline immediately without an extra client +round trip. + +## 8. SSE Contract + +Add these events while retaining the existing profile, diff, analysis, error, +and done events: + +```typescript +type CacheHitMatchedBy = "tax_id" | "domain" | "selected"; + +type CacheSuggestion = { + companyId: string; + officialName: string; + taxId?: string; + domain?: string; + lastSyncedAt: string; +}; + +type CacheStreamEvent = + | { + event: "cache:hit"; + data: { + companyId: string; + matchedBy: CacheHitMatchedBy; + version: number; + lastSyncedAt: string; + }; + } + | { + event: "cache:suggestions"; + data: { suggestions: CacheSuggestion[] }; + }; +``` + +A cache hit streams, in order: + +1. `cache:hit` +2. `profile:ready` +3. `diff:ready` +4. `analysis:ready` +5. `done` + +Every SSE execution path terminates explicitly. A fatal streaming failure emits +`error` and then `done`; the server never silently closes a stream it has +started. + +Extend the existing error event: + +```typescript +type ResearchErrorCode = + | "identity_conflict" + | "cache_invalid" + | "persist_failed" + | "research_failed"; + +type ResearchErrorEvent = { + event: "error"; + data: { + code?: ResearchErrorCode; + message: string; + source?: SourceName; + }; +}; +``` + +`cache_invalid` is recoverable: emit the error event, continue with a cache +miss, and finish with the pipeline's normal final events. `persist_failed` and +post-pipeline `identity_conflict` are fatal: emit the error event followed by +`done`, with no profile/diff/analysis final events. + +## 9. Server Flow + +### Default lookup + +1. Parse and validate `ResearchRequest`. +2. Construct only the storage/cache dependencies. +3. Normalize input and perform lookup. +4. Return HTTP errors that occur before SSE starts. +5. On hit, validate and stream the complete snapshot without constructing paid + providers. +6. On suggestions, stream the candidates and `done`. +7. On an empty lookup, resolve/create an identity and run the pipeline. + +### Pipeline miss + +1. Resolve or create a canonical identity. +2. Construct LLM, search, scraper, registry, profile, analyst, and workflow + modules only after the miss is confirmed. +3. Pass `companyId` and `existingProfile` into the workflow. The workflow does + not derive identity with `slugify(name)` and does not repeat the cache read. +4. Stream source and build progress only; hold final profile/diff/analysis + events until validation and persistence succeed. +5. Normalize the tax ID/domain/name found in the completed profile and validate + them against `company_identities` again. +6. Persist identity metadata, profile, report, and matching diff atomically. +7. Stream final profile, diff, analysis, and done events. + +### Refresh + +1. Revalidate the client-provided company ID against the current input. +2. Load its latest complete snapshot as the previous profile. +3. Bypass the cache response and run the pipeline under the same canonical ID. +4. Persist the next version and its diff. + +### Post-pipeline conflict + +If newly discovered identity values belong to another company, do not merge, +persist, or stream the completed result. Record conflict telemetry, emit +`error.code = "identity_conflict"`, then emit `done`. + +## 10. Database Concurrency + +Use database RPCs because `supabase-js` cannot group multiple PostgREST calls +into one client-controlled transaction. + +### Resolve/create identity RPC + +The RPC uses separate branches: + +- With tax ID: rely on the partial unique index and `INSERT ... ON CONFLICT` + behavior. Re-read and validate the winning identity; do not overwrite or + merge conflicting domain/name data automatically. +- Without tax ID but with domain: acquire + `pg_advisory_xact_lock(hashtext(normalized_domain))`, recheck the domain/name, + and insert or reuse inside the same transaction. +- Name only: create a UUID identity after suggestions have been rejected or no + suggestions exist. Duplicate name-only identities remain possible because a + name is intentionally not treated as unique. + +The lock is transaction-scoped and releases automatically on commit or +rollback. A session-level advisory lock is forbidden. + +The identity lock prevents duplicate identity rows; it does not prevent two +simultaneous cache misses from both running the expensive pipeline. Profile and +diff upserts preserve database integrity if this occurs, but duplicate provider +cost remains a documented ceiling. + +### Persist snapshot RPC + +Persist the identity metadata update, complete profile/report row, and matching +diff in one transaction. Revalidate pipeline-derived keys in this transaction +before writing. Any conflict or write failure rolls back the whole snapshot. + +## 11. Runtime Validation + +Validate Supabase JSONB before it crosses the cache interface. The runtime +schema covers the complete `CompanyProfile`, `AnalysisReport`, and optional +`ProfileDiff`, including version/company-ID agreement. + +An invalid snapshot: + +1. Emits `cache_invalid` telemetry. +2. Emits a recoverable SSE error when the stream has started. +3. Is treated as a miss. +4. Is never returned to the client as a cache hit. + +## 12. HTTP Error Contract + +Errors detected before the SSE response use JSON: + +```typescript +type ResearchHttpError = { + error: { + code: + | "invalid_request" + | "invalid_cache_selection" + | "identity_conflict" + | "cache_unavailable"; + message: string; + }; +}; +``` + +| Status | Code | Behavior | +|---|---|---| +| `400` | `invalid_request` | Malformed input or cache action. | +| `400` | `invalid_cache_selection` | Existing company ID is not a candidate for the current input. | +| `409` | `identity_conflict` | Strong identifiers disagree. No provider is constructed or called. | +| `503` | `cache_unavailable` | Supabase lookup/RPC is unavailable. No provider is constructed or called. | + +The client handles JSON errors before opening/consuming SSE and handles SSE +errors after a stream begins. In both cases it reaches an explicit terminal UI +state. + +## 13. Telemetry + +Reuse the existing Langfuse integration. Do not add a logger or observability +dependency. + +Record: + +- `cacheOutcome`: `hit`, `miss`, `suggestions`, `refresh`, `bypass`, `conflict`, + or `invalid`. +- `matchedBy`: `tax_id`, `domain`, `normalized_name`, or `selected`. +- Resolved company ID, selected profile version, last synchronization time, and + lookup duration. +- On conflict: both company IDs, key type, and a keyed fingerprint. Never emit + the raw tax ID or domain. + +Use Node's built-in `crypto.createHmac("sha256", secret)` with the dedicated +server secret `CACHE_TELEMETRY_HMAC_SECRET`. A plain SHA-256 hash is forbidden +for tax IDs because their input space is small. Use the same HMAC helper for +domains for consistency. If the secret is absent, omit the fingerprint rather +than logging the raw value or failing the research request. + +## 14. Client Experience + +- A cache hit displays the existing result immediately, its last synchronized + time, and a visible “Cập nhật lại” action. +- Name or ambiguous-domain suggestions show company name, tax ID when present, + domain, and last synchronized time. +- Selecting a suggestion submits `action: "select"`. +- Rejecting non-empty suggestions submits `action: "bypass"`. +- Refresh submits `action: "refresh"` for the currently displayed company. +- Recoverable `cache_invalid` informs the user that cached data was unusable and + that fresh research is running. +- Fatal errors stop loading and show the server message. + +## 15. Verification Strategy + +Use the existing Vitest suite and its current mock-adapter patterns. Do not add +a test framework. + +### Unit tests + +- Tax ID, domain, and name normalization. +- Lookup priority and decision table. +- Tax/domain conflict detection. +- Ambiguous domain and name suggestions. +- Complete-snapshot runtime validation. +- Latest complete profile selection and diff `to_version` matching. +- HMAC fingerprint determinism, secret separation, and omission without a + secret. + +### Route tests + +- Cache hit streams the complete event sequence and never constructs/calls LLM, + Serper, or scraper adapters. +- Supabase/RPC failure returns `503 cache_unavailable` and never + constructs/calls paid providers. +- A corrupt snapshot emits `cache_invalid`, becomes a miss, and runs the + pipeline. +- `select` rejects an existing, valid company ID that is not in the current + input's suggestion set. +- `refresh` rejects an existing, valid company ID belonging to an unrelated + company. +- An empty lookup starts the pipeline without a bypass round trip. +- A pre-stream identity conflict returns `409`. +- A post-pipeline conflict emits `error.code = "identity_conflict"`, then + `done`, and emits no final profile/diff/analysis events. +- Persist failure emits `error.code = "persist_failed"`, then `done`, and emits + no final profile/diff/analysis events. + +### Workflow tests + +- Workflow uses the supplied canonical company ID and previous profile. +- Refresh creates the next version and a diff whose `toVersion` matches it. +- Workflow no longer reads existing data via `slugify(name)`. + +### Database integration tests + +- Two genuinely separate database clients/connections invoke the domain-only + resolve/create RPC concurrently for the same normalized domain and name. +- Assert both calls return the same identity and only one matching identity row + exists. +- A sequential or same-connection test is insufficient because it cannot prove + the advisory lock works under concurrent transactions. +- Verify tax-ID `ON CONFLICT`, transactional rollback, the partial complete-row + index query, and snapshot/diff foreign-key integrity. + +### UI verification + +- Verify name suggestions, rejecting suggestions, selecting a cached company, + last-synchronized time, recoverable cache-invalid state, and refresh. +- Capture before/after screenshots for the changed UI states. + +### Final verification + +- Full Vitest suite. +- Lint. +- Typecheck. +- Production build. + +## 16. Security Properties + +- Client-supplied company IDs are always rebound to the current normalized + input before read or refresh. +- Strong-identifier conflicts fail closed and never merge identities. +- Cached JSONB is runtime-validated before use. +- Paid providers are not constructed on hit, invalid selection, conflict, or + cache-backend failure. +- Advisory locks are transaction-scoped. +- Telemetry never stores raw tax IDs or domains. +- Database functions use `SECURITY INVOKER` by default, explicitly qualify + referenced schemas, and receive only the grants required by the server role. +- Existing Supabase keys remain server-only; no service-role key is exposed to + the browser. + +## 17. Deferred Scope + +- TTL or automatic freshness invalidation. +- In-process LRU/cache layer in front of Supabase. +- Automatic identity merge or an admin merge interface. +- Resuming analysis from partially persisted findings/profile data. +- Full cache-stampede prevention for concurrent first misses. +- Refresh rate limiting. Until a later phase adds per-user/company limits, the + existing global research guards remain the only cost ceiling; this risk must + be revisited before exposing refresh to untrusted high-volume traffic. + +## 18. Acceptance Criteria + +The design is complete when all of the following are true: + +1. A safe cache hit returns profile, matching diff, and analysis without + constructing or calling paid providers. +2. A miss runs the existing pipeline and atomically stores a complete reusable + snapshot. +3. Name matches and ambiguous domains require server-validated user selection. +4. Refresh is explicit, server-bound to the supplied input, and creates a new + version/diff. +5. Identity conflicts never auto-merge and always produce observable terminal + errors. +6. Supabase failure cannot trigger an expensive uncached run. +7. Concurrent domain-only identity creation is verified with independent + database connections. +8. Every SSE path ends with `done`, including fatal errors. +9. The UI displays cache age and offers manual refresh. +10. Existing research, export, observability, and storage tests continue to + pass. From 7e16b169da5b5c2b296398b90a7e24213c1e2ed3 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 14:32:14 +0700 Subject: [PATCH 02/17] docs(cache): split implementation into sprints --- .../README.md | 50 ++ .../sprint-01-contracts-and-normalization.md | 490 +++++++++++++++ .../sprint-02-supabase-schema-and-storage.md | 573 ++++++++++++++++++ .../sprint-03-server-read-through-flow.md | 494 +++++++++++++++ ...print-04-client-suggestions-and-refresh.md | 460 ++++++++++++++ ...rint-05-telemetry-and-release-hardening.md | 402 ++++++++++++ 6 files changed, 2469 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/README.md create mode 100644 docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-01-contracts-and-normalization.md create mode 100644 docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-02-supabase-schema-and-storage.md create mode 100644 docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-03-server-read-through-flow.md create mode 100644 docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-04-client-suggestions-and-refresh.md create mode 100644 docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-05-telemetry-and-release-hardening.md diff --git a/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/README.md b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/README.md new file mode 100644 index 0000000..8924bbf --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/README.md @@ -0,0 +1,50 @@ +# Supabase Research Cache Sprint Roadmap + +**Spec:** [`../../specs/2026-08-26-supabase-research-cache-design.md`](../../specs/2026-08-26-supabase-research-cache-design.md) + +This roadmap splits the approved cache design into dependency-ordered, +independently reviewable deliverables. A sprint is a technical delivery slice, +not a calendar estimate. Execute and review one sprint before starting its +successor. + +## Sprint sequence + +| Sprint | Deliverable | Depends on | Completion gate | +|---|---|---|---| +| [01 — Contracts and normalization](./sprint-01-contracts-and-normalization.md) | Typed request/SSE/snapshot contracts, safe identity normalization, pure lookup decisions | Approved spec | Focused unit tests, typecheck, Node 22 build | +| [02 — Supabase schema and storage](./sprint-02-supabase-schema-and-storage.md) | Identity schema, transactional RPCs, complete-snapshot storage adapter | Sprint 01 | Local database reset, adapter tests, two-client concurrency test | +| [03 — Server read-through flow](./sprint-03-server-read-through-flow.md) | Cache-first route, lazy paid providers, canonical workflow identity, atomic persist | Sprint 02 | Route/workflow tests prove hit/miss/conflict/error behavior | +| [04 — Client suggestions and refresh](./sprint-04-client-suggestions-and-refresh.md) | Safe company confirmation, cache metadata, manual refresh UI | Sprint 03 | State-transition tests plus visual before/after verification | +| [05 — Telemetry and release hardening](./sprint-05-telemetry-and-release-hardening.md) | HMAC telemetry, remaining negative-path coverage, advisors and release checks | Sprint 04 | Full test/lint/typecheck/build and database advisor pass | + +## Dependency flow + +```text +contracts → database/storage → server/workflow → client/UI → hardening/release +``` + +## Cross-sprint constraints + +- Supabase remains the only shared persistent cache. Do not add localStorage, + Redis, or an in-process LRU. +- Cache entries do not expire. Only explicit refresh creates a new version. +- Tax ID may auto-match; a unique domain may auto-match; a normalized name + only produces suggestions. +- Never trust a client-provided company ID without rebinding it to the current + normalized input. +- Never auto-merge identities. +- A cache hit, invalid selection, identity conflict, or unavailable cache must + not construct or call LLM, Serper, or scraper adapters. +- Every started SSE stream ends with `done`, including fatal failures. +- Runtime-validate cached JSONB before returning it. +- Use `pg_advisory_xact_lock`, never a session advisory lock. +- Use HMAC-SHA256 for telemetry fingerprints; never emit raw tax IDs/domains. +- No refresh rate limiter is added in these sprints. The spec records it as a + later security/cost control. + +## Execution rule + +Each sprint document is a standalone implementation plan. The executor reads +the approved spec and the selected sprint only, performs its test-first tasks, +and stops at that sprint's review gate. Do not batch multiple sprint commits +without review. diff --git a/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-01-contracts-and-normalization.md b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-01-contracts-and-normalization.md new file mode 100644 index 0000000..9591b98 --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-01-contracts-and-normalization.md @@ -0,0 +1,490 @@ +# Sprint 01 — Cache Contracts and Normalization Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Establish the runtime-validated request, SSE, snapshot, normalization, +and pure cache-decision contracts without changing the research route behavior. + +**Architecture:** Add domain contracts to `src/lib/types.ts` and one focused +cache module containing pure normalization, snapshot validation, and lookup +decision logic. No Supabase I/O belongs in this sprint. Upgrade the production +container to Node.js 22 because current Supabase client releases no longer +support Node.js 20. + +**Tech Stack:** Next.js 16.3.2, TypeScript, Zod 4.4.3, Vitest 4.1.11, Node.js 22, +platform `URL` and `crypto.randomUUID()` APIs. + +**Spec:** `docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md` + +## Global Constraints + +- Do not change the current `/api/research` runtime flow in this sprint. +- Do not add a cache dependency, localStorage, Redis, or an LRU. +- Preserve Vietnamese diacritics and legal suffixes in normalized names. +- A normalized name never creates an automatic hit. +- Runtime schemas must reject malformed cached JSONB instead of casting it. +- Do not emit raw tax IDs or domains to logs or telemetry. +- Match the existing Zod/type style in `src/lib/types.ts`. +- Read `node_modules/next/dist/docs/01-app/01-getting-started/15-route-handlers.md` + before later route work; no Next.js route change occurs in this sprint. +- Stage only files named by each task. + +--- + +## File map + +| File | Action | Responsibility | +|---|---|---| +| `Dockerfile` | Modify | Move production runtime from Node 20 to Node 22 | +| `src/lib/types.ts` | Modify | Request, cache, snapshot, and SSE runtime contracts | +| `src/modules/cache/index.ts` | Create | Pure identity normalization, snapshot validation, and cache decision logic | +| `tests/unit/types-validation.test.ts` | Modify | Request-union and complete-snapshot schema coverage | +| `tests/unit/research-cache.test.ts` | Create | Normalization and lookup decision coverage | + +### Task 1: Define request and SSE contracts + +**Files:** + +- Modify: `src/lib/types.ts` +- Modify: `tests/unit/types-validation.test.ts` + +**Interfaces:** + +- Consumes: existing `CompanyInputSchema`, `CompanyProfile`, `ProfileDiff`, + `AnalysisReport`, and `StreamEvent`. +- Produces: + +```ts +export const CacheActionSchema: z.ZodType< + | { action: "select"; companyId: string } + | { action: "refresh"; companyId: string } + | { action: "bypass" } +>; + +export const ResearchRequestSchema: z.ZodType<{ + input: CompanyInput; + cache?: z.infer; +}>; + +export type ResearchErrorCode = + | "identity_conflict" + | "cache_invalid" + | "persist_failed" + | "research_failed"; + +export type CacheHitMatchedBy = "tax_id" | "domain" | "selected"; +``` + +- [ ] **Step 1: Write failing request-union tests** + +Append these cases to `tests/unit/types-validation.test.ts`: + +```ts +import { + ResearchRequestSchema, + type ResearchRequest, +} from "@/lib/types"; + +it("accepts default, select, refresh, and bypass research requests", () => { + const requests: ResearchRequest[] = [ + { input: { name: "FPT" } }, + { input: { name: "FPT" }, cache: { action: "select", companyId: "fpt" } }, + { input: { name: "FPT" }, cache: { action: "refresh", companyId: "fpt" } }, + { input: { name: "FPT" }, cache: { action: "bypass" } }, + ]; + + expect(requests.every((request) => ResearchRequestSchema.safeParse(request).success)) + .toBe(true); +}); + +it("rejects cache actions with missing or unexpected company IDs", () => { + expect( + ResearchRequestSchema.safeParse({ + input: { name: "FPT" }, + cache: { action: "select" }, + }).success, + ).toBe(false); + expect( + ResearchRequestSchema.safeParse({ + input: { name: "FPT" }, + cache: { action: "bypass", companyId: "injected" }, + }).success, + ).toBe(false); +}); +``` + +- [ ] **Step 2: Run the request tests and confirm the missing exports fail** + +Run: `npm test -- tests/unit/types-validation.test.ts` + +Expected: FAIL because `ResearchRequestSchema` and `ResearchRequest` do not +exist. + +- [ ] **Step 3: Implement the request schemas and stream-event additions** + +Add to `src/lib/types.ts`: + +```ts +export const CacheActionSchema = z.discriminatedUnion("action", [ + z.object({ action: z.literal("select"), companyId: z.string().min(1) }).strict(), + z.object({ action: z.literal("refresh"), companyId: z.string().min(1) }).strict(), + z.object({ action: z.literal("bypass") }).strict(), +]); + +export const ResearchRequestSchema = z.object({ + input: CompanyInputSchema, + cache: CacheActionSchema.optional(), +}).strict(); + +export type ResearchRequest = z.infer; +export type ResearchErrorCode = + | "identity_conflict" + | "cache_invalid" + | "persist_failed" + | "research_failed"; +export type CacheHitMatchedBy = "tax_id" | "domain" | "selected"; + +export interface CacheSuggestion { + companyId: string; + officialName: string; + taxId?: string; + domain?: string; + lastSyncedAt: string; +} +``` + +Extend `StreamEvent` with `cache:hit` and `cache:suggestions`, and extend the +existing error payload with optional `code: ResearchErrorCode`. Use the exact +payloads from spec section 8. + +- [ ] **Step 4: Run focused validation and type checks** + +Run: + +```bash +npm test -- tests/unit/types-validation.test.ts +npm run typecheck +``` + +Expected: tests and typecheck pass. + +- [ ] **Step 5: Commit the request boundary** + +```bash +git add src/lib/types.ts tests/unit/types-validation.test.ts +git commit -m "feat(cache): define cache request contracts" +``` + +### Task 2: Add complete snapshot runtime schemas + +**Files:** + +- Modify: `src/lib/types.ts` +- Modify: `tests/unit/types-validation.test.ts` + +**Interfaces:** + +- Produces: + +```ts +export interface ResearchSnapshot { + profile: CompanyProfile; + report: AnalysisReport; + diff: ProfileDiff | null; + lastSyncedAt: string; +} + +export const ResearchSnapshotSchema: z.ZodType; +``` + +- [ ] **Step 1: Write failing complete/corrupt snapshot tests** + +Use the existing valid profile fixtures or construct the minimum full objects: + +```ts +it("parses a complete research snapshot and restores dates", () => { + const result = ResearchSnapshotSchema.parse({ + profile: validProfileJson, + report: validReportJson, + diff: null, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }); + + expect(result.profile.lastUpdated).toBeInstanceOf(Date); + expect(result.report.generatedAt).toBeInstanceOf(Date); +}); + +it("rejects mismatched and incomplete snapshots", () => { + expect(() => ResearchSnapshotSchema.parse({ + profile: validProfileJson, + report: { ...validReportJson, companyId: "other-company" }, + diff: null, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + })).toThrow(); +}); +``` + +Define `validProfileJson` with every current `CompanyProfile` field and +`validReportJson` with every current `AnalysisReport` field. Do not cast a +partial object to bypass the schema. + +- [ ] **Step 2: Run and verify the schema test fails** + +Run: `npm test -- tests/unit/types-validation.test.ts` + +Expected: FAIL because `ResearchSnapshotSchema` is absent. + +- [ ] **Step 3: Implement runtime schemas that mirror the domain types** + +Add Zod schemas for `Address`, `Person`, `Activity`, `SourceCitation`, +`CompanyProfile`, `FieldChange`, `ProfileDiff`, `FitScore`, `RiskFlag`, +`SuggestedAction`, `AnalysisReport`, and `ResearchSnapshot`. Use +`z.coerce.date()` for persisted date values, numeric bounds already enforced by +the analyst/profile modules, and `.strict()` on trust-boundary objects. + +Add this final cross-object validation: + +```ts +export const ResearchSnapshotSchema = z.object({ + profile: CompanyProfileSchema, + report: AnalysisReportSchema, + diff: ProfileDiffSchema.nullable(), + lastSyncedAt: z.string().datetime(), +}).strict().superRefine((snapshot, ctx) => { + if (snapshot.report.companyId !== snapshot.profile.id) { + ctx.addIssue({ + code: "custom", + path: ["report", "companyId"], + message: "Analysis report companyId must match profile id", + }); + } + if ( + snapshot.diff && + (snapshot.diff.companyId !== snapshot.profile.id || + snapshot.diff.toVersion !== snapshot.profile.version) + ) { + ctx.addIssue({ + code: "custom", + path: ["diff"], + message: "Profile diff must match profile id and version", + }); + } +}); +``` + +- [ ] **Step 4: Run validation tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/types-validation.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 5: Commit runtime snapshot validation** + +```bash +git add src/lib/types.ts tests/unit/types-validation.test.ts +git commit -m "feat(cache): validate cached research snapshots" +``` + +### Task 3: Normalize identity and decide cache outcomes + +**Files:** + +- Create: `src/modules/cache/index.ts` +- Create: `tests/unit/research-cache.test.ts` + +**Interfaces:** + +```ts +export interface NormalizedCompanyIdentity { + taxId: string | null; + domain: string | null; + name: string; +} + +export interface IdentityCandidate { + companyId: string; + taxId: string | null; + domain: string | null; + name: string; +} + +export type CacheDecision = + | { kind: "hit"; companyId: string; matchedBy: "tax_id" | "domain" } + | { kind: "suggestions"; companyIds: string[] } + | { kind: "miss" } + | { kind: "conflict"; taxCompanyId: string; domainCompanyIds: string[] }; + +export function normalizeCompanyIdentity(input: CompanyInput): NormalizedCompanyIdentity; +export function decideCacheLookup( + identity: NormalizedCompanyIdentity, + candidates: readonly IdentityCandidate[], +): CacheDecision; +``` + +- [ ] **Step 1: Write failing normalization tests** + +```ts +it("normalizes tax ID, domain, and Vietnamese name without dropping legal suffixes", () => { + expect(normalizeCompanyIdentity({ + name: " CÔNG TY CP Ánh Dương ", + taxId: "0101-245.486", + website: "https://WWW.Example.VN:443/about?q=1", + })).toEqual({ + taxId: "0101245486", + domain: "example.vn", + name: "công ty cp ánh dương", + }); +}); + +it("rejects a malformed supplied tax ID", () => { + expect(() => normalizeCompanyIdentity({ name: "FPT", taxId: "abc" })) + .toThrow("Mã số thuế phải có 10 hoặc 13 chữ số"); +}); +``` + +- [ ] **Step 2: Write failing decision-table tests** + +Cover these exact cases: + +```ts +expect(decideCacheLookup(withTaxAndDomain, candidatesForSameCompany)).toEqual({ + kind: "hit", companyId: "company-a", matchedBy: "tax_id", +}); +expect(decideCacheLookup(withConflictingKeys, conflictingCandidates)).toEqual({ + kind: "conflict", + taxCompanyId: "company-a", + domainCompanyIds: ["company-b"], +}); +expect(decideCacheLookup(domainOnly, twoDomainCandidates)).toEqual({ + kind: "suggestions", companyIds: ["company-a", "company-b"], +}); +expect(decideCacheLookup(nameOnly, oneNameCandidate)).toEqual({ + kind: "suggestions", companyIds: ["company-a"], +}); +expect(decideCacheLookup(nameOnly, [])).toEqual({ kind: "miss" }); +``` + +- [ ] **Step 3: Run and verify the new test fails** + +Run: `npm test -- tests/unit/research-cache.test.ts` + +Expected: FAIL because `@/modules/cache` does not exist. + +- [ ] **Step 4: Implement normalization with platform APIs** + +Use these rules directly: + +```ts +const TAX_ID_PATTERN = /^\d{10}(?:\d{3})?$/; + +function normalizeTaxId(value?: string): string | null { + if (!value) return null; + const normalized = value.trim().replace(/[\s.-]/g, ""); + if (!TAX_ID_PATTERN.test(normalized)) { + throw new Error("Mã số thuế phải có 10 hoặc 13 chữ số"); + } + return normalized; +} + +function normalizeDomain(website?: string): string | null { + if (!website) return null; + return new URL(website).hostname + .toLowerCase() + .replace(/\.$/, "") + .replace(/^www\./, ""); +} + +function normalizeName(name: string): string { + return name.normalize("NFKC").trim().toLocaleLowerCase("vi-VN") + .replace(/\s+/g, " "); +} +``` + +Implement `decideCacheLookup` as the spec decision table. Sort and deduplicate +suggestion IDs before returning them so database row order cannot affect output. + +- [ ] **Step 5: Run cache tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/research-cache.test.ts tests/unit/types-validation.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Commit pure cache behavior** + +```bash +git add src/modules/cache/index.ts tests/unit/research-cache.test.ts +git commit -m "feat(cache): normalize and resolve identities" +``` + +### Task 4: Align the production runtime with supported Node.js + +**Files:** + +- Modify: `Dockerfile` + +**Interfaces:** + +- Consumes: the existing multi-stage Docker build. +- Produces: the same image layout running Node.js 22 Alpine. + +- [ ] **Step 1: Confirm the current base is unsupported** + +Read `Dockerfile` and confirm it currently contains: + +```dockerfile +FROM node:20-alpine AS base +``` + +The Supabase 2026 changelog states current client libraries dropped Node.js 20 +support. CI already uses Node.js 22. + +- [ ] **Step 2: Make the one-line runtime update** + +```dockerfile +FROM node:22-alpine AS base +``` + +- [ ] **Step 3: Verify production compilation** + +Run: + +```bash +npm run typecheck +npm run build +``` + +Expected: both commands pass under the development environment; the Docker +stages remain otherwise unchanged. + +- [ ] **Step 4: Commit the runtime prerequisite** + +```bash +git add Dockerfile +git commit -m "chore(runtime): move production to node 22" +``` + +## Sprint 01 review gate + +Run: + +```bash +npm test -- tests/unit/types-validation.test.ts tests/unit/research-cache.test.ts +npm run lint +npm run typecheck +npm run build +git status --short +``` + +Expected: all checks pass and the worktree contains no uncommitted Sprint 01 +files. Review the exported types before starting Sprint 02; later sprint plans +use these names exactly. diff --git a/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-02-supabase-schema-and-storage.md b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-02-supabase-schema-and-storage.md new file mode 100644 index 0000000..4b16629 --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-02-supabase-schema-and-storage.md @@ -0,0 +1,573 @@ +# Sprint 02 — Supabase Schema and Storage Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add the canonical company identity schema, transactional database +functions, complete-snapshot persistence, and matching memory/Supabase storage +behavior without changing the research route. + +**Architecture:** Keep `StorageAdapter` as the existing persistence seam and +deepen it with identity/cache methods. Supabase implements multi-statement +identity creation and snapshot persistence through transaction-scoped RPCs; +the memory adapter provides deterministic parity for route/workflow tests. + +**Tech Stack:** PostgreSQL 17-compatible SQL, Supabase Data API/PostgREST, +`@supabase/supabase-js` 2.112.x, pinned Supabase CLI 2.115.0, TypeScript, +Vitest 4.1.11. + +**Spec:** `docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md` + +## Global Constraints + +- Sprint 01 types and function names are fixed inputs. +- Use `pg_advisory_xact_lock`; session-level advisory locks are forbidden. +- Tax IDs are unique only when non-null; domains and names are not unique. +- Never auto-merge identities or overwrite conflicting identity metadata. +- Persist profile, report, diff, and identity metadata in one transaction. +- Cache reads accept only profile rows with non-null `analysis_report`. +- Database functions use `SECURITY INVOKER`, an empty search path, and fully + qualified relation names. +- Revoke Data API access from `anon` and `authenticated`; grant only the + server-side `service_role` the required table/function access. +- Never expose `SUPABASE_SERVICE_ROLE_KEY` through `NEXT_PUBLIC_*` or client + modules. +- Create migrations with `supabase migration new`; do not hand-name migration + files. +- Stage only files named by each task. + +--- + +## File map + +| File | Action | Responsibility | +|---|---|---| +| `package.json` | Modify | Pin Supabase CLI and add the database test command | +| `package-lock.json` | Modify | Lock CLI binaries and package graph | +| `supabase/config.toml` | Create via CLI | Local Supabase project configuration | +| `supabase/migrations/_research_cache.sql` | Create via CLI | Existing-database migration and RPC definitions | +| `supabase/schema.sql` | Modify | Canonical fresh-project schema matching the migration | +| `.env.example` | Modify | Server-only service-role configuration | +| `src/adapters/storage/types.ts` | Modify | Identity and complete-snapshot storage interface | +| `src/adapters/storage/memory.ts` | Modify | Test/development implementation | +| `src/adapters/storage/supabase.ts` | Modify | RPC and complete-snapshot implementation | +| `src/config/index.ts` | Modify | Require the server-only key for Supabase storage | +| `tests/unit/adapters.test.ts` | Modify | Memory cache/storage parity | +| `tests/unit/supabase-storage.test.ts` | Modify | RPC mapping and service-key validation | +| `tests/integration/supabase-cache-concurrency.test.ts` | Create | Real transaction/concurrency verification | +| `.github/workflows/ci.yml` | Modify | Run local Supabase database checks in CI | + +### Task 1: Pin and initialize the database toolchain + +**Files:** + +- Modify: `package.json` +- Modify: `package-lock.json` +- Create via CLI: `supabase/config.toml` + +**Interfaces:** + +- Consumes: Node.js 22 from Sprint 01 and Docker on developer/CI hosts. +- Produces: reproducible `npx supabase` commands at version 2.115.0. + +- [ ] **Step 1: Install the stable CLI version verified for this plan** + +Run: + +```bash +npm install --save-dev --save-exact supabase@2.115.0 +npx supabase --version +npx supabase migration new --help +npx supabase db advisors --help +``` + +Expected: version `2.115.0`; help lists `migration new` and local database +advisor support. Stop and update this sprint document if the pinned commands do +not match the installed help. + +- [ ] **Step 2: Initialize local Supabase configuration** + +Run: + +```bash +npx supabase init +``` + +Expected: `supabase/config.toml` is created without replacing +`supabase/schema.sql`. + +- [ ] **Step 3: Add an explicit database-test script** + +Add to `package.json`: + +```json +"test:db": "vitest run tests/integration/supabase-cache-concurrency.test.ts" +``` + +- [ ] **Step 4: Verify package integrity and commit** + +Run: + +```bash +npm ci +npx supabase --version +``` + +Expected: clean install and CLI `2.115.0`. + +```bash +git add package.json package-lock.json supabase/config.toml +git commit -m "chore(db): pin supabase cli" +``` + +### Task 2: Add identity and complete-snapshot schema + +**Files:** + +- Create via CLI: `supabase/migrations/_research_cache.sql` +- Modify: `supabase/schema.sql` + +**Interfaces:** + +- Produces tables/indexes described in spec section 4 and these RPCs: + +```sql +public.lookup_company_identities(text, text, text) +public.resolve_company_identity(text, text, text, text) +public.persist_research_snapshot(text, text, text, text, integer, jsonb, jsonb, jsonb) +``` + +- [ ] **Step 1: Create the migration through the CLI** + +Run: + +```bash +npx supabase migration new research_cache +``` + +Expected: CLI prints the exact new path under `supabase/migrations`. Use that +printed path for every remaining migration edit and commit; do not rename it. + +- [ ] **Step 2: Add the identity table and profile/diff constraints** + +Write these statements into the generated migration and mirror them in +`supabase/schema.sql` for fresh projects: + +```sql +create table if not exists public.company_identities ( + id text primary key, + tax_id text, + normalized_domain text, + normalized_name text not null, + created_at timestamptz not null default timezone('utc'::text, now()), + updated_at timestamptz not null default timezone('utc'::text, now()) +); + +create unique index if not exists idx_company_identities_tax_id + on public.company_identities (tax_id) + where tax_id is not null; +create index if not exists idx_company_identities_domain + on public.company_identities (normalized_domain); +create index if not exists idx_company_identities_name + on public.company_identities (normalized_name); + +alter table public.company_profiles + add column if not exists analysis_report jsonb; + +create index if not exists idx_company_profiles_complete + on public.company_profiles (id, version desc) + where analysis_report is not null; +``` + +Backfill `company_identities` from the latest row per existing profile ID before +adding foreign keys. Use `data->>'taxId'`, `data->>'website'`, and +`data->>'officialName'` with the same digit/domain/lowercase whitespace rules +as Sprint 01. Invalid legacy tax IDs become null; never fail the migration on +one malformed JSONB value. + +Then add foreign keys: + +```sql +alter table public.company_profiles + add constraint company_profiles_identity_fk + foreign key (id) references public.company_identities(id); + +alter table public.company_diffs + add constraint company_diffs_identity_fk + foreign key (company_id) references public.company_identities(id); +``` + +Guard each named constraint with a `pg_constraint` existence check so rerunning +the canonical schema is idempotent. + +- [ ] **Step 3: Replace broad public policies with server-only access** + +Drop the two existing “Allow anon read/write” policies, enable RLS on all three +tables, revoke table access from `anon`/`authenticated`, and grant the minimum +table privileges to `service_role`: + +```sql +drop policy if exists "Allow anon read/write company_profiles" + on public.company_profiles; +drop policy if exists "Allow anon read/write company_diffs" + on public.company_diffs; + +alter table public.company_identities enable row level security; +alter table public.company_profiles enable row level security; +alter table public.company_diffs enable row level security; + +revoke all on public.company_identities from anon, authenticated; +revoke all on public.company_profiles from anon, authenticated; +revoke all on public.company_diffs from anon, authenticated; + +grant select, insert, update on public.company_identities to service_role; +grant select, insert, update on public.company_profiles to service_role; +grant select, insert, update on public.company_diffs to service_role; +``` + +No browser/client code receives the service-role key. + +- [ ] **Step 4: Add the read-only lookup RPC** + +Implement `public.lookup_company_identities(p_tax_id text, p_domain text, +p_name text)` as `LANGUAGE sql STABLE SECURITY INVOKER SET search_path = ''`. +Return distinct identity rows matching any non-null supplied key. Fully qualify +`public.company_identities` and order by `id` for deterministic adapter output. + +Revoke default execute and grant only server execution: + +```sql +revoke execute on function public.lookup_company_identities(text, text, text) + from public, anon, authenticated; +grant execute on function public.lookup_company_identities(text, text, text) + to service_role; +``` + +- [ ] **Step 5: Add the transactional resolve/create RPC** + +Implement `public.resolve_company_identity` with these exact branches: + +```plpgsql +if p_tax_id is not null then + insert into public.company_identities ( + id, tax_id, normalized_domain, normalized_name + ) values ( + p_candidate_id, p_tax_id, p_domain, p_name + ) on conflict (tax_id) where tax_id is not null do nothing; + + select id into resolved_id + from public.company_identities + where tax_id = p_tax_id; +elsif p_domain is not null then + perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtext(p_domain)); + + select id into resolved_id + from public.company_identities + where normalized_domain = p_domain + and normalized_name = p_name + order by id + limit 1; + + if resolved_id is null then + insert into public.company_identities ( + id, normalized_domain, normalized_name + ) values ( + p_candidate_id, p_domain, p_name + ) returning id into resolved_id; + end if; +else + insert into public.company_identities (id, normalized_name) + values (p_candidate_id, p_name) + returning id into resolved_id; +end if; +``` + +Before returning, detect supplied tax/domain disagreement and raise a stable +exception marker `identity_conflict`. Do not update an existing identity's keys +inside the tax-ID conflict branch. + +Declare the function `SECURITY INVOKER SET search_path = ''`, revoke execution +from `public`, `anon`, and `authenticated`, and grant it to `service_role`. + +- [ ] **Step 6: Add atomic snapshot persistence RPC** + +Implement `public.persist_research_snapshot` as one PL/pgSQL transaction that: + +1. Locks the target `company_identities` row with `FOR UPDATE`. +2. Rechecks the pipeline-derived tax ID and domain against other identities. +3. Raises `identity_conflict` before mutation when either key belongs to a + different identity under the approved conflict rules. +4. Updates only the target identity's non-conflicting normalized metadata. +5. Upserts `company_profiles(id, version, official_name, data, + analysis_report, updated_at)` on `(id, version)`. +6. Upserts the supplied diff when non-null; version 1 accepts null. +7. Returns the authoritative `updated_at` value. + +Use `SECURITY INVOKER SET search_path = ''`, explicit schema qualification, +server-only execute grants, and no dynamic SQL. + +- [ ] **Step 7: Apply and inspect the local schema** + +Run: + +```bash +npx supabase start +npx supabase db reset +npx supabase migration list --local +npx supabase db advisors --local +``` + +Expected: migration is applied, no duplicate/failed migration, and advisors +report no security/performance issue introduced by these objects. + +- [ ] **Step 8: Commit schema and RPCs** + +```bash +git add supabase/schema.sql supabase/migrations +git commit -m "feat(db): add research cache schema" +``` + +### Task 3: Deepen the storage interface and memory adapter + +**Files:** + +- Modify: `src/adapters/storage/types.ts` +- Modify: `src/adapters/storage/memory.ts` +- Modify: `tests/unit/adapters.test.ts` + +**Interfaces:** + +Add these methods while retaining existing profile/diff methods until Sprint 03 +migrates the workflow: + +```ts +findIdentityCandidates( + identity: NormalizedCompanyIdentity, + options?: StorageReadOptions, +): Promise; + +getLatestCompleteSnapshot( + companyId: string, + options?: StorageReadOptions, +): Promise; + +resolveOrCreateIdentity( + identity: NormalizedCompanyIdentity, + candidateId: string, + options?: StorageWriteOptions, +): Promise; + +persistResearchSnapshot( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions, +): Promise; +``` + +- [ ] **Step 1: Write failing memory-adapter tests** + +Add tests proving: + +```ts +await storage.resolveOrCreateIdentity(identity, "company-a"); +await storage.persistResearchSnapshot(identity, draft); + +await expect(storage.findIdentityCandidates(identity)).resolves.toEqual([ + expect.objectContaining({ companyId: "company-a" }), +]); +await expect(storage.getLatestCompleteSnapshot("company-a")).resolves + .toMatchObject({ profile: { id: "company-a" }, report: { companyId: "company-a" } }); +``` + +Add an invalid selection/conflicting tax-ID case and a version-2 snapshot whose +diff matches `toVersion: 2`. + +- [ ] **Step 2: Run and verify interface failures** + +Run: `npm test -- tests/unit/adapters.test.ts` + +Expected: FAIL because the new methods do not exist. + +- [ ] **Step 3: Implement the minimum in-memory parity** + +Store identities in a `Map` and complete snapshots +in the existing company/version maps. Reuse Sprint 01's `decideCacheLookup` +rules; do not create a second normalization implementation. Return cloned +arrays/objects where mutation would leak across tests. + +- [ ] **Step 4: Run focused tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/adapters.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 5: Commit the storage seam** + +```bash +git add src/adapters/storage/types.ts src/adapters/storage/memory.ts tests/unit/adapters.test.ts +git commit -m "feat(storage): add complete cache snapshots" +``` + +### Task 4: Implement Supabase storage and server-only credentials + +**Files:** + +- Modify: `.env.example` +- Modify: `src/config/index.ts` +- Modify: `src/adapters/storage/supabase.ts` +- Modify: `tests/unit/supabase-storage.test.ts` + +**Interfaces:** Implements all Sprint 02 `StorageAdapter` methods via +`.rpc(...)`, complete-row selection, and exact-version diff selection. + +- [ ] **Step 1: Write failing service-key and RPC mapping tests** + +Mock the Supabase client boundary and assert: + +- Supabase storage refuses startup without `SUPABASE_SERVICE_ROLE_KEY`. +- Identity lookup calls `lookup_company_identities` with normalized values. +- Identity creation calls `resolve_company_identity`. +- Snapshot persistence calls `persist_research_snapshot`. +- Complete snapshot selection filters `analysis_report` non-null, orders version + descending, then fetches diff by exact `company_id` and `to_version`. +- Abort signals propagate to all PostgREST/RPC builders that support them. + +- [ ] **Step 2: Run and verify tests fail** + +Run: `npm test -- tests/unit/supabase-storage.test.ts` + +Expected: FAIL against the old adapter constructor/method set. + +- [ ] **Step 3: Require the server-only credential** + +Document in `.env.example`: + +```dotenv +SUPABASE_URL=https://xyz.supabase.co +SUPABASE_SERVICE_ROLE_KEY=server-only-secret +``` + +Remove the Supabase storage path's use of `SUPABASE_ANON_KEY` in +`createStorageAdapter`. The adapter constructor accepts URL and service-role key +only. Do not rename the key with a `NEXT_PUBLIC_` prefix. + +- [ ] **Step 4: Implement RPC and snapshot mapping** + +Call the three exact RPC names from Task 2. Parse every JSONB response through +`ResearchSnapshotSchema`; do not use `as CompanyProfile` or +`as AnalysisReport`. Convert known RPC conflict markers to a typed +`IdentityConflictError` exported from `src/modules/cache/index.ts`; convert +transport errors to a storage error without provider fallback. + +- [ ] **Step 5: Run focused tests** + +Run: + +```bash +npm test -- tests/unit/supabase-storage.test.ts tests/unit/adapters.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Commit Supabase adapter behavior** + +```bash +git add .env.example src/config/index.ts src/adapters/storage/supabase.ts tests/unit/supabase-storage.test.ts +git commit -m "feat(storage): use transactional supabase cache" +``` + +### Task 5: Prove transaction-level concurrency with two clients + +**Files:** + +- Create: `tests/integration/supabase-cache-concurrency.test.ts` +- Modify: `.github/workflows/ci.yml` + +**Interfaces:** Uses `SUPABASE_TEST_URL` and +`SUPABASE_TEST_SERVICE_ROLE_KEY`; never uses production credentials. + +- [ ] **Step 1: Write the real concurrent RPC test** + +Create two independent `createClient` instances, start both RPC calls before +awaiting either, and assert one identity: + +```ts +const first = createClient(testUrl, serviceKey, clientOptions); +const second = createClient(testUrl, serviceKey, clientOptions); +const domain = `race-${crypto.randomUUID()}.example`; +const name = `race ${crypto.randomUUID()}`; + +const [a, b] = await Promise.all([ + first.rpc("resolve_company_identity", { + p_tax_id: null, + p_domain: domain, + p_name: name, + p_candidate_id: crypto.randomUUID(), + }), + second.rpc("resolve_company_identity", { + p_tax_id: null, + p_domain: domain, + p_name: name, + p_candidate_id: crypto.randomUUID(), + }), +]); + +expect(a.error).toBeNull(); +expect(b.error).toBeNull(); +expect(a.data).toBe(b.data); +``` + +Query `company_identities` with the service-role test client and assert exactly +one row for that domain/name. Add a rollback test for post-pipeline conflict so +no profile/diff row survives a failed persistence RPC. + +- [ ] **Step 2: Run against local Supabase** + +Run: + +```bash +eval "$(npx supabase status -o env)" +SUPABASE_TEST_URL="$API_URL" \ +SUPABASE_TEST_SERVICE_ROLE_KEY="$SERVICE_ROLE_KEY" \ +npm run test:db +``` + +Expected: both concurrent calls return the same ID and all database integration +tests pass. + +- [ ] **Step 3: Add the same local database gate to CI** + +After `npm ci`, add CI steps that run `npx supabase start`, export local API URL +and service key only within the database-test step, run `npm run test:db`, then +stop the stack with `npx supabase stop --no-backup`. Do not print service keys. + +- [ ] **Step 4: Commit concurrency verification** + +```bash +git add tests/integration/supabase-cache-concurrency.test.ts .github/workflows/ci.yml +git commit -m "test(db): verify cache identity locking" +``` + +## Sprint 02 review gate + +Run: + +```bash +npx supabase db reset +npx supabase migration list --local +npx supabase db advisors --local +eval "$(npx supabase status -o env)" +SUPABASE_TEST_URL="$API_URL" SUPABASE_TEST_SERVICE_ROLE_KEY="$SERVICE_ROLE_KEY" npm run test:db +npm test -- tests/unit/adapters.test.ts tests/unit/supabase-storage.test.ts +npm run lint +npm run typecheck +git status --short +``` + +Expected: migration/advisors pass, independent-client concurrency passes, unit +tests pass, and the worktree is clean before Sprint 03. diff --git a/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-03-server-read-through-flow.md b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-03-server-read-through-flow.md new file mode 100644 index 0000000..3a21e2b --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-03-server-read-through-flow.md @@ -0,0 +1,494 @@ +# Sprint 03 — Server Read-Through Flow Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Put the complete Supabase snapshot in front of the expensive research +pipeline, bind every client selection to current input, and persist misses or +refreshes atomically under a canonical company ID. + +**Architecture:** Deepen the pure cache module into a storage-backed research +cache, then keep the route as the orchestration point. The workflow becomes +storage-independent: it receives canonical identity/previous profile, produces +a terminal state, and leaves final SSE emission/persistence to the route. + +**Tech Stack:** Next.js 16.3.2 Node Route Handler, Web Streams/SSE, LangGraph +1.4.12, Zod 4.4.3, Supabase storage seam, Vitest 4.1.11. + +**Spec:** `docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md` + +## Global Constraints + +- Read the installed Next.js 16 route-handler and streaming guides before + editing `route.ts`. +- Construct only storage/cache dependencies before cache resolution. +- Paid provider factories must remain untouched on hit, invalid selection, + identity conflict, and cache-backend failure. +- Recompute candidates server-side for every `select` and `refresh` request. +- Never derive canonical identity with `slugify(name)`. +- Workflow owns research computation; route owns persistence and final SSE + events. +- Every started SSE stream ends with `done`. +- A corrupt cache is recoverable; a persist failure or post-pipeline identity + conflict is fatal. +- Do not add a queue, background job, rate limiter, TTL, or LRU. +- Stage only files named by each task. + +--- + +## File map + +| File | Action | Responsibility | +|---|---|---| +| `src/modules/cache/index.ts` | Modify | Storage-backed lookup, selection, refresh binding, and persistence interface | +| `src/modules/workflow/state.ts` | Modify | Canonical ID and supplied previous profile in initial state | +| `src/modules/workflow/index.ts` | Modify | Remove storage reads/writes and final result emission | +| `src/app/api/research/route.ts` | Modify | Cache-first orchestration, HTTP/SSE errors, lazy providers, atomic persist | +| `src/lib/stream.ts` | Modify only if required | Preserve explicit error/done ordering and close-once behavior | +| `tests/unit/research-cache.test.ts` | Modify | Storage-backed selection/refresh/cache-invalid behavior | +| `tests/integration/research-workflow.test.ts` | Modify | Storage-independent canonical workflow behavior | +| `tests/unit/research-cache-route.test.ts` | Create | Hit/miss/selection/conflict/provider-construction route coverage | +| `tests/unit/research-route-observability.test.ts` | Modify | New request body and route terminal behavior | +| `tests/e2e/workflow-e2e.test.ts` | Modify | New request body and final SSE ownership | + +### Task 1: Build the storage-backed research cache module + +**Files:** + +- Modify: `src/modules/cache/index.ts` +- Modify: `tests/unit/research-cache.test.ts` + +**Interfaces:** + +```ts +export type CacheResolution = + | { + kind: "hit"; + snapshot: ResearchSnapshot; + matchedBy: "tax_id" | "domain"; + } + | { kind: "suggestions"; suggestions: CacheSuggestion[] } + | { + kind: "miss"; + identity: NormalizedCompanyIdentity; + cacheInvalid: boolean; + } + | { + kind: "conflict"; + taxCompanyId: string; + domainCompanyIds: string[]; + }; + +export interface ResearchCache { + lookup(input: CompanyInput, options?: StorageReadOptions): Promise; + select( + input: CompanyInput, + companyId: string, + options?: StorageReadOptions, + ): Promise; + prepareRefresh( + input: CompanyInput, + companyId: string, + options?: StorageReadOptions, + ): Promise; + resolveMiss( + input: CompanyInput, + options?: StorageWriteOptions, + ): Promise<{ companyId: string; identity: NormalizedCompanyIdentity }>; + persist( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions, + ): Promise; +} + +export function createResearchCache(storage: StorageAdapter): ResearchCache; +``` + +- [ ] **Step 1: Write failing hit/suggestion/miss tests** + +Use `MemoryStorageAdapter` and prove: + +```ts +await expect(cache.lookup({ name: "FPT", taxId: "0101248141" })) + .resolves.toMatchObject({ + kind: "hit", + matchedBy: "tax_id", + snapshot: { profile: { id: "company-a" } }, + }); + +await expect(cache.lookup({ name: "FPT" })).resolves.toMatchObject({ + kind: "suggestions", + suggestions: [expect.objectContaining({ companyId: "company-a" })], +}); + +await expect(cache.lookup({ name: "Unknown" })).resolves.toEqual({ + kind: "miss", + identity: { taxId: null, domain: null, name: "unknown" }, + cacheInvalid: false, +}); +``` + +- [ ] **Step 2: Write the real unrelated-existing-ID tests** + +Seed complete snapshots for company A and company B. Assert: + +```ts +await expect(cache.select({ name: "Company A" }, "company-b")) + .rejects.toMatchObject({ code: "invalid_cache_selection" }); + +await expect(cache.prepareRefresh( + { name: "Company A", taxId: "tax-a" }, + "company-b", +)).rejects.toMatchObject({ code: "identity_conflict" }); +``` + +The rejected IDs must exist and have valid snapshots; a nonexistent ID does not +cover the input-binding vulnerability. + +- [ ] **Step 3: Write a corrupt-snapshot recovery test** + +Make `getLatestCompleteSnapshot` throw `CacheInvalidError` and assert `lookup` +returns the same normalized miss with `cacheInvalid: true`. Transport/storage +errors must propagate instead of becoming a miss. + +- [ ] **Step 4: Run and verify missing module behavior fails** + +Run: `npm test -- tests/unit/research-cache.test.ts` + +Expected: FAIL because `createResearchCache` and the typed cache errors are not +implemented. + +- [ ] **Step 5: Implement the minimum cache orchestration** + +`lookup` calls `findIdentityCandidates`, passes the result to +`decideCacheLookup`, then loads complete snapshots only for the chosen hit or +suggestion IDs. Drop identities without a complete snapshot from suggestions. +If no complete candidate remains, return a miss and retain the normalized +identity for `resolveMiss`. + +`select` reruns the full lookup and accepts `companyId` only when it appears in +the current suggestion set. `prepareRefresh` reruns lookup and accepts the ID +only when the hit/suggestion set contains it without a strong-key conflict. + +`resolveMiss` calls: + +```ts +storage.resolveOrCreateIdentity( + normalizeCompanyIdentity(input), + crypto.randomUUID(), + options, +); +``` + +`persist` delegates once to `persistResearchSnapshot` and returns the database +timestamped snapshot. + +- [ ] **Step 6: Run focused cache tests** + +Run: + +```bash +npm test -- tests/unit/research-cache.test.ts tests/unit/adapters.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 7: Commit the cache module** + +```bash +git add src/modules/cache/index.ts tests/unit/research-cache.test.ts +git commit -m "feat(cache): bind cache decisions to input" +``` + +### Task 2: Make the workflow canonical-ID driven and storage independent + +**Files:** + +- Modify: `src/modules/workflow/state.ts` +- Modify: `src/modules/workflow/index.ts` +- Modify: `tests/integration/research-workflow.test.ts` + +**Interfaces:** + +Change workflow options/state initialization to require: + +```ts +export interface ResearchWorkflowOptions { + researchRunId: string; + companyId: string; + existingProfile: CompanyProfile | null; + signal?: AbortSignal; + callbacks?: readonly unknown[]; + sessionId?: string; + onComplete?: (state: ResearchWorkflowState) => void | Promise; +} +``` + +Remove `storage` from `ResearchWorkflowDeps` after all workflow reads/writes are +deleted. + +- [ ] **Step 1: Rewrite failing workflow tests around supplied identity** + +Replace storage-failure tests with these assertions: + +```ts +const state = await workflow.run( + { name: "Different Display Name" }, + { + researchRunId: "canonical-id", + companyId: "stable-company-id", + existingProfile, + }, +); + +expect(state.profile?.id).toBe("stable-company-id"); +expect(state.profile?.version).toBe(existingProfile.version + 1); +expect(state.diff).toMatchObject({ + companyId: "stable-company-id", + fromVersion: existingProfile.version, + toVersion: existingProfile.version + 1, +}); +``` + +Add a stream test proving source/progress/build events still appear but +`profile:ready`, `diff:ready`, `analysis:ready`, and `done` do not; the route +will own those final events. + +- [ ] **Step 2: Run and verify old workflow behavior fails the new assertions** + +Run: `npm test -- tests/integration/research-workflow.test.ts` + +Expected: FAIL because options do not accept canonical identity and the graph +still reads/writes storage/emits final events. + +- [ ] **Step 3: Remove storage nodes and derive state from options** + +Initialize state with: + +```ts +{ + researchRunId: options.researchRunId, + input, + existingProfile: options.existingProfile, + // existing source/findings/profile/diff/report/outcome defaults remain +} +``` + +Build the profile with `options.companyId`; calculate diff without saving it. +Remove `load_existing_profile` and `persist_profile` nodes/edges. Remove final +result custom events from diff/analyze nodes and remove the workflow-level +`done` yield. Keep fatal/source error events and always invoke `onComplete` with +the terminal state. + +- [ ] **Step 4: Delete storage from workflow callers/tests** + +Remove `storage` from `ResearchWorkflowDeps`, workflow construction, and test +builders. Keep storage tests in the adapter/cache suites. + +- [ ] **Step 5: Run workflow tests and typecheck** + +Run: + +```bash +npm test -- tests/integration/research-workflow.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Commit workflow isolation** + +```bash +git add src/modules/workflow/state.ts src/modules/workflow/index.ts tests/integration/research-workflow.test.ts +git commit -m "refactor(workflow): accept canonical cache state" +``` + +### Task 3: Put cache lookup before all paid providers + +**Files:** + +- Modify: `src/app/api/research/route.ts` +- Create: `tests/unit/research-cache-route.test.ts` +- Modify: `tests/unit/research-route-observability.test.ts` + +**Interfaces:** + +- HTTP errors use `{ error: { code, message } }` and the status table from spec + section 12. +- Started SSE streams use `cache:*`, current progress events, explicit error + codes, final result events, and `done`. + +- [ ] **Step 1: Write a cache-hit route test that forbids provider construction** + +Mock `createStorageAdapter` with a complete cached snapshot and define +`createLLMAdapter`, `createSearchAdapter`, and `createScraperAdapter` as spies +that throw if called. POST: + +```json +{ "input": { "name": "FPT", "taxId": "0101248141" } } +``` + +Assert status 200 and this event order: + +```ts +expect(body.indexOf("event: cache:hit")).toBeLessThan(body.indexOf("event: profile:ready")); +expect(body.indexOf("event: profile:ready")).toBeLessThan(body.indexOf("event: diff:ready")); +expect(body.indexOf("event: diff:ready")).toBeLessThan(body.indexOf("event: analysis:ready")); +expect(body.trimEnd()).toContain("event: done"); +expect(createLLMAdapter).not.toHaveBeenCalled(); +expect(createSearchAdapter).not.toHaveBeenCalled(); +expect(createScraperAdapter).not.toHaveBeenCalled(); +``` + +- [ ] **Step 2: Write pre-stream failure tests** + +Cover: + +- Existing unrelated `companyId` on `select` → `400 invalid_cache_selection`. +- Strong-key disagreement → `409 identity_conflict`. +- Storage/RPC transport failure → `503 cache_unavailable`. + +For every case, assert paid provider factories have zero calls. + +- [ ] **Step 3: Write miss and corrupt-cache tests** + +- Empty lookup: assert identity resolves and workflow/provider factories are + constructed without a second bypass request. +- Corrupt snapshot: assert SSE contains `error.code = cache_invalid`, then + progress/persisted final events and `done`. + +- [ ] **Step 4: Run route tests and verify they fail** + +Run: `npm test -- tests/unit/research-cache-route.test.ts` + +Expected: FAIL against the current eager-provider route. + +- [ ] **Step 5: Implement cache-first route branching** + +Parse `ResearchRequestSchema`, create storage/cache, and resolve the cache before +calling any paid-provider factory. Use these branches: + +```ts +switch (resolution.kind) { + case "hit": + return streamCachedSnapshot(resolution); + case "suggestions": + return streamSuggestions(resolution.suggestions); + case "conflict": + return jsonError(409, "identity_conflict", "Thông tin định danh công ty mâu thuẫn."); + case "miss": + break; +} +``` + +Handle `select`, `refresh`, and `bypass` before default lookup. `bypass` skips +returning suggestions but still calls `resolveMiss`; it never accepts a client +company ID. + +Only after identity resolution succeeds, construct providers/workflow and start +the long-running SSE flow. + +- [ ] **Step 6: Centralize final events after atomic persistence** + +Capture workflow terminal state through `onComplete`. If it has profile/report +and no fatal error, call `cache.persist` once. On success write: + +```ts +writer.write({ event: "profile:ready", data: { profile: persisted.profile } }); +writer.write({ event: "diff:ready", data: { diff: persisted.diff } }); +writer.write({ event: "analysis:ready", data: { report: persisted.report } }); +writer.write({ event: "done", data: {} }); +``` + +On persistence conflict write `error(code: identity_conflict)` then `done`; on +other persistence errors write `error(code: persist_failed)` then `done`. Do not +write profile/diff/analysis first. Preserve abort/deadline cleanup and close the +writer exactly once in `finally`. + +- [ ] **Step 7: Run route tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/research-cache-route.test.ts tests/unit/research-route-observability.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 8: Commit route orchestration** + +```bash +git add src/app/api/research/route.ts tests/unit/research-cache-route.test.ts tests/unit/research-route-observability.test.ts +git commit -m "feat(api): serve research through cache" +``` + +### Task 4: Preserve end-to-end SSE behavior + +**Files:** + +- Modify: `tests/e2e/workflow-e2e.test.ts` +- Modify only if tests require it: `src/lib/stream.ts` + +**Interfaces:** Existing SSE serialization plus new explicit cache/error events. + +- [ ] **Step 1: Update E2E requests and assertions** + +Send nested `{ input }` request bodies. Add one seeded cache hit and one miss. +Assert both terminate with exactly one `done`; hit has no source progress and +miss retains research progress before final events. + +- [ ] **Step 2: Add fatal terminal-path assertions** + +For post-pipeline conflict and persistence failure, assert: + +```ts +expect(events.at(-2)).toMatchObject({ event: "error" }); +expect(events.at(-1)).toEqual({ event: "done", data: {} }); +expect(events.some(({ event }) => event === "profile:ready")).toBe(false); +``` + +- [ ] **Step 3: Run E2E and full focused server suites** + +Run: + +```bash +npm test -- tests/e2e/workflow-e2e.test.ts \ + tests/unit/research-cache-route.test.ts \ + tests/integration/research-workflow.test.ts +``` + +Expected: PASS. Change `src/lib/stream.ts` only if close-once or event-order +behavior cannot be expressed with its current interface. + +- [ ] **Step 4: Commit E2E contract** + +```bash +git add tests/e2e/workflow-e2e.test.ts +git diff -- src/lib/stream.ts +git commit -m "test(api): cover cache sse outcomes" +``` + +If `src/lib/stream.ts` changed, include it in `git add`; otherwise leave it +untouched. + +## Sprint 03 review gate + +Run: + +```bash +npm test -- tests/unit/research-cache.test.ts \ + tests/unit/research-cache-route.test.ts \ + tests/unit/research-route-observability.test.ts \ + tests/integration/research-workflow.test.ts \ + tests/e2e/workflow-e2e.test.ts +npm run lint +npm run typecheck +npm run build +git status --short +``` + +Expected: cache hit never touches paid factories; all HTTP/SSE failure paths are +terminal; miss/refresh persist only complete snapshots; worktree is clean before +Sprint 04. diff --git a/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-04-client-suggestions-and-refresh.md b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-04-client-suggestions-and-refresh.md new file mode 100644 index 0000000..aa7456c --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-04-client-suggestions-and-refresh.md @@ -0,0 +1,460 @@ +# Sprint 04 — Client Suggestions and Refresh Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Let users safely confirm name/domain cache suggestions, see cache +freshness, reject incorrect suggestions, and explicitly refresh a cached +company. + +**Architecture:** Keep network and SSE state in the existing `useResearch` +client hook. Add one inline suggestion component and a pure exported reducer so +state transitions are testable under the existing Node Vitest environment +without installing a DOM test framework. + +**Tech Stack:** React 19.2.8 Client Components, Next.js 16.3.2 App Router, +TypeScript, existing Tailwind CSS styles, Vitest 4.1.11. + +**Spec:** `docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md` + +## Global Constraints + +- Do not use localStorage, browser cache APIs, or client-side Supabase. +- Never decide whether a selected company is valid in the browser; the server + remains authoritative. +- Use one inline accessible panel, not a modal dependency. +- Display official name, available tax ID/domain, and last synchronized time. +- Make “Không phải các công ty trên” and “Cập nhật lại” explicit actions. +- A recoverable `cache_invalid` is a notice while fresh research continues; it + is not a terminal error. +- Fatal errors and every `done` event must leave the UI out of loading state. +- Preserve existing research progress and profile rendering. +- Do not redesign unrelated page/header/form/profile styles. +- Stage only files named by each task. + +--- + +## File map + +| File | Action | Responsibility | +|---|---|---| +| `src/app/hooks/use-research.ts` | Modify | Cache-aware request builder, reducer, actions, and SSE state | +| `src/app/components/cache-suggestions.tsx` | Create | Accessible inline candidate selection | +| `src/app/page.tsx` | Modify | Render suggestions, freshness, bypass, and refresh controls | +| `tests/unit/use-research-state.test.ts` | Create | Pure request/reducer state-transition tests | + +### Task 1: Make cache request and SSE transitions pure and testable + +**Files:** + +- Modify: `src/app/hooks/use-research.ts` +- Create: `tests/unit/use-research-state.test.ts` + +**Interfaces:** + +```ts +export interface CacheState { + hit: boolean; + matchedBy: CacheHitMatchedBy | null; + lastSyncedAt: string | null; + suggestions: CacheSuggestion[]; +} + +export interface ResearchState { + status: "idle" | "researching" | "choosing" | "building" | "done" | "error"; + // existing fields remain + cache: CacheState; + notice: string | null; +} + +export function buildResearchRequest( + input: CompanyInput, + cache?: ResearchRequest["cache"], +): ResearchRequest; + +export function reduceResearchEvent( + state: ResearchState, + event: StreamEvent, +): ResearchState; +``` + +- [ ] **Step 1: Write failing request-builder tests** + +```ts +it("builds default, selected, bypass, and refresh requests", () => { + const input = { name: "FPT" }; + + expect(buildResearchRequest(input)).toEqual({ input }); + expect(buildResearchRequest(input, { action: "select", companyId: "fpt" })) + .toEqual({ input, cache: { action: "select", companyId: "fpt" } }); + expect(buildResearchRequest(input, { action: "bypass" })) + .toEqual({ input, cache: { action: "bypass" } }); + expect(buildResearchRequest(input, { action: "refresh", companyId: "fpt" })) + .toEqual({ input, cache: { action: "refresh", companyId: "fpt" } }); +}); +``` + +- [ ] **Step 2: Write failing cache-event reducer tests** + +```ts +it("enters choosing state when suggestions arrive and stays there on done", () => { + const suggested = reduceResearchEvent(researchingState, { + event: "cache:suggestions", + data: { suggestions: [suggestion] }, + }); + const finished = reduceResearchEvent(suggested, { event: "done", data: {} }); + + expect(finished.status).toBe("choosing"); + expect(finished.cache.suggestions).toEqual([suggestion]); +}); + +it("records cache metadata before applying cached final results", () => { + const next = reduceResearchEvent(researchingState, { + event: "cache:hit", + data: { + companyId: "fpt", + matchedBy: "tax_id", + version: 2, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }, + }); + + expect(next.cache).toMatchObject({ + hit: true, + matchedBy: "tax_id", + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }); +}); + +it("treats cache_invalid as recoverable and persist_failed as terminal", () => { + const recoverable = reduceResearchEvent(researchingState, { + event: "error", + data: { code: "cache_invalid", message: "Cache không hợp lệ" }, + }); + expect(recoverable.status).toBe("researching"); + expect(recoverable.notice).toBe("Cache không hợp lệ"); + + const fatal = reduceResearchEvent(researchingState, { + event: "error", + data: { code: "persist_failed", message: "Không thể lưu kết quả" }, + }); + expect(fatal.status).toBe("error"); +}); +``` + +- [ ] **Step 3: Run and verify exports are missing** + +Run: `npm test -- tests/unit/use-research-state.test.ts` + +Expected: FAIL because the request builder/reducer/cache state do not exist. + +- [ ] **Step 4: Implement the pure builder and reducer** + +Move the current `handleSSEEvent` switch into `reduceResearchEvent`. Keep all +existing progress/finding/profile/diff/report behavior and add: + +- `cache:hit` → set cache metadata. +- `cache:suggestions` → clear result fields, store suggestions, set `choosing`. +- `error(cache_invalid)` → set `notice`, keep active research state. +- Other `error` codes → set terminal error status/message. +- `done` with suggestions → `choosing`. +- `done` with profile → `done`. +- Other `done` with fatal error → `error`. + +`handleSSEEvent` becomes one `setState(prev => reduceResearchEvent(prev, +event))` call. Keep malformed JSON handling unchanged. + +- [ ] **Step 5: Run focused state tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/use-research-state.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Commit state behavior** + +```bash +git add src/app/hooks/use-research.ts tests/unit/use-research-state.test.ts +git commit -m "feat(ui): handle cache research states" +``` + +### Task 2: Expose safe select, bypass, and refresh hook actions + +**Files:** + +- Modify: `src/app/hooks/use-research.ts` +- Modify: `tests/unit/use-research-state.test.ts` + +**Interfaces:** + +```ts +export interface UseResearchResult { + state: ResearchState; + research(input: CompanyInput): Promise; + selectSuggestion(companyId: string): Promise; + researchNewCompany(): Promise; + refresh(): Promise; + reset(): void; +} +``` + +- [ ] **Step 1: Add request-action tests** + +Test the action-to-request mapping through `buildResearchRequest`: + +- `selectSuggestion("company-a")` uses current `state.input` and action + `select`. +- `researchNewCompany()` uses current input and action `bypass`. +- `refresh()` uses current input/profile ID and action `refresh`. +- Missing input/profile produces a resolved no-op and no fetch. + +Do not install a hook rendering library. Keep network execution in one internal +`runResearch(input, cacheAction?)` callback and cover the pure request builder. + +- [ ] **Step 2: Implement one shared network path** + +Refactor current `research` so it delegates to: + +```ts +const runResearch = useCallback(async ( + input: CompanyInput, + cache?: ResearchRequest["cache"], +) => { + abortRef.current?.abort(); + const controller = new AbortController(); + abortRef.current = controller; + + setState({ + ...INITIAL_STATE, + input, + status: "researching", + }); + + try { + const response = await fetch("/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(buildResearchRequest(input, cache)), + signal: controller.signal, + }); + + await consumeResearchStream(response, setState); + } catch (error) { + if ((error as Error).name === "AbortError") return; + setState((current) => ({ + ...current, + status: "error", + error: (error as Error).message, + })); + } +}, []); +``` + +Extract the current response-status check, reader loop, and SSE parsing into +`consumeResearchStream`; replace its state mutations with +`setState((current) => reduceResearchEvent(current, event))`. This is a move of +existing behavior, not a second network implementation. + +The public actions read current state only to construct the approved action and +then call `runResearch`. Clear previous suggestions when select, bypass, or +refresh starts. Keep abort behavior for repeated clicks. + +- [ ] **Step 3: Run state tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/use-research-state.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 4: Commit hook actions** + +```bash +git add src/app/hooks/use-research.ts tests/unit/use-research-state.test.ts +git commit -m "feat(ui): add cache selection actions" +``` + +### Task 3: Render accessible cache suggestions + +**Files:** + +- Create: `src/app/components/cache-suggestions.tsx` +- Modify: `src/app/page.tsx` + +**Interfaces:** + +```ts +interface CacheSuggestionsProps { + suggestions: CacheSuggestion[]; + disabled: boolean; + onSelect(companyId: string): void; + onReject(): void; +} +``` + +- [ ] **Step 1: Create the inline semantic panel** + +Use this structure without a modal or new dependency: + +```tsx +
+
+

+ Chọn đúng doanh nghiệp +

+

+ Chúng tôi tìm thấy dữ liệu đã lưu có tên tương tự. +

+
+
    + {suggestions.map((suggestion) => ( +
  • + +
  • + ))} +
+ +
+``` + +Reuse existing focus styles or add visible `focus-visible` utilities to both +button types. Do not remove native button semantics. + +- [ ] **Step 2: Wire choosing state into the page** + +Destructure `selectSuggestion` and `researchNewCompany` from the hook. When +`state.status === "choosing"`, render `CacheSuggestions` in the result area +instead of research progress/profile. Preserve the left-side form and current +input so the user can correct identifiers instead. + +- [ ] **Step 3: Run lint/typecheck/build** + +Run: + +```bash +npm run lint +npm run typecheck +npm run build +``` + +Expected: PASS with no client/server boundary violation. + +- [ ] **Step 4: Commit suggestions UI** + +```bash +git add src/app/components/cache-suggestions.tsx src/app/page.tsx +git commit -m "feat(ui): confirm cached companies" +``` + +### Task 4: Display cache freshness and manual refresh + +**Files:** + +- Modify: `src/app/page.tsx` +- Modify: `tests/unit/use-research-state.test.ts` + +**Interfaces:** Uses `state.cache.lastSyncedAt`, `state.profile.id`, and the +hook's `refresh()` action. + +- [ ] **Step 1: Add refresh state expectations** + +Extend reducer/request tests to prove refresh: + +- clears previous suggestions/notices; +- sets status to `researching`; +- sends the displayed profile ID; +- a subsequent result replaces cache metadata and profile version. + +- [ ] **Step 2: Add the freshness/refresh controls above the profile** + +When a profile and `lastSyncedAt` exist, render: + +```tsx +
+ + Cập nhật lần cuối: {new Date(state.cache.lastSyncedAt).toLocaleString("vi-VN")} + + +
+``` + +Show `state.notice` as a neutral/warning callout, distinct from the existing +fatal error callout. + +- [ ] **Step 3: Run focused and full UI compilation checks** + +Run: + +```bash +npm test -- tests/unit/use-research-state.test.ts +npm run lint +npm run typecheck +npm run build +``` + +Expected: PASS. + +- [ ] **Step 4: Verify visually** + +Run `npm run dev`, then use the in-app browser workflow to capture before/after +screenshots for: + +1. Multiple cache suggestions. +2. Cached profile with last-synchronized time. +3. Refresh in progress with disabled button. +4. Recoverable cache-invalid notice. + +Check keyboard focus order, readable timestamps, narrow viewport wrapping, and +that selecting/rejecting suggestions reaches the intended server request. + +- [ ] **Step 5: Commit freshness UI** + +```bash +git add src/app/page.tsx tests/unit/use-research-state.test.ts +git commit -m "feat(ui): show cache freshness and refresh" +``` + +## Sprint 04 review gate + +Run: + +```bash +npm test -- tests/unit/use-research-state.test.ts \ + tests/unit/research-cache-route.test.ts \ + tests/e2e/workflow-e2e.test.ts +npm run lint +npm run typecheck +npm run build +git status --short +``` + +Expected: state/action/server contract tests pass, all four UI states are +visually verified, accessibility basics work by keyboard, and the worktree is +clean before Sprint 05. diff --git a/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-05-telemetry-and-release-hardening.md b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-05-telemetry-and-release-hardening.md new file mode 100644 index 0000000..83aba48 --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-supabase-research-cache-sprints/sprint-05-telemetry-and-release-hardening.md @@ -0,0 +1,402 @@ +# Sprint 05 — Telemetry and Release Hardening Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make cache outcomes observable without leaking identifiers, close the +remaining negative-path coverage, verify database permissions/concurrency, and +prepare the complete feature for release. + +**Architecture:** Extend the existing Langfuse wrapper with one cache-outcome +metadata function and one HMAC fingerprint helper. Keep route/storage behavior +unchanged except for telemetry calls; finish with security, database, UI, and +full repository verification. + +**Tech Stack:** Node.js `crypto`, Langfuse JS/TS 5.10.1, +`@langfuse/tracing` 5.10.1, Vitest 4.1.11, Supabase CLI 2.115.0, +Next.js 16.3.2. + +**Spec:** `docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md` + +## Global Constraints + +- Use HMAC-SHA256 with a dedicated server secret; plain SHA-256 is forbidden + for low-entropy tax IDs. +- Never log raw tax IDs, domains, credentials, or request bodies. +- Missing HMAC secret omits the fingerprint and does not fail research. +- Reuse Langfuse; do not add a logger, metrics SDK, or telemetry dependency. +- Cache telemetry must also exist for hits/suggestions that never create paid + providers. +- Do not change cache lookup/persistence semantics while adding telemetry. +- Verify anon/authenticated cannot call cache RPCs or access cache tables. +- Refresh rate limiting remains explicitly deferred. +- Stage only files named by each task. + +--- + +## File map + +| File | Action | Responsibility | +|---|---|---| +| `.env.example` | Modify | Dedicated HMAC secret documentation | +| `src/observability/langfuse.ts` | Modify | HMAC fingerprint and cache outcome metadata | +| `src/app/api/research/route.ts` | Modify | Record lookup outcomes across every route branch | +| `tests/unit/langfuse-observability.test.ts` | Modify | Fingerprint and cache metadata tests | +| `tests/unit/research-cache-route.test.ts` | Modify | Final negative-path and telemetry matrix | +| `tests/integration/supabase-cache-concurrency.test.ts` | Modify | Data API grants/RLS and rollback verification | +| `README.md` | Modify | Cache behavior, configuration, and operational caveats | +| `docs/superpowers/specs/2026-08-26-supabase-research-cache-design.md` | Read only | Acceptance checklist source | + +### Task 1: Add non-reversible cache-key fingerprints + +**Files:** + +- Modify: `.env.example` +- Modify: `src/observability/langfuse.ts` +- Modify: `tests/unit/langfuse-observability.test.ts` + +**Interfaces:** + +```ts +export function fingerprintCacheKey( + keyType: "tax_id" | "domain", + value: string, + secret?: string, +): string | undefined; + +export interface ResearchCacheTelemetry { + cacheOutcome: + | "hit" + | "miss" + | "suggestions" + | "refresh" + | "bypass" + | "conflict" + | "invalid"; + matchedBy?: "tax_id" | "domain" | "normalized_name" | "selected"; + companyId?: string; + version?: number; + lastSyncedAt?: string; + lookupDurationMs: number; + conflictingCompanyIds?: string[]; + keyType?: "tax_id" | "domain"; + keyFingerprint?: string; +} + +export function updateResearchCacheOutcome( + telemetry: ResearchCacheTelemetry, +): void; +``` + +- [ ] **Step 1: Write failing HMAC tests** + +```ts +it("fingerprints low-entropy tax IDs with a keyed HMAC", () => { + const first = fingerprintCacheKey("tax_id", "0101248141", "secret-a"); + const second = fingerprintCacheKey("tax_id", "0101248141", "secret-b"); + + expect(first).toMatch(/^[a-f0-9]{64}$/); + expect(first).not.toContain("0101248141"); + expect(second).not.toBe(first); + expect(fingerprintCacheKey("tax_id", "0101248141", undefined)).toBeUndefined(); +}); + +it("separates key types in the authenticated message", () => { + expect(fingerprintCacheKey("tax_id", "example.vn", "secret")) + .not.toBe(fingerprintCacheKey("domain", "example.vn", "secret")); +}); +``` + +- [ ] **Step 2: Run and verify the helper is missing** + +Run: `npm test -- tests/unit/langfuse-observability.test.ts` + +Expected: FAIL because `fingerprintCacheKey` is not exported. + +- [ ] **Step 3: Implement HMAC with Node's standard library** + +```ts +import { createHmac } from "node:crypto"; + +export function fingerprintCacheKey( + keyType: "tax_id" | "domain", + value: string, + secret = process.env.CACHE_TELEMETRY_HMAC_SECRET, +): string | undefined { + if (!secret) return undefined; + return createHmac("sha256", secret) + .update(`${keyType}\0${value}`) + .digest("hex"); +} +``` + +Document in `.env.example`: + +```dotenv +# Server-only HMAC secret for cache-key telemetry fingerprints +CACHE_TELEMETRY_HMAC_SECRET=replace-with-random-server-secret +``` + +Do not prefix it with `NEXT_PUBLIC_` and do not reuse a Supabase/Langfuse key. + +- [ ] **Step 4: Run focused tests and typecheck** + +Run: + +```bash +npm test -- tests/unit/langfuse-observability.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 5: Commit the fingerprint helper** + +```bash +git add .env.example src/observability/langfuse.ts tests/unit/langfuse-observability.test.ts +git commit -m "feat(observability): protect cache identifiers" +``` + +### Task 2: Record every cache outcome in the existing trace + +**Files:** + +- Modify: `src/observability/langfuse.ts` +- Modify: `src/app/api/research/route.ts` +- Modify: `tests/unit/langfuse-observability.test.ts` +- Modify: `tests/unit/research-cache-route.test.ts` + +**Interfaces:** `updateResearchCacheOutcome(telemetry)` updates active Langfuse +observation metadata and is a no-op when Langfuse is disabled. + +- [ ] **Step 1: Write failing metadata tests** + +Mock `updateActiveObservation` and assert: + +```ts +updateResearchCacheOutcome({ + cacheOutcome: "hit", + matchedBy: "tax_id", + companyId: "company-a", + version: 3, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + lookupDurationMs: 12, +}); + +expect(updateActiveObservation).toHaveBeenCalledWith({ + metadata: expect.objectContaining({ + cacheOutcome: "hit", + matchedBy: "tax_id", + companyId: "company-a", + cacheVersion: 3, + cacheLookupDurationMs: 12, + }), +}); +``` + +Add a conflict test proving metadata contains only company IDs, key type, and +HMAC fingerprint—not the raw key. + +- [ ] **Step 2: Implement metadata-only updates** + +Call: + +```ts +updateActiveObservation({ + metadata: { + cacheOutcome: telemetry.cacheOutcome, + matchedBy: telemetry.matchedBy, + companyId: telemetry.companyId, + cacheVersion: telemetry.version, + cacheLastSyncedAt: telemetry.lastSyncedAt, + cacheLookupDurationMs: telemetry.lookupDurationMs, + conflictingCompanyIds: telemetry.conflictingCompanyIds, + cacheKeyType: telemetry.keyType, + cacheKeyFingerprint: telemetry.keyFingerprint, + }, +}); +``` + +Reuse the existing Langfuse enabled check. Do not put cache metadata into +`output`, because workflow outcome updates already own that field. + +- [ ] **Step 3: Start the trace before cache lookup and instrument branches** + +Allow `ResearchTraceContext.companyId` to be optional at trace start. Wrap cache +lookup in the existing `traceResearch` scope, measure duration with +`performance.now()`, then call `updateResearchCacheOutcome` for hit, miss, +suggestions, select, refresh, bypass, conflict, and corrupt-cache recovery. + +Create LangChain/LangGraph callbacks only in the miss/refresh pipeline branch +after the company ID is resolved. A hit still gets one research trace without +provider spans. + +- [ ] **Step 4: Add route telemetry assertions** + +For hit, suggestions, miss, refresh, conflict, and invalid cache, assert exactly +one call containing the expected `cacheOutcome`. For a conflict, assert the +mock received an HMAC fingerprint and never received the raw test tax ID/domain. + +- [ ] **Step 5: Run observability/route suites** + +Run: + +```bash +npm test -- tests/unit/langfuse-observability.test.ts \ + tests/unit/research-cache-route.test.ts \ + tests/unit/research-route-observability.test.ts +npm run typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Commit cache telemetry** + +```bash +git add src/observability/langfuse.ts src/app/api/research/route.ts \ + tests/unit/langfuse-observability.test.ts tests/unit/research-cache-route.test.ts +git commit -m "feat(observability): trace cache outcomes" +``` + +### Task 3: Close the security/error regression matrix + +**Files:** + +- Modify: `tests/unit/research-cache-route.test.ts` +- Modify: `tests/integration/supabase-cache-concurrency.test.ts` + +**Interfaces:** No production interface change; this task proves approved +negative-path contracts. + +- [ ] **Step 1: Verify route tests contain all named regressions** + +Add any missing test so the suite explicitly contains these behaviors: + +1. `503 cache_unavailable` and zero paid-provider factory calls. +2. Corrupt JSONB emits `cache_invalid`, then runs/persists fresh research. +3. `select` rejects an existing unrelated company ID. +4. `refresh` rejects an existing unrelated company ID. +5. Pre-stream strong-key conflict returns `409`. +6. Post-pipeline conflict emits `error(identity_conflict)` then `done`, with no + final profile/diff/analysis events. +7. Persistence failure emits `error(persist_failed)` then `done`, with no final + profile/diff/analysis events. +8. Every successful hit/miss/suggestion path emits exactly one `done`. + +Use provider-construction spies in cases 1, 3, 4, and 5. + +- [ ] **Step 2: Verify Data API permissions with anon and service clients** + +In the local Supabase integration suite, create separate anon and service-role +clients. Assert anon cannot select/insert/update cache tables and cannot execute +the three cache RPCs. Assert the service client can execute the intended RPCs. +Check PostgREST error codes rather than matching English error text. + +- [ ] **Step 3: Rerun concurrency and rollback checks** + +Run two independent service clients concurrently as defined in Sprint 02. Also +force a post-pipeline tax-ID conflict inside `persist_research_snapshot` and +assert profile, report, diff, and identity update all roll back. + +- [ ] **Step 4: Run focused security gates** + +Run: + +```bash +npm test -- tests/unit/research-cache-route.test.ts +eval "$(npx supabase status -o env)" +SUPABASE_TEST_URL="$API_URL" SUPABASE_TEST_SERVICE_ROLE_KEY="$SERVICE_ROLE_KEY" \ +SUPABASE_TEST_ANON_KEY="$ANON_KEY" npm run test:db +npx supabase db advisors --local +``` + +Expected: all regression, permission, concurrency, rollback, and advisor checks +pass. + +- [ ] **Step 5: Commit the hardening tests** + +```bash +git add tests/unit/research-cache-route.test.ts tests/integration/supabase-cache-concurrency.test.ts +git commit -m "test(cache): cover security failure paths" +``` + +### Task 4: Document operations and execute release verification + +**Files:** + +- Modify: `README.md` + +**Interfaces:** Documents supported configuration and operator-visible cache +behavior; no runtime interface change. + +- [ ] **Step 1: Document cache configuration and behavior** + +Add concise README sections covering: + +- Required `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, and optional + `CACHE_TELEMETRY_HMAC_SECRET`. +- Cache hit/miss/suggestions/manual refresh behavior. +- Tax ID/domain/name matching safety rules. +- Local Supabase start/reset/database-test commands. +- Cache entries have no TTL. +- Identity merge, first-miss stampede prevention, partial pipeline resume, and + refresh rate limiting are not implemented. +- Raw service-role/HMAC secrets are server-only. + +- [ ] **Step 2: Run the complete repository gate** + +Run: + +```bash +npm test +npm run lint +npm run typecheck +npm run build +npx supabase migration list --local +npx supabase db advisors --local +eval "$(npx supabase status -o env)" +SUPABASE_TEST_URL="$API_URL" SUPABASE_TEST_SERVICE_ROLE_KEY="$SERVICE_ROLE_KEY" \ +SUPABASE_TEST_ANON_KEY="$ANON_KEY" npm run test:db +git diff --check +``` + +Expected: every command passes with no warning attributable to the cache +feature. + +- [ ] **Step 3: Repeat visual acceptance checks** + +Use the local app and local Supabase to verify: + +1. First query miss persists a complete snapshot. +2. Second safe query hit performs no provider call and displays freshness. +3. Name-only query shows suggestions and requires confirmation. +4. Rejecting suggestions starts new research. +5. Refresh creates the next version/diff. +6. Invalid cache recovers visibly; fatal persistence stops cleanly. + +Capture final screenshots and compare them with Sprint 04 screenshots for +unexpected layout regressions. + +- [ ] **Step 4: Commit operational documentation** + +```bash +git add README.md +git commit -m "docs(cache): document operations and limits" +``` + +## Sprint 05 release gate + +The feature is ready for branch integration only when: + +- all Sprint 05 commands pass; +- Supabase advisors are clean; +- anon/authenticated direct access is denied; +- service-role access is server-only; +- HMAC telemetry contains no raw keys; +- hit/miss/suggestion/refresh/error traces are observable; +- all SSE paths terminate; +- final UI screenshots are reviewed; +- `git status --short` is empty. + +Skipped by design: refresh rate limiting, full first-miss stampede prevention, +TTL, automatic identity merge, and partial pipeline resume. Add them only in a +separate approved spec when production evidence justifies the extra machinery. From f501aea6615dda9e4d406cfb106e22df355fb732 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 14:46:26 +0700 Subject: [PATCH 03/17] feat(cache): implement sprint 01 contracts and normalization --- .gitignore | 3 +- Dockerfile | 2 +- src/lib/types.ts | 219 +++++++++++++++++++++++++++- src/modules/cache/index.ts | 127 ++++++++++++++++ tests/unit/research-cache.test.ts | 204 ++++++++++++++++++++++++++ tests/unit/types-validation.test.ts | 132 +++++++++++++++++ 6 files changed, 684 insertions(+), 3 deletions(-) create mode 100644 src/modules/cache/index.ts create mode 100644 tests/unit/research-cache.test.ts diff --git a/.gitignore b/.gitignore index 01ab0a9..83272c7 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,5 @@ next-env.d.ts .worktrees/ # temp fixtures -/tmp/ \ No newline at end of file +/tmp/ +supabase/.temp/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 1720aaf..9b0f815 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # Dockerfile — PartnerIQ (Google Cloud Run / Production) # ═══════════════════════════════════════════════════════ -FROM node:20-alpine AS base +FROM node:22-alpine AS base # Step 1: Install dependencies FROM base AS deps diff --git a/src/lib/types.ts b/src/lib/types.ts index 6ab2504..fc40d85 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -185,9 +185,218 @@ export interface AnalysisContext { sponsorCriteria?: string; } +// ─── Cache & Request Contracts ─── + +export const CacheActionSchema = z.discriminatedUnion("action", [ + z.object({ action: z.literal("select"), companyId: z.string().min(1) }).strict(), + z.object({ action: z.literal("refresh"), companyId: z.string().min(1) }).strict(), + z.object({ action: z.literal("bypass") }).strict(), +]); + +export const ResearchRequestSchema = z.object({ + input: CompanyInputSchema, + cache: CacheActionSchema.optional(), +}).strict(); + +export type ResearchRequest = z.infer; + +export type ResearchErrorCode = + | "identity_conflict" + | "cache_invalid" + | "persist_failed" + | "research_failed"; + +export type CacheHitMatchedBy = "tax_id" | "domain" | "selected"; + +export interface CacheSuggestion { + companyId: string; + officialName: string; + taxId?: string; + domain?: string; + lastSyncedAt: string; +} + +// ─── Runtime Schemas for Cached Snapshot ─── + +export const SourceNameSchema = z.enum([ + "web_search", + "website", + "registry", + "news", + "linkedin", +]); + +export const AddressSchema = z.object({ + street: z.string().optional(), + city: z.string().optional(), + province: z.string().optional(), + country: z.string(), +}); + +export const PersonSchema = z.object({ + name: z.string(), + title: z.string(), + source: SourceNameSchema, + confidence: z.number().min(0).max(1), +}); + +export const ActivitySchema = z.object({ + date: z.coerce.date(), + title: z.string(), + summary: z.string(), + url: z.string(), + source: SourceNameSchema, +}); + +export const SourceCitationSchema = z.object({ + source: SourceNameSchema, + url: z.string(), + accessedAt: z.coerce.date(), + fieldsContributed: z.array(z.string()), +}); + +export const CompanySizeSchema = z.enum([ + "1-10", + "11-50", + "51-200", + "201-500", + "501-1000", + "1000+", +]); + +export const RevenueRangeSchema = z.enum([ + "< 1B VND", + "1-10B VND", + "10-100B VND", + "100B-1T VND", + "> 1T VND", +]); + +export const CompanyProfileSchema = z.object({ + id: z.string(), + version: z.number().int().min(1), + createdAt: z.coerce.date(), + input: CompanyInputSchema, + officialName: z.string(), + tradingNames: z.array(z.string()), + taxId: z.string().optional(), + industry: z.array(z.string()), + description: z.string(), + foundedYear: z.number().int().optional(), + headquarters: AddressSchema.optional(), + website: z.string().optional(), + keyPeople: z.array(PersonSchema), + products: z.array(z.string()), + markets: z.array(z.string()), + companySize: CompanySizeSchema.optional(), + revenue: RevenueRangeSchema.optional(), + recentActivities: z.array(ActivitySchema), + lastUpdated: z.coerce.date(), + sources: z.array(SourceCitationSchema), + overallConfidence: z.number().min(0).max(1), + lowConfidence: z.boolean().optional(), +}); + +export const FieldChangeSchema = z.object({ + field: z.string(), + oldValue: z.unknown(), + newValue: z.unknown(), + changeType: z.enum(["added", "removed", "modified"]), + significance: z.enum(["high", "medium", "low"]), +}); + +export const ProfileDiffSchema = z.object({ + companyId: z.string(), + fromVersion: z.number().int(), + toVersion: z.number().int(), + changes: z.array(FieldChangeSchema), + summary: z.string(), +}); + +export const FitScoreCriteriaSchema = z.object({ + name: z.string(), + score: z.number().min(0).max(100), + weight: z.number(), + reasoning: z.string().optional(), +}); + +export const FitScoreSchema = z.object({ + score: z.number().min(0).max(100), + reasoning: z.string(), + criteria: z.array(FitScoreCriteriaSchema), +}); + +export const RiskFlagSchema = z.object({ + type: z.enum(["legal", "financial", "reputation", "operational"]), + description: z.string(), + severity: z.enum(["high", "medium", "low"]), + source: SourceNameSchema, +}); + +export const SuggestedActionSchema = z.object({ + action: z.string(), + priority: z.enum(["high", "medium", "low"]), + reasoning: z.string(), +}); + +export const AnalysisReportSchema = z.object({ + companyId: z.string(), + generatedAt: z.coerce.date(), + fitScore: FitScoreSchema.optional(), + riskFlags: z.array(RiskFlagSchema), + suggestedActions: z.array(SuggestedActionSchema), + executiveSummary: z.string(), +}); + +export interface ResearchSnapshot { + profile: CompanyProfile; + report: AnalysisReport; + diff: ProfileDiff | null; + lastSyncedAt: string; +} + +export const ResearchSnapshotSchema = z.object({ + profile: CompanyProfileSchema, + report: AnalysisReportSchema, + diff: ProfileDiffSchema.nullable(), + lastSyncedAt: z.string().datetime(), +}).strict().superRefine((snapshot, ctx) => { + if (snapshot.report.companyId !== snapshot.profile.id) { + ctx.addIssue({ + code: "custom", + path: ["report", "companyId"], + message: "Analysis report companyId must match profile id", + }); + } + if ( + snapshot.diff && + (snapshot.diff.companyId !== snapshot.profile.id || + snapshot.diff.toVersion !== snapshot.profile.version) + ) { + ctx.addIssue({ + code: "custom", + path: ["diff"], + message: "Profile diff must match profile id and version", + }); + } +}); + // ─── SSE Stream Events ─── export type StreamEvent = + | { + event: "cache:hit"; + data: { + companyId: string; + matchedBy: CacheHitMatchedBy; + version: number; + lastSyncedAt: string; + }; + } + | { + event: "cache:suggestions"; + data: { suggestions: CacheSuggestion[] }; + } | { event: "research:start"; data: { sources: SourceName[] } } | { event: "research:progress"; @@ -201,7 +410,14 @@ export type StreamEvent = | { event: "profile:ready"; data: { profile: CompanyProfile } } | { event: "diff:ready"; data: { diff: ProfileDiff | null } } | { event: "analysis:ready"; data: { report: AnalysisReport } } - | { event: "error"; data: { message: string; source?: SourceName } } + | { + event: "error"; + data: { + message: string; + source?: SourceName; + code?: ResearchErrorCode; + }; + } | { event: "done"; data: Record }; // ─── Source Result (error contract) ─── @@ -230,3 +446,4 @@ export interface PreparedEvidence { sourceCoverage: number; outcome: Exclude; } + diff --git a/src/modules/cache/index.ts b/src/modules/cache/index.ts new file mode 100644 index 0000000..a0e1805 --- /dev/null +++ b/src/modules/cache/index.ts @@ -0,0 +1,127 @@ +import type { CompanyInput } from "@/lib/types"; + +const TAX_ID_PATTERN = /^\d{10}(?:\d{3})?$/; + +export interface NormalizedCompanyIdentity { + taxId: string | null; + domain: string | null; + name: string; +} + +export interface IdentityCandidate { + companyId: string; + taxId: string | null; + domain: string | null; + name: string; +} + +export type CacheDecision = + | { kind: "hit"; companyId: string; matchedBy: "tax_id" | "domain" } + | { kind: "suggestions"; companyIds: string[] } + | { kind: "miss" } + | { kind: "conflict"; taxCompanyId: string; domainCompanyIds: string[] }; + +export function normalizeTaxId(value?: string): string | null { + if (!value) return null; + const normalized = value.trim().replace(/[\s.-]/g, ""); + if (!TAX_ID_PATTERN.test(normalized)) { + throw new Error("Mã số thuế phải có 10 hoặc 13 chữ số"); + } + return normalized; +} + +export function normalizeDomain(website?: string): string | null { + if (!website) return null; + try { + return new URL(website).hostname + .toLowerCase() + .replace(/\.$/, "") + .replace(/^www\./, ""); + } catch { + return null; + } +} + +export function normalizeName(name: string): string { + return name + .normalize("NFKC") + .trim() + .toLocaleLowerCase("vi-VN") + .replace(/\s+/g, " "); +} + +export function normalizeCompanyIdentity(input: CompanyInput): NormalizedCompanyIdentity { + return { + taxId: normalizeTaxId(input.taxId), + domain: normalizeDomain(input.website), + name: normalizeName(input.name), + }; +} + +export function decideCacheLookup( + identity: NormalizedCompanyIdentity, + candidates: readonly IdentityCandidate[] +): CacheDecision { + const taxMatches = identity.taxId + ? candidates.filter((c) => c.taxId === identity.taxId) + : []; + + const domainMatches = identity.domain + ? candidates.filter((c) => c.domain === identity.domain) + : []; + + // 1. Tax ID lookup + if (identity.taxId && taxMatches.length > 0) { + const taxCompanyId = taxMatches[0].companyId; + if (identity.domain && domainMatches.length > 0) { + const domainCompanyIds = Array.from( + new Set(domainMatches.map((c) => c.companyId)) + ).sort(); + if (!domainCompanyIds.includes(taxCompanyId)) { + return { + kind: "conflict", + taxCompanyId, + domainCompanyIds, + }; + } + } + return { + kind: "hit", + companyId: taxCompanyId, + matchedBy: "tax_id", + }; + } + + // 2. Domain lookup + if (identity.domain && domainMatches.length > 0) { + const domainCompanyIds = Array.from( + new Set(domainMatches.map((c) => c.companyId)) + ).sort(); + if (domainCompanyIds.length === 1) { + return { + kind: "hit", + companyId: domainCompanyIds[0], + matchedBy: "domain", + }; + } + return { + kind: "suggestions", + companyIds: domainCompanyIds, + }; + } + + // 3. Name lookup + const nameMatches = candidates.filter((c) => c.name === identity.name); + if (nameMatches.length > 0) { + const nameCompanyIds = Array.from( + new Set(nameMatches.map((c) => c.companyId)) + ).sort(); + return { + kind: "suggestions", + companyIds: nameCompanyIds, + }; + } + + // 4. Miss + return { kind: "miss" }; +} diff --git a/tests/unit/research-cache.test.ts b/tests/unit/research-cache.test.ts new file mode 100644 index 0000000..5e999b1 --- /dev/null +++ b/tests/unit/research-cache.test.ts @@ -0,0 +1,204 @@ +import { describe, it, expect } from "vitest"; +import { + normalizeCompanyIdentity, + decideCacheLookup, + type IdentityCandidate, + type NormalizedCompanyIdentity, +} from "@/modules/cache"; + +describe("Research Cache - Normalization", () => { + it("normalizes tax ID, domain, and Vietnamese name without dropping legal suffixes", () => { + expect( + normalizeCompanyIdentity({ + name: " CÔNG TY CP Ánh Dương ", + taxId: "0101-245.486", + website: "https://WWW.Example.VN:443/about?q=1", + }) + ).toEqual({ + taxId: "0101245486", + domain: "example.vn", + name: "công ty cp ánh dương", + }); + }); + + it("handles 13-digit tax IDs correctly", () => { + expect( + normalizeCompanyIdentity({ + name: "Chi nhánh FPT", + taxId: "0101248141-001", + }) + ).toEqual({ + taxId: "0101248141001", + domain: null, + name: "chi nhánh fpt", + }); + }); + + it("rejects a malformed supplied tax ID", () => { + expect(() => + normalizeCompanyIdentity({ name: "FPT", taxId: "abc" }) + ).toThrow("Mã số thuế phải có 10 hoặc 13 chữ số"); + + expect(() => + normalizeCompanyIdentity({ name: "FPT", taxId: "123456789" }) + ).toThrow("Mã số thuế phải có 10 hoặc 13 chữ số"); + + expect(() => + normalizeCompanyIdentity({ name: "FPT", taxId: "12345678901" }) + ).toThrow("Mã số thuế phải có 10 hoặc 13 chữ số"); + }); + + it("normalizes domain removing leading www. and trailing dot", () => { + expect( + normalizeCompanyIdentity({ + name: "Test Corp", + website: "http://www.sub.domain.vn./path", + }) + ).toEqual({ + taxId: null, + domain: "sub.domain.vn", + name: "test corp", + }); + }); + + it("handles null/optional taxId and website gracefully", () => { + expect( + normalizeCompanyIdentity({ + name: "Công ty TNHH Một Thành Viên", + }) + ).toEqual({ + taxId: null, + domain: null, + name: "công ty tnhh một thành viên", + }); + }); +}); + +describe("Research Cache - Decision Logic", () => { + const withTaxAndDomain: NormalizedCompanyIdentity = { + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }; + + const candidatesForSameCompany: IdentityCandidate[] = [ + { + companyId: "company-a", + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }, + ]; + + const withConflictingKeys: NormalizedCompanyIdentity = { + taxId: "0101245486", + domain: "fpt.com.vn", + name: "tập đoàn vingroup", + }; + + const conflictingCandidates: IdentityCandidate[] = [ + { + companyId: "company-a", + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }, + { + companyId: "company-b", + taxId: "0101248141", + domain: "fpt.com.vn", + name: "công ty cp fpt", + }, + ]; + + const domainOnly: NormalizedCompanyIdentity = { + taxId: null, + domain: "shared-domain.vn", + name: "công ty a", + }; + + const twoDomainCandidates: IdentityCandidate[] = [ + { + companyId: "company-a", + taxId: "0101245486", + domain: "shared-domain.vn", + name: "công ty a", + }, + { + companyId: "company-b", + taxId: "0101248141", + domain: "shared-domain.vn", + name: "công ty b", + }, + ]; + + const nameOnly: NormalizedCompanyIdentity = { + taxId: null, + domain: null, + name: "công ty cp ánh dương", + }; + + const oneNameCandidate: IdentityCandidate[] = [ + { + companyId: "company-a", + taxId: "0101245486", + domain: "anhduong.vn", + name: "công ty cp ánh dương", + }, + ]; + + it("resolves exact tax ID and compatible domain to an automatic hit", () => { + expect(decideCacheLookup(withTaxAndDomain, candidatesForSameCompany)).toEqual({ + kind: "hit", + companyId: "company-a", + matchedBy: "tax_id", + }); + }); + + it("detects conflict when supplied tax ID and domain resolve to different companies", () => { + expect(decideCacheLookup(withConflictingKeys, conflictingCandidates)).toEqual({ + kind: "conflict", + taxCompanyId: "company-a", + domainCompanyIds: ["company-b"], + }); + }); + + it("returns suggestions for multi-candidate domain matches", () => { + expect(decideCacheLookup(domainOnly, twoDomainCandidates)).toEqual({ + kind: "suggestions", + companyIds: ["company-a", "company-b"], + }); + }); + + it("returns automatic hit for unique domain match without tax ID", () => { + const singleDomainCandidate: IdentityCandidate[] = [ + { + companyId: "company-a", + taxId: "0101245486", + domain: "unique-domain.vn", + name: "công ty a", + }, + ]; + expect( + decideCacheLookup( + { taxId: null, domain: "unique-domain.vn", name: "công ty a" }, + singleDomainCandidate + ) + ).toEqual({ + kind: "hit", + companyId: "company-a", + matchedBy: "domain", + }); + }); + + it("returns suggestions for name matches", () => { + expect(decideCacheLookup(nameOnly, oneNameCandidate)).toEqual({ + kind: "suggestions", + companyIds: ["company-a"], + }); + }); + + it("returns miss when no candidates match", () => { + expect(decideCacheLookup(nameOnly, [])).toEqual({ kind: "miss" }); + }); +}); diff --git a/tests/unit/types-validation.test.ts b/tests/unit/types-validation.test.ts index 1f2e8dc..9763af1 100644 --- a/tests/unit/types-validation.test.ts +++ b/tests/unit/types-validation.test.ts @@ -56,3 +56,135 @@ describe("Domain Validation - CompanyInputSchema", () => { expect(result.success).toBe(false); }); }); + +describe("ResearchRequestSchema and ResearchSnapshotSchema", () => { + const validProfileJson = { + id: "fpt-corp", + version: 1, + createdAt: "2026-08-26T00:00:00.000Z", + input: { name: "FPT Corporation" }, + officialName: "Công ty Cổ phần FPT", + tradingNames: ["FPT Corp"], + taxId: "0101248141", + industry: ["Technology"], + description: "Technology corporation in Vietnam", + foundedYear: 1988, + headquarters: { country: "Vietnam", city: "Hanoi" }, + website: "https://fpt.com.vn", + keyPeople: [ + { + name: "Trương Gia Bình", + title: "Chủ tịch HĐQT", + source: "website", + confidence: 0.9, + }, + ], + products: ["FPT Software", "FPT Telecom"], + markets: ["Vietnam", "Global"], + companySize: "1000+", + revenue: "> 1T VND", + recentActivities: [ + { + date: "2026-08-20T00:00:00.000Z", + title: "AI Expansion", + summary: "FPT expands AI research hub", + url: "https://fpt.com.vn/news/1", + source: "news", + }, + ], + lastUpdated: "2026-08-26T08:00:00.000Z", + sources: [ + { + source: "website", + url: "https://fpt.com.vn", + accessedAt: "2026-08-26T08:00:00.000Z", + fieldsContributed: ["officialName", "products"], + }, + ], + overallConfidence: 0.95, + }; + + const validReportJson = { + companyId: "fpt-corp", + generatedAt: "2026-08-26T08:00:00.000Z", + fitScore: { + score: 88, + reasoning: "Strong industry alignment and solid financial health.", + criteria: [ + { name: "Industry Alignment", score: 90, weight: 0.3, reasoning: "Tech fit" }, + ], + }, + riskFlags: [ + { + type: "operational", + description: "Talent competition", + severity: "low", + source: "news", + }, + ], + suggestedActions: [ + { + action: "Partner on Cloud transformation", + priority: "high", + reasoning: "High capability match", + }, + ], + executiveSummary: "FPT is an ideal strategic technology partner.", + }; + + it("accepts default, select, refresh, and bypass research requests", async () => { + const { ResearchRequestSchema } = await import("@/lib/types"); + const requests = [ + { input: { name: "FPT" } }, + { input: { name: "FPT" }, cache: { action: "select", companyId: "fpt" } }, + { input: { name: "FPT" }, cache: { action: "refresh", companyId: "fpt" } }, + { input: { name: "FPT" }, cache: { action: "bypass" } }, + ]; + + expect(requests.every((request) => ResearchRequestSchema.safeParse(request).success)) + .toBe(true); + }); + + it("rejects cache actions with missing or unexpected company IDs", async () => { + const { ResearchRequestSchema } = await import("@/lib/types"); + expect( + ResearchRequestSchema.safeParse({ + input: { name: "FPT" }, + cache: { action: "select" }, + }).success, + ).toBe(false); + expect( + ResearchRequestSchema.safeParse({ + input: { name: "FPT" }, + cache: { action: "bypass", companyId: "injected" }, + }).success, + ).toBe(false); + }); + + it("parses a complete research snapshot and restores dates", async () => { + const { ResearchSnapshotSchema } = await import("@/lib/types"); + const result = ResearchSnapshotSchema.parse({ + profile: validProfileJson, + report: validReportJson, + diff: null, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }); + + expect(result.profile.lastUpdated).toBeInstanceOf(Date); + expect(result.report.generatedAt).toBeInstanceOf(Date); + expect(result.profile.createdAt).toBeInstanceOf(Date); + }); + + it("rejects mismatched and incomplete snapshots", async () => { + const { ResearchSnapshotSchema } = await import("@/lib/types"); + expect(() => + ResearchSnapshotSchema.parse({ + profile: validProfileJson, + report: { ...validReportJson, companyId: "other-company" }, + diff: null, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }), + ).toThrow(); + }); +}); + From 762cca3ad02c9e36764ddb82dd64c46bc338e617 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 14:50:38 +0700 Subject: [PATCH 04/17] feat(storage): implement sprint 02 supabase schema and storage adapter --- .env.example | 2 +- .github/workflows/ci.yml | 8 + package.json | 2 + src/adapters/storage/memory.ts | 225 +++++++++- src/adapters/storage/supabase.ts | 171 +++++++- src/adapters/storage/types.ts | 38 +- src/config/index.ts | 2 +- src/modules/cache/index.ts | 32 ++ supabase/config.toml | 414 ++++++++++++++++++ .../20260826074659_research_cache.sql | 316 +++++++++++++ supabase/schema.sql | 292 ++++++++++-- .../supabase-cache-concurrency.test.ts | 102 +++++ tests/unit/adapters.test.ts | 114 +++++ tests/unit/supabase-storage.test.ts | 166 ++++++- 14 files changed, 1824 insertions(+), 60 deletions(-) create mode 100644 supabase/config.toml create mode 100644 supabase/migrations/20260826074659_research_cache.sql create mode 100644 tests/integration/supabase-cache-concurrency.test.ts diff --git a/.env.example b/.env.example index ec5fa1f..9b13a17 100644 --- a/.env.example +++ b/.env.example @@ -26,7 +26,7 @@ VIETQR_ENABLED=true # Storage STORAGE_PROVIDER=supabase # supabase | memory SUPABASE_URL=https://xyz.supabase.co -SUPABASE_ANON_KEY=... +SUPABASE_SERVICE_ROLE_KEY=server-only-secret # Rate limit guards (optional overrides) MAX_CONCURRENT_RESEARCH=1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b43b8af..5c3f7a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,5 +34,13 @@ jobs: - name: Test run: npm test + - name: Database integration tests + run: | + npx supabase start + eval "$(npx supabase status -o env)" + SUPABASE_TEST_URL="$API_URL" SUPABASE_TEST_SERVICE_ROLE_KEY="$SERVICE_ROLE_KEY" SUPABASE_TEST_ANON_KEY="$ANON_KEY" npm run test:db + npx supabase stop --no-backup + - name: Build run: npm run build + diff --git a/package.json b/package.json index b6bf049..352bacc 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "next start", "lint": "eslint", "test": "vitest run", + "test:db": "vitest run tests/integration/supabase-cache-concurrency.test.ts", "typecheck": "next typegen && tsc --noEmit", "typecheck:legacy": "next typegen && tsc6 --noEmit", "postinstall": "node -e \"const fs=require('fs'),p='node_modules/typescript/package.json';if(fs.existsSync(p)){const j=JSON.parse(fs.readFileSync(p,'utf8'));if(!j.bin||!j.bin.tsc){j.bin=j.bin||{};j.bin.tsc='./bin/tsc';fs.writeFileSync(p,JSON.stringify(j,null,2));const b='node_modules/typescript/bin/tsc';if(!fs.existsSync(b)){fs.writeFileSync(b,'#!/usr/bin/env node\\nrequire(\\'../../@typescript/native/bin/tsc\\');\\n',{mode:0o755});}}}\"" @@ -37,6 +38,7 @@ "@typescript/native": "npm:typescript@7.0.2", "eslint": "^9", "eslint-config-next": "16.3.2", + "supabase": "2.115.0", "tailwindcss": "^4", "typescript": "npm:@typescript/typescript6@6.0.2", "vitest": "^4.1.11" diff --git a/src/adapters/storage/memory.ts b/src/adapters/storage/memory.ts index 771d797..7c2615a 100644 --- a/src/adapters/storage/memory.ts +++ b/src/adapters/storage/memory.ts @@ -2,7 +2,16 @@ // In-Memory Storage Adapter — for development & testing // ═══════════════════════════════════════════════════════ -import type { CompanyProfile, ProfileDiff } from "@/lib/types"; +import type { + CompanyProfile, + ProfileDiff, + ResearchSnapshot, +} from "@/lib/types"; +import { + type IdentityCandidate, + type NormalizedCompanyIdentity, + IdentityConflictError, +} from "@/modules/cache"; import type { StorageAdapter, StorageReadOptions, @@ -10,9 +19,10 @@ import type { } from "./types"; export class MemoryStorageAdapter implements StorageAdapter { - // companyId → version → profile + private identities: Map = new Map(); private profiles: Map> = new Map(); - // companyId → diffs + private reports: Map> = new Map(); + private timestamps: Map> = new Map(); private diffs: Map = new Map(); async saveProfile( @@ -23,7 +33,7 @@ export class MemoryStorageAdapter implements StorageAdapter { if (!this.profiles.has(profile.id)) { this.profiles.set(profile.id, new Map()); } - this.profiles.get(profile.id)!.set(profile.version, profile); + this.profiles.get(profile.id)!.set(profile.version, structuredClone(profile)); } async getProfile( @@ -34,10 +44,10 @@ export class MemoryStorageAdapter implements StorageAdapter { if (!versions) return null; if (version !== undefined) { - return versions.get(version) ?? null; + const p = versions.get(version); + return p ? structuredClone(p) : null; } - // Return latest return this.getLatestProfile(companyId); } @@ -50,7 +60,8 @@ export class MemoryStorageAdapter implements StorageAdapter { if (!versions || versions.size === 0) return null; const maxVersion = Math.max(...versions.keys()); - return versions.get(maxVersion) ?? null; + const p = versions.get(maxVersion); + return p ? structuredClone(p) : null; } async listProfiles(): Promise { @@ -58,7 +69,7 @@ export class MemoryStorageAdapter implements StorageAdapter { for (const versions of this.profiles.values()) { const maxVersion = Math.max(...versions.keys()); const latest = versions.get(maxVersion); - if (latest) result.push(latest); + if (latest) result.push(structuredClone(latest)); } return result; } @@ -71,16 +82,210 @@ export class MemoryStorageAdapter implements StorageAdapter { if (!this.diffs.has(diff.companyId)) { this.diffs.set(diff.companyId, []); } - this.diffs.get(diff.companyId)!.push(diff); + this.diffs.get(diff.companyId)!.push(structuredClone(diff)); } async getDiffs(companyId: string): Promise { - return this.diffs.get(companyId) ?? []; + const d = this.diffs.get(companyId) ?? []; + return structuredClone(d); + } + + async findIdentityCandidates( + identity: NormalizedCompanyIdentity, + options?: StorageReadOptions, + ): Promise { + options?.signal?.throwIfAborted(); + const result: IdentityCandidate[] = []; + for (const cand of this.identities.values()) { + const matchTax = identity.taxId !== null && cand.taxId === identity.taxId; + const matchDomain = identity.domain !== null && cand.domain === identity.domain; + const matchName = cand.name === identity.name; + if (matchTax || matchDomain || matchName) { + result.push(structuredClone(cand)); + } + } + return result.sort((a, b) => a.companyId.localeCompare(b.companyId)); + } + + async getLatestCompleteSnapshot( + companyId: string, + options?: StorageReadOptions, + ): Promise { + options?.signal?.throwIfAborted(); + const profileMap = this.profiles.get(companyId); + const reportMap = this.reports.get(companyId); + if (!profileMap || !reportMap) return null; + + const completeVersions = Array.from(profileMap.keys()) + .filter((v) => reportMap.has(v)) + .sort((a, b) => b - a); + + if (completeVersions.length === 0) return null; + const latestVersion = completeVersions[0]; + const profile = structuredClone(profileMap.get(latestVersion)!); + const report = structuredClone(reportMap.get(latestVersion)!); + const companyDiffs = this.diffs.get(companyId) ?? []; + const diff = companyDiffs.find((d) => d.toVersion === latestVersion) ?? null; + const lastSyncedAt = + this.timestamps.get(companyId)?.get(latestVersion) ?? + profile.lastUpdated.toISOString(); + + return { + profile, + report, + diff: diff ? structuredClone(diff) : null, + lastSyncedAt, + }; + } + + async resolveOrCreateIdentity( + identity: NormalizedCompanyIdentity, + candidateId: string, + options?: StorageWriteOptions, + ): Promise { + options?.signal?.throwIfAborted(); + // 1. If taxId provided + if (identity.taxId) { + let taxOwnerId: string | null = null; + for (const cand of this.identities.values()) { + if (cand.taxId === identity.taxId) { + taxOwnerId = cand.companyId; + break; + } + } + + if (taxOwnerId && identity.domain) { + const domainMatches = Array.from(this.identities.values()).filter( + (c) => c.domain === identity.domain + ); + if ( + domainMatches.length > 0 && + !domainMatches.some((c) => c.companyId === taxOwnerId) + ) { + throw new IdentityConflictError(); + } + } + + if (taxOwnerId) { + return taxOwnerId; + } + + this.identities.set(candidateId, { + companyId: candidateId, + taxId: identity.taxId, + domain: identity.domain, + name: identity.name, + }); + return candidateId; + } + + // 2. If domain provided + if (identity.domain) { + for (const cand of this.identities.values()) { + if (cand.domain === identity.domain && cand.name === identity.name) { + return cand.companyId; + } + } + + this.identities.set(candidateId, { + companyId: candidateId, + taxId: null, + domain: identity.domain, + name: identity.name, + }); + return candidateId; + } + + // 3. Name only + this.identities.set(candidateId, { + companyId: candidateId, + taxId: null, + domain: null, + name: identity.name, + }); + return candidateId; + } + + async persistResearchSnapshot( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions, + ): Promise { + options?.signal?.throwIfAborted(); + const companyId = snapshot.profile.id; + + // Check conflict + if (identity.taxId) { + for (const cand of this.identities.values()) { + if (cand.taxId === identity.taxId && cand.companyId !== companyId) { + throw new IdentityConflictError(); + } + } + } + + // Update / insert identity + const existingIdentity = this.identities.get(companyId); + this.identities.set(companyId, { + companyId, + taxId: identity.taxId ?? existingIdentity?.taxId ?? null, + domain: identity.domain ?? existingIdentity?.domain ?? null, + name: identity.name || existingIdentity?.name || "", + }); + + const nowIso = new Date().toISOString(); + const version = snapshot.profile.version; + + // Save profile + if (!this.profiles.has(companyId)) { + this.profiles.set(companyId, new Map()); + } + const profileToSave = { + ...snapshot.profile, + lastUpdated: new Date(nowIso), + }; + this.profiles.get(companyId)!.set(version, structuredClone(profileToSave)); + + // Save report + if (!this.reports.has(companyId)) { + this.reports.set(companyId, new Map()); + } + this.reports.get(companyId)!.set(version, structuredClone(snapshot.report)); + + // Save timestamp + if (!this.timestamps.has(companyId)) { + this.timestamps.set(companyId, new Map()); + } + this.timestamps.get(companyId)!.set(version, nowIso); + + // Save diff + if (snapshot.diff) { + if (!this.diffs.has(companyId)) { + this.diffs.set(companyId, []); + } + const existingDiffIndex = this.diffs + .get(companyId)! + .findIndex((d) => d.toVersion === version); + if (existingDiffIndex >= 0) { + this.diffs.get(companyId)![existingDiffIndex] = structuredClone(snapshot.diff); + } else { + this.diffs.get(companyId)!.push(structuredClone(snapshot.diff)); + } + } + + return { + profile: structuredClone(profileToSave), + report: structuredClone(snapshot.report), + diff: snapshot.diff ? structuredClone(snapshot.diff) : null, + lastSyncedAt: nowIso, + }; } // Test helpers clear(): void { + this.identities.clear(); this.profiles.clear(); + this.reports.clear(); + this.timestamps.clear(); this.diffs.clear(); } diff --git a/src/adapters/storage/supabase.ts b/src/adapters/storage/supabase.ts index 4576fe2..e154e16 100644 --- a/src/adapters/storage/supabase.ts +++ b/src/adapters/storage/supabase.ts @@ -1,16 +1,21 @@ // ═══════════════════════════════════════════════════════ // Supabase PostgreSQL Storage Adapter -// Implements JSONB multi-version storage for CompanyProfile & ProfileDiff +// Implements JSONB multi-version storage for CompanyProfile, ProfileDiff, and ResearchSnapshot // ═══════════════════════════════════════════════════════ -// Ensure WebSocket constructor exists in Node.js runtimes < 22 for @supabase/realtime-js -if (typeof globalThis !== "undefined" && typeof globalThis.WebSocket === "undefined") { - // @ts-expect-error fallback mock for RealtimeClient in REST-only mode - globalThis.WebSocket = class WebSocket {}; -} - -import { createClient, SupabaseClient } from "@supabase/supabase-js"; -import type { CompanyProfile, ProfileDiff } from "@/lib/types"; +import { createClient, type SupabaseClient } from "@supabase/supabase-js"; +import { + type CompanyProfile, + type ProfileDiff, + type ResearchSnapshot, + ResearchSnapshotSchema, +} from "@/lib/types"; +import { + type IdentityCandidate, + type NormalizedCompanyIdentity, + IdentityConflictError, + CacheInvalidError, +} from "@/modules/cache"; import type { StorageAdapter, StorageReadOptions, @@ -22,10 +27,10 @@ export class SupabaseStorageAdapter implements StorageAdapter { constructor(supabaseUrl?: string, supabaseKey?: string) { const url = supabaseUrl || process.env.SUPABASE_URL; - const key = supabaseKey || process.env.SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY; + const key = supabaseKey || process.env.SUPABASE_SERVICE_ROLE_KEY; if (!url || !key) { - throw new Error("Missing Supabase credentials: SUPABASE_URL or SUPABASE_ANON_KEY"); + throw new Error("Missing Supabase credentials: SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY"); } this.client = createClient(url, key, { @@ -103,7 +108,6 @@ export class SupabaseStorageAdapter implements StorageAdapter { } async listProfiles(): Promise { - // Get unique latest version per company const { data, error } = await this.client .from("company_profiles") .select("data") @@ -116,7 +120,6 @@ export class SupabaseStorageAdapter implements StorageAdapter { if (!data) return []; - // Deduplicate to keep latest version per company id const seen = new Set(); const profiles: CompanyProfile[] = []; @@ -170,4 +173,146 @@ export class SupabaseStorageAdapter implements StorageAdapter { return (data ?? []).map((row) => row.data as ProfileDiff); } + + // ─── Cache & Snapshot RPCs ─── + + async findIdentityCandidates( + identity: NormalizedCompanyIdentity, + options?: StorageReadOptions, + ): Promise { + const query = this.client.rpc("lookup_company_identities", { + p_tax_id: identity.taxId, + p_domain: identity.domain, + p_name: identity.name, + }); + if (options?.signal) query.abortSignal(options.signal); + + const { data, error } = await query; + if (error) { + throw new Error(`Failed to lookup company identities: ${error.message}`); + } + + if (!Array.isArray(data)) return []; + + return data.map((row: { id: string; tax_id: string | null; normalized_domain: string | null; normalized_name: string }) => ({ + companyId: row.id, + taxId: row.tax_id, + domain: row.normalized_domain, + name: row.normalized_name, + })); + } + + async getLatestCompleteSnapshot( + companyId: string, + options?: StorageReadOptions, + ): Promise { + const profileQuery = this.client + .from("company_profiles") + .select("version, data, analysis_report, updated_at") + .eq("id", companyId) + .not("analysis_report", "is", null) + .order("version", { ascending: false }) + .limit(1); + + if (options?.signal) profileQuery.abortSignal(options.signal); + + const { data: profileRows, error: profileError } = await profileQuery; + if (profileError) { + throw new Error(`Failed to get complete profile: ${profileError.message}`); + } + + if (!profileRows || profileRows.length === 0) return null; + + const row = profileRows[0]; + const version = row.version; + + const diffQuery = this.client + .from("company_diffs") + .select("data") + .eq("company_id", companyId) + .eq("to_version", version); + + if (options?.signal) diffQuery.abortSignal(options.signal); + + const { data: diffRow, error: diffError } = await diffQuery.maybeSingle(); + if (diffError) { + throw new Error(`Failed to get snapshot diff: ${diffError.message}`); + } + + const rawSnapshot = { + profile: row.data, + report: row.analysis_report, + diff: diffRow ? diffRow.data : null, + lastSyncedAt: typeof row.updated_at === "string" ? row.updated_at : new Date(row.updated_at).toISOString(), + }; + + const parsed = ResearchSnapshotSchema.safeParse(rawSnapshot); + if (!parsed.success) { + throw new CacheInvalidError( + `Cached snapshot for company ${companyId} failed validation: ${parsed.error.message}` + ); + } + + return parsed.data; + } + + async resolveOrCreateIdentity( + identity: NormalizedCompanyIdentity, + candidateId: string, + options?: StorageWriteOptions, + ): Promise { + const query = this.client.rpc("resolve_company_identity", { + p_tax_id: identity.taxId, + p_domain: identity.domain, + p_name: identity.name, + p_candidate_id: candidateId, + }); + if (options?.signal) query.abortSignal(options.signal); + + const { data, error } = await query; + if (error) { + if (error.message.includes("identity_conflict")) { + throw new IdentityConflictError(); + } + throw new Error(`Failed to resolve company identity: ${error.message}`); + } + + return data as string; + } + + async persistResearchSnapshot( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions, + ): Promise { + const query = this.client.rpc("persist_research_snapshot", { + p_company_id: snapshot.profile.id, + p_tax_id: identity.taxId, + p_domain: identity.domain, + p_name: identity.name, + p_version: snapshot.profile.version, + p_profile_data: snapshot.profile, + p_analysis_report: snapshot.report, + p_diff_data: snapshot.diff, + }); + if (options?.signal) query.abortSignal(options.signal); + + const { data, error } = await query; + if (error) { + if (error.message.includes("identity_conflict")) { + throw new IdentityConflictError(); + } + throw new Error(`Failed to persist research snapshot: ${error.message}`); + } + + const lastSyncedAt = + typeof data === "string" ? data : new Date(data).toISOString(); + + return { + profile: snapshot.profile, + report: snapshot.report, + diff: snapshot.diff, + lastSyncedAt, + }; + } } diff --git a/src/adapters/storage/types.ts b/src/adapters/storage/types.ts index 4c81d61..9cdf68c 100644 --- a/src/adapters/storage/types.ts +++ b/src/adapters/storage/types.ts @@ -1,8 +1,12 @@ -// ═══════════════════════════════════════════════════════ -// Storage Adapter — Interface -// ═══════════════════════════════════════════════════════ - -import type { CompanyProfile, ProfileDiff } from "@/lib/types"; +import type { + CompanyProfile, + ProfileDiff, + ResearchSnapshot, +} from "@/lib/types"; +import type { + NormalizedCompanyIdentity, + IdentityCandidate, +} from "@/modules/cache"; export interface StorageWriteOptions { signal?: AbortSignal; @@ -28,4 +32,28 @@ export interface StorageAdapter { listProfiles(): Promise; saveDiff(diff: ProfileDiff, options?: StorageWriteOptions): Promise; getDiffs(companyId: string): Promise; + + // Cache and complete snapshot methods + findIdentityCandidates( + identity: NormalizedCompanyIdentity, + options?: StorageReadOptions, + ): Promise; + + getLatestCompleteSnapshot( + companyId: string, + options?: StorageReadOptions, + ): Promise; + + resolveOrCreateIdentity( + identity: NormalizedCompanyIdentity, + candidateId: string, + options?: StorageWriteOptions, + ): Promise; + + persistResearchSnapshot( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions, + ): Promise; } + diff --git a/src/config/index.ts b/src/config/index.ts index f05e1fa..838737d 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -191,7 +191,7 @@ export function createStorageAdapter(): StorageAdapter { case "supabase": _storage = new SupabaseStorageAdapter( process.env.SUPABASE_URL, - process.env.SUPABASE_ANON_KEY + process.env.SUPABASE_SERVICE_ROLE_KEY ); break; default: diff --git a/src/modules/cache/index.ts b/src/modules/cache/index.ts index a0e1805..8848c3c 100644 --- a/src/modules/cache/index.ts +++ b/src/modules/cache/index.ts @@ -21,6 +21,38 @@ export type CacheDecision = | { kind: "miss" } | { kind: "conflict"; taxCompanyId: string; domainCompanyIds: string[] }; +export class IdentityConflictError extends Error { + readonly code = "identity_conflict"; + constructor(message = "Thông tin định danh công ty mâu thuẫn.") { + super(message); + this.name = "IdentityConflictError"; + } +} + +export class CacheInvalidError extends Error { + readonly code = "cache_invalid"; + constructor(message = "Dữ liệu cache không hợp lệ.") { + super(message); + this.name = "CacheInvalidError"; + } +} + +export class InvalidCacheSelectionError extends Error { + readonly code = "invalid_cache_selection"; + constructor(message = "Lựa chọn cache không hợp lệ với dữ liệu nhập.") { + super(message); + this.name = "InvalidCacheSelectionError"; + } +} + +export class CacheUnavailableError extends Error { + readonly code = "cache_unavailable"; + constructor(message = "Dịch vụ cache tạm thời không khả dụng.") { + super(message); + this.name = "CacheUnavailableError"; + } +} + export function normalizeTaxId(value?: string): string | null { if (!value) return null; const normalized = value.trim().replace(/[\s.-]/g, ""); diff --git a/supabase/config.toml b/supabase/config.toml new file mode 100644 index 0000000..6880acd --- /dev/null +++ b/supabase/config.toml @@ -0,0 +1,414 @@ +# For detailed configuration reference documentation, visit: +# https://supabase.com/docs/guides/local-development/cli/config +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "TechBridgeAI" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` and `graphql_public` schemas are included by default. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 +# Controls whether new tables, views, sequences and functions created in the `public` schema by +# `postgres` are reachable through the Data API roles (`anon`, `authenticated`, `service_role`) +# without explicit GRANTs. When unset, new entities are NOT auto-exposed, matching the new cloud +# default. Set to `true` to keep the legacy behaviour of auto-exposing new entities; this is +# deprecated and the field is removed on 2026-10-30 once the always-revoked behaviour is permanent. +# auto_expose_new_tables = true + +[api.tls] +# Enable HTTPS endpoints locally using a self-signed certificate. +enabled = false +# Paths to self-signed certificate pair. +# cert_path = "../certs/my-cert.pem" +# key_path = "../certs/my-key.pem" + +[db] +# Port to use for the local database URL. +port = 54322 +# Port used by db diff command to initialize the shadow database. +shadow_port = 54320 +# Maximum amount of time to wait for health check when starting the local database. +health_timeout = "2m" +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 17 + +[db.pooler] +enabled = false +# Port to use for the local connection pooler. +port = 54329 +# Specifies when a server connection can be reused by other clients. +# Configure one of the supported pooler modes: `transaction`, `session`. +pool_mode = "transaction" +# How many server connections to allow per user/database pair. +default_pool_size = 20 +# Maximum number of client connections allowed. +max_client_conn = 100 + +# [db.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.migrations] +# If disabled, migrations will be skipped during a db push or reset. +enabled = true +# Specifies an ordered list of schema files, directories, or glob patterns that describe your database. +# Supports paths relative to supabase directory: "./schemas/*.sql", "./database". +schema_paths = [] + +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory: "./seeds/*.sql" +sql_paths = ["./seed.sql"] + +[db.network_restrictions] +# Enable management of network restrictions. +enabled = false +# List of IPv4 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv4 connections. Set empty array to block all IPs. +allowed_cidrs = ["0.0.0.0/0"] +# List of IPv6 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv6 connections. Set empty array to block all IPs. +allowed_cidrs_v6 = ["::/0"] + +# Uncomment to reject non-secure connections to the database. +# [db.ssl_enforcement] +# enabled = true + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[local_smtp] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" + +# Allow connections via S3 compatible clients +[storage.s3_protocol] +enabled = true + +# Image transformation API is available to Supabase Pro plan. +# [storage.image_transformation] +# enabled = true + +# Store analytical data in S3 for running ETL jobs over Iceberg Catalog +# This feature is only available on the hosted platform. +[storage.analytics] +enabled = false +max_namespaces = 5 +max_tables = 10 +max_catalogs = 2 + +# Analytics Buckets is available to Supabase Pro plan. +# [storage.analytics.buckets.my-warehouse] + +# Store vector embeddings in S3 for large and durable datasets +[storage.vector] +enabled = true +max_buckets = 10 +max_indexes = 5 + +# Vector Buckets is available to Supabase Pro plan. +# [storage.vector.buckets.documents-openai] + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# The public URL that Auth serves on. Defaults to the API external URL with `/auth/v1` appended. +# external_url = "" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# JWT issuer URL. If not set, defaults to auth.external_url. +# jwt_issuer = "" +# Path to JWT signing key. DO NOT commit your signing keys file to git. +# signing_keys_path = "./signing_keys.json" +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false +# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. +minimum_password_length = 6 +# Passwords that do not meet the following requirements will be rejected as weak. Supported values +# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` +password_requirements = "" + +# Configure passkey sign-ins. +# [auth.passkey] +# enabled = false + +# Configure WebAuthn relying party settings (required when passkey is enabled). +# [auth.webauthn] +# rp_display_name = "Supabase" +# rp_id = "localhost" +# rp_origins = ["http://127.0.0.1:3000"] + +[auth.rate_limit] +# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. +email_sent = 2 +# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. +sms_sent = 30 +# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. +anonymous_users = 30 +# Number of sessions that can be refreshed in a 5 minute interval per IP address. +token_refresh = 150 +# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). +sign_in_sign_ups = 30 +# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. +token_verifications = 30 +# Number of Web3 logins that can be made in a 5 minute interval per IP address. +web3 = 30 + +# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. +# [auth.captcha] +# enabled = true +# provider = "hcaptcha" +# secret = "" + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false +# If enabled, users will need to reauthenticate or have logged in recently to change their password. +secure_password_change = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" +# Number of characters used in the email OTP. +otp_length = 6 +# Number of seconds before the email OTP expires (defaults to 1 hour). +otp_expiry = 3600 + +# Use a production-ready SMTP server +# [auth.email.smtp] +# enabled = true +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" + +# Uncomment to customize email template +# [auth.email.template.invite] +# subject = "You have been invited" +# content_path = "./supabase/templates/invite.html" + +# Uncomment to customize notification email template +# [auth.email.notification.password_changed] +# enabled = true +# subject = "Your password has been changed" +# content_path = "./supabase/templates/password_changed_notification.html" + +[auth.sms] +# Allow/disallow new user signups via SMS to your project. +enable_signup = false +# If enabled, users need to confirm their phone number before signing in. +enable_confirmations = false +# Template for sending OTP to users +template = "Your code is {{ .Code }}" +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" + +# Use pre-defined map of phone number to OTP for testing. +# [auth.sms.test_otp] +# 4152127777 = "123456" + +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + +# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object. +# [auth.hook.before_user_created] +# enabled = true +# uri = "pg-functions://postgres/auth/before-user-created-hook" + +# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. +# [auth.hook.custom_access_token] +# enabled = true +# uri = "pg-functions:////" + +# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. +[auth.sms.twilio] +enabled = false +account_sid = "" +message_service_sid = "" +# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: +auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" + +# Multi-factor-authentication is available to Supabase Pro plan. +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = false +verify_enabled = false + +# Configure MFA via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ .Code }}" +max_frequency = "5s" + +# Configure MFA via WebAuthn +# [auth.mfa.web_authn] +# enroll_enabled = true +# verify_enabled = true + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, +# `twitter`, `x`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth callback URL derived from auth.external_url. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false +# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address. +email_optional = false + +# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. +# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. +[auth.web3.solana] +enabled = false + +# Use Firebase Auth as a third-party provider alongside Supabase Auth. +[auth.third_party.firebase] +enabled = false +# project_id = "my-firebase-project" + +# Use Auth0 as a third-party provider alongside Supabase Auth. +[auth.third_party.auth0] +enabled = false +# tenant = "my-auth0-tenant" +# tenant_region = "us" + +# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. +[auth.third_party.aws_cognito] +enabled = false +# user_pool_id = "my-user-pool-id" +# user_pool_region = "us-east-1" + +# Use Clerk as a third-party provider alongside Supabase Auth. +[auth.third_party.clerk] +enabled = false +# Obtain from https://clerk.com/setup/supabase +# domain = "example.clerk.accounts.dev" + +# OAuth server configuration +[auth.oauth_server] +# Enable OAuth server functionality +enabled = false +# Path for OAuth consent flow UI +authorization_url_path = "/oauth/consent" +# Allow dynamic client registration +allow_dynamic_registration = false + +[edge_runtime] +enabled = true +# Supported request policies: `oneshot`, `per_worker`. +# `per_worker` (default) — enables hot reload during local development. +# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks). +policy = "per_worker" +# Port to attach the Chrome inspector for debugging edge functions. +inspector_port = 8083 +# The Deno major version to use. +deno_version = 2 + +# [edge_runtime.secrets] +# secret_key = "env(SECRET_VALUE)" + +[analytics] +enabled = true +port = 54327 +# Configure one of the supported backends: `postgres`, `bigquery`. +backend = "postgres" + +# Experimental features may be deprecated any time +[experimental] +# Configures Postgres storage engine to use OrioleDB (S3) +orioledb_version = "" +# Configures S3 bucket URL, eg. .s3-.amazonaws.com +s3_host = "env(S3_HOST)" +# Configures S3 bucket region, eg. us-east-1 +s3_region = "env(S3_REGION)" +# Configures AWS_ACCESS_KEY_ID for S3 bucket +s3_access_key = "env(S3_ACCESS_KEY)" +# Configures AWS_SECRET_ACCESS_KEY for S3 bucket +s3_secret_key = "env(S3_SECRET_KEY)" + +# pg-delta is the schema diff engine for db diff / db pull / db remote commit. +# Set enabled = false to fall back to the legacy migra engine. +[experimental.pgdelta] +enabled = true +# Directory under `supabase/` where declarative files are written. +# declarative_schema_path = "./schemas" +# JSON string passed through to pg-delta SQL formatting. +# format_options = "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":80,\"commaStyle\":\"trailing\"}" diff --git a/supabase/migrations/20260826074659_research_cache.sql b/supabase/migrations/20260826074659_research_cache.sql new file mode 100644 index 0000000..7df2c7e --- /dev/null +++ b/supabase/migrations/20260826074659_research_cache.sql @@ -0,0 +1,316 @@ +-- ═══════════════════════════════════════════════════════ +-- PartnerIQ — Research Cache Migration +-- ═══════════════════════════════════════════════════════ + +-- 1. Table for Canonical Company Identities +CREATE TABLE IF NOT EXISTS public.company_identities ( + id TEXT PRIMARY KEY, + tax_id TEXT, + normalized_domain TEXT, + normalized_name TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT TIMEZONE('utc'::text, NOW()), + updated_at TIMESTAMPTZ NOT NULL DEFAULT TIMEZONE('utc'::text, NOW()) +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_company_identities_tax_id + ON public.company_identities (tax_id) + WHERE tax_id IS NOT NULL; +CREATE INDEX IF NOT EXISTS idx_company_identities_domain + ON public.company_identities (normalized_domain); +CREATE INDEX IF NOT EXISTS idx_company_identities_name + ON public.company_identities (normalized_name); + +-- 2. Add analysis_report column and complete profile index +ALTER TABLE public.company_profiles + ADD COLUMN IF NOT EXISTS analysis_report JSONB; + +CREATE INDEX IF NOT EXISTS idx_company_profiles_complete + ON public.company_profiles (id, version DESC) + WHERE analysis_report IS NOT NULL; + +-- 3. Backfill identities from existing distinct company_profiles if any +INSERT INTO public.company_identities (id, tax_id, normalized_domain, normalized_name, created_at, updated_at) +SELECT DISTINCT ON (cp.id) + cp.id, + CASE + WHEN (cp.data->>'taxId') ~ '^\d{10}(\d{3})?$' THEN cp.data->>'taxId' + ELSE NULL + END AS tax_id, + CASE + WHEN cp.data->>'website' IS NOT NULL AND cp.data->>'website' <> '' THEN + regexp_replace( + regexp_replace( + lower(split_part(split_part(split_part(cp.data->>'website', '://', 2), '/', 1), ':', 1)), + '\.$', '' + ), + '^www\.', '' + ) + ELSE NULL + END AS normalized_domain, + lower(trim(regexp_replace(cp.official_name, '\s+', ' ', 'g'))) AS normalized_name, + cp.created_at, + cp.updated_at +FROM public.company_profiles cp +ORDER BY cp.id, cp.version DESC +ON CONFLICT (id) DO NOTHING; + +-- 4. Add foreign key constraints +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint WHERE conname = 'company_profiles_identity_fk' + ) THEN + ALTER TABLE public.company_profiles + ADD CONSTRAINT company_profiles_identity_fk + FOREIGN KEY (id) REFERENCES public.company_identities(id); + END IF; + + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint WHERE conname = 'company_diffs_identity_fk' + ) THEN + ALTER TABLE public.company_diffs + ADD CONSTRAINT company_diffs_identity_fk + FOREIGN KEY (company_id) REFERENCES public.company_identities(id); + END IF; +END $$; + +-- 5. RLS & Server-Only Privileges +DROP POLICY IF EXISTS "Allow anon read/write company_profiles" ON public.company_profiles; +DROP POLICY IF EXISTS "Allow anon read/write company_diffs" ON public.company_diffs; + +ALTER TABLE public.company_identities ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.company_diffs ENABLE ROW LEVEL SECURITY; + +REVOKE ALL ON public.company_identities FROM anon, authenticated; +REVOKE ALL ON public.company_profiles FROM anon, authenticated; +REVOKE ALL ON public.company_diffs FROM anon, authenticated; + +GRANT SELECT, INSERT, UPDATE ON public.company_identities TO service_role; +GRANT SELECT, INSERT, UPDATE ON public.company_profiles TO service_role; +GRANT SELECT, INSERT, UPDATE ON public.company_diffs TO service_role; + +-- 6. Read-only Lookup RPC +CREATE OR REPLACE FUNCTION public.lookup_company_identities( + p_tax_id text, + p_domain text, + p_name text +) +RETURNS TABLE ( + id text, + tax_id text, + normalized_domain text, + normalized_name text, + created_at timestamptz, + updated_at timestamptz +) +LANGUAGE sql +STABLE +SECURITY INVOKER +SET search_path = '' +AS $$ + SELECT DISTINCT + ci.id, + ci.tax_id, + ci.normalized_domain, + ci.normalized_name, + ci.created_at, + ci.updated_at + FROM public.company_identities ci + WHERE (p_tax_id IS NOT NULL AND ci.tax_id = p_tax_id) + OR (p_domain IS NOT NULL AND ci.normalized_domain = p_domain) + OR (p_name IS NOT NULL AND ci.normalized_name = p_name) + ORDER BY ci.id; +$$; + +REVOKE EXECUTE ON FUNCTION public.lookup_company_identities(text, text, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.lookup_company_identities(text, text, text) TO service_role; + +-- 7. Transactional Resolve/Create Identity RPC +CREATE OR REPLACE FUNCTION public.resolve_company_identity( + p_tax_id text, + p_domain text, + p_name text, + p_candidate_id text +) +RETURNS text +LANGUAGE plpgsql +SECURITY INVOKER +SET search_path = '' +AS $$ +DECLARE + resolved_id text; + tax_owner_id text; +BEGIN + -- Detect conflict between tax ID and domain if both provided + IF p_tax_id IS NOT NULL AND p_domain IS NOT NULL THEN + SELECT id INTO tax_owner_id + FROM public.company_identities + WHERE tax_id = p_tax_id; + + IF tax_owner_id IS NOT NULL THEN + IF EXISTS ( + SELECT 1 FROM public.company_identities + WHERE normalized_domain = p_domain + AND id <> tax_owner_id + ) AND NOT EXISTS ( + SELECT 1 FROM public.company_identities + WHERE normalized_domain = p_domain + AND id = tax_owner_id + ) THEN + RAISE EXCEPTION 'identity_conflict'; + END IF; + END IF; + END IF; + + IF p_tax_id IS NOT NULL THEN + INSERT INTO public.company_identities ( + id, tax_id, normalized_domain, normalized_name + ) VALUES ( + p_candidate_id, p_tax_id, p_domain, p_name + ) ON CONFLICT (tax_id) WHERE tax_id IS NOT NULL DO NOTHING; + + SELECT id INTO resolved_id + FROM public.company_identities + WHERE tax_id = p_tax_id; + ELSIF p_domain IS NOT NULL THEN + PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtext(p_domain)); + + SELECT id INTO resolved_id + FROM public.company_identities + WHERE normalized_domain = p_domain + AND normalized_name = p_name + ORDER BY id + LIMIT 1; + + IF resolved_id IS NULL THEN + INSERT INTO public.company_identities ( + id, normalized_domain, normalized_name + ) VALUES ( + p_candidate_id, p_domain, p_name + ) RETURNING id INTO resolved_id; + END IF; + ELSE + INSERT INTO public.company_identities (id, normalized_name) + VALUES (p_candidate_id, p_name) + RETURNING id INTO resolved_id; + END IF; + + RETURN resolved_id; +END; +$$; + +REVOKE EXECUTE ON FUNCTION public.resolve_company_identity(text, text, text, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.resolve_company_identity(text, text, text, text) TO service_role; + +-- 8. Atomic Persist Research Snapshot RPC +CREATE OR REPLACE FUNCTION public.persist_research_snapshot( + p_company_id text, + p_tax_id text, + p_domain text, + p_name text, + p_version integer, + p_profile_data jsonb, + p_analysis_report jsonb, + p_diff_data jsonb +) +RETURNS timestamptz +LANGUAGE plpgsql +SECURITY INVOKER +SET search_path = '' +AS $$ +DECLARE + v_now timestamptz := timezone('utc'::text, now()); + v_official_name text; + v_diff_id text; + v_from_version integer; + v_to_version integer; +BEGIN + -- 1. Lock the target company_identities row + PERFORM 1 + FROM public.company_identities + WHERE id = p_company_id + FOR UPDATE; + + IF NOT FOUND THEN + RAISE EXCEPTION 'identity_not_found'; + END IF; + + -- 2. Recheck tax_id conflict against other identities + IF p_tax_id IS NOT NULL THEN + IF EXISTS ( + SELECT 1 FROM public.company_identities + WHERE tax_id = p_tax_id AND id <> p_company_id + ) THEN + RAISE EXCEPTION 'identity_conflict'; + END IF; + END IF; + + -- 3. Update target identity metadata + UPDATE public.company_identities + SET + tax_id = COALESCE(p_tax_id, tax_id), + normalized_domain = COALESCE(p_domain, normalized_domain), + normalized_name = COALESCE(p_name, normalized_name), + updated_at = v_now + WHERE id = p_company_id; + + v_official_name := COALESCE(p_profile_data->>'officialName', p_name); + + -- 4. Upsert company_profiles + INSERT INTO public.company_profiles ( + id, + version, + official_name, + data, + analysis_report, + created_at, + updated_at + ) VALUES ( + p_company_id, + p_version, + v_official_name, + p_profile_data, + p_analysis_report, + v_now, + v_now + ) + ON CONFLICT (id, version) DO UPDATE SET + official_name = EXCLUDED.official_name, + data = EXCLUDED.data, + analysis_report = EXCLUDED.analysis_report, + updated_at = EXCLUDED.updated_at; + + -- 5. Upsert diff if provided + IF p_diff_data IS NOT NULL THEN + v_diff_id := COALESCE(p_diff_data->>'id', p_company_id || '-v' || p_version); + v_from_version := (p_diff_data->>'fromVersion')::integer; + v_to_version := (p_diff_data->>'toVersion')::integer; + + INSERT INTO public.company_diffs ( + id, + company_id, + from_version, + to_version, + data, + created_at + ) VALUES ( + v_diff_id, + p_company_id, + v_from_version, + v_to_version, + p_diff_data, + v_now + ) + ON CONFLICT (id) DO UPDATE SET + from_version = EXCLUDED.from_version, + to_version = EXCLUDED.to_version, + data = EXCLUDED.data; + END IF; + + RETURN v_now; +END; +$$; + +REVOKE EXECUTE ON FUNCTION public.persist_research_snapshot(text, text, text, text, integer, jsonb, jsonb, jsonb) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.persist_research_snapshot(text, text, text, text, integer, jsonb, jsonb, jsonb) TO service_role; diff --git a/supabase/schema.sql b/supabase/schema.sql index ef10ff3..dc5f2d0 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -1,54 +1,296 @@ -- ═══════════════════════════════════════════════════════ --- PartnerIQ — Supabase PostgreSQL Schema --- Run this script in the Supabase SQL Editor +-- PartnerIQ — Supabase PostgreSQL Canonical Schema -- ═══════════════════════════════════════════════════════ --- 1. Table for Company Profiles (Multi-versioning) +-- 1. Table for Canonical Company Identities +CREATE TABLE IF NOT EXISTS public.company_identities ( + id TEXT PRIMARY KEY, + tax_id TEXT, + normalized_domain TEXT, + normalized_name TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT TIMEZONE('utc'::text, NOW()), + updated_at TIMESTAMPTZ NOT NULL DEFAULT TIMEZONE('utc'::text, NOW()) +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_company_identities_tax_id + ON public.company_identities (tax_id) + WHERE tax_id IS NOT NULL; +CREATE INDEX IF NOT EXISTS idx_company_identities_domain + ON public.company_identities (normalized_domain); +CREATE INDEX IF NOT EXISTS idx_company_identities_name + ON public.company_identities (normalized_name); + +-- 2. Table for Company Profiles (Multi-versioning) CREATE TABLE IF NOT EXISTS public.company_profiles ( id TEXT NOT NULL, version INT NOT NULL, official_name TEXT NOT NULL, data JSONB NOT NULL, + analysis_report JSONB, created_at TIMESTAMPTZ DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, updated_at TIMESTAMPTZ DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, - PRIMARY KEY (id, version) + PRIMARY KEY (id, version), + CONSTRAINT company_profiles_identity_fk FOREIGN KEY (id) REFERENCES public.company_identities(id) ); --- Index for querying latest version quickly CREATE INDEX IF NOT EXISTS idx_company_profiles_lookup -ON public.company_profiles (id, version DESC); + ON public.company_profiles (id, version DESC); --- Index for ordering by last updated CREATE INDEX IF NOT EXISTS idx_company_profiles_updated -ON public.company_profiles (updated_at DESC); + ON public.company_profiles (updated_at DESC); --- 2. Table for Profile Diffs +CREATE INDEX IF NOT EXISTS idx_company_profiles_complete + ON public.company_profiles (id, version DESC) + WHERE analysis_report IS NOT NULL; + +-- 3. Table for Profile Diffs CREATE TABLE IF NOT EXISTS public.company_diffs ( id TEXT PRIMARY KEY, company_id TEXT NOT NULL, from_version INT NOT NULL, to_version INT NOT NULL, data JSONB NOT NULL, - created_at TIMESTAMPTZ DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL + created_at TIMESTAMPTZ DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, + CONSTRAINT company_diffs_identity_fk FOREIGN KEY (company_id) REFERENCES public.company_identities(id) ); --- Index for fetching diffs by company CREATE INDEX IF NOT EXISTS idx_company_diffs_company -ON public.company_diffs (company_id, created_at DESC); + ON public.company_diffs (company_id, created_at DESC); --- 3. Enable Row Level Security (RLS) - Optional for Public/Service Access +-- 4. Enable Row Level Security (RLS) & Server-Only Privileges +ALTER TABLE public.company_identities ENABLE ROW LEVEL SECURITY; ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; ALTER TABLE public.company_diffs ENABLE ROW LEVEL SECURITY; --- Allow all operations for anon/service role (Public access for the app) -CREATE POLICY "Allow anon read/write company_profiles" -ON public.company_profiles FOR ALL -TO anon, authenticated, service_role -USING (true) -WITH CHECK (true); - -CREATE POLICY "Allow anon read/write company_diffs" -ON public.company_diffs FOR ALL -TO anon, authenticated, service_role -USING (true) -WITH CHECK (true); +REVOKE ALL ON public.company_identities FROM anon, authenticated; +REVOKE ALL ON public.company_profiles FROM anon, authenticated; +REVOKE ALL ON public.company_diffs FROM anon, authenticated; + +GRANT SELECT, INSERT, UPDATE ON public.company_identities TO service_role; +GRANT SELECT, INSERT, UPDATE ON public.company_profiles TO service_role; +GRANT SELECT, INSERT, UPDATE ON public.company_diffs TO service_role; + +-- 5. Read-only Lookup RPC +CREATE OR REPLACE FUNCTION public.lookup_company_identities( + p_tax_id text, + p_domain text, + p_name text +) +RETURNS TABLE ( + id text, + tax_id text, + normalized_domain text, + normalized_name text, + created_at timestamptz, + updated_at timestamptz +) +LANGUAGE sql +STABLE +SECURITY INVOKER +SET search_path = '' +AS $$ + SELECT DISTINCT + ci.id, + ci.tax_id, + ci.normalized_domain, + ci.normalized_name, + ci.created_at, + ci.updated_at + FROM public.company_identities ci + WHERE (p_tax_id IS NOT NULL AND ci.tax_id = p_tax_id) + OR (p_domain IS NOT NULL AND ci.normalized_domain = p_domain) + OR (p_name IS NOT NULL AND ci.normalized_name = p_name) + ORDER BY ci.id; +$$; + +REVOKE EXECUTE ON FUNCTION public.lookup_company_identities(text, text, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.lookup_company_identities(text, text, text) TO service_role; + +-- 6. Transactional Resolve/Create Identity RPC +CREATE OR REPLACE FUNCTION public.resolve_company_identity( + p_tax_id text, + p_domain text, + p_name text, + p_candidate_id text +) +RETURNS text +LANGUAGE plpgsql +SECURITY INVOKER +SET search_path = '' +AS $$ +DECLARE + resolved_id text; + tax_owner_id text; +BEGIN + -- Detect conflict between tax ID and domain if both provided + IF p_tax_id IS NOT NULL AND p_domain IS NOT NULL THEN + SELECT id INTO tax_owner_id + FROM public.company_identities + WHERE tax_id = p_tax_id; + + IF tax_owner_id IS NOT NULL THEN + IF EXISTS ( + SELECT 1 FROM public.company_identities + WHERE normalized_domain = p_domain + AND id <> tax_owner_id + ) AND NOT EXISTS ( + SELECT 1 FROM public.company_identities + WHERE normalized_domain = p_domain + AND id = tax_owner_id + ) THEN + RAISE EXCEPTION 'identity_conflict'; + END IF; + END IF; + END IF; + + IF p_tax_id IS NOT NULL THEN + INSERT INTO public.company_identities ( + id, tax_id, normalized_domain, normalized_name + ) VALUES ( + p_candidate_id, p_tax_id, p_domain, p_name + ) ON CONFLICT (tax_id) WHERE tax_id IS NOT NULL DO NOTHING; + + SELECT id INTO resolved_id + FROM public.company_identities + WHERE tax_id = p_tax_id; + ELSIF p_domain IS NOT NULL THEN + PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtext(p_domain)); + + SELECT id INTO resolved_id + FROM public.company_identities + WHERE normalized_domain = p_domain + AND normalized_name = p_name + ORDER BY id + LIMIT 1; + + IF resolved_id IS NULL THEN + INSERT INTO public.company_identities ( + id, normalized_domain, normalized_name + ) VALUES ( + p_candidate_id, p_domain, p_name + ) RETURNING id INTO resolved_id; + END IF; + ELSE + INSERT INTO public.company_identities (id, normalized_name) + VALUES (p_candidate_id, p_name) + RETURNING id INTO resolved_id; + END IF; + + RETURN resolved_id; +END; +$$; + +REVOKE EXECUTE ON FUNCTION public.resolve_company_identity(text, text, text, text) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.resolve_company_identity(text, text, text, text) TO service_role; + +-- 7. Atomic Persist Research Snapshot RPC +CREATE OR REPLACE FUNCTION public.persist_research_snapshot( + p_company_id text, + p_tax_id text, + p_domain text, + p_name text, + p_version integer, + p_profile_data jsonb, + p_analysis_report jsonb, + p_diff_data jsonb +) +RETURNS timestamptz +LANGUAGE plpgsql +SECURITY INVOKER +SET search_path = '' +AS $$ +DECLARE + v_now timestamptz := timezone('utc'::text, now()); + v_official_name text; + v_diff_id text; + v_from_version integer; + v_to_version integer; +BEGIN + -- 1. Lock the target company_identities row + PERFORM 1 + FROM public.company_identities + WHERE id = p_company_id + FOR UPDATE; + + IF NOT FOUND THEN + RAISE EXCEPTION 'identity_not_found'; + END IF; + + -- 2. Recheck tax_id conflict against other identities + IF p_tax_id IS NOT NULL THEN + IF EXISTS ( + SELECT 1 FROM public.company_identities + WHERE tax_id = p_tax_id AND id <> p_company_id + ) THEN + RAISE EXCEPTION 'identity_conflict'; + END IF; + END IF; + + -- 3. Update target identity metadata + UPDATE public.company_identities + SET + tax_id = COALESCE(p_tax_id, tax_id), + normalized_domain = COALESCE(p_domain, normalized_domain), + normalized_name = COALESCE(p_name, normalized_name), + updated_at = v_now + WHERE id = p_company_id; + + v_official_name := COALESCE(p_profile_data->>'officialName', p_name); + + -- 4. Upsert company_profiles + INSERT INTO public.company_profiles ( + id, + version, + official_name, + data, + analysis_report, + created_at, + updated_at + ) VALUES ( + p_company_id, + p_version, + v_official_name, + p_profile_data, + p_analysis_report, + v_now, + v_now + ) + ON CONFLICT (id, version) DO UPDATE SET + official_name = EXCLUDED.official_name, + data = EXCLUDED.data, + analysis_report = EXCLUDED.analysis_report, + updated_at = EXCLUDED.updated_at; + + -- 5. Upsert diff if provided + IF p_diff_data IS NOT NULL THEN + v_diff_id := COALESCE(p_diff_data->>'id', p_company_id || '-v' || p_version); + v_from_version := (p_diff_data->>'fromVersion')::integer; + v_to_version := (p_diff_data->>'toVersion')::integer; + + INSERT INTO public.company_diffs ( + id, + company_id, + from_version, + to_version, + data, + created_at + ) VALUES ( + v_diff_id, + p_company_id, + v_from_version, + v_to_version, + p_diff_data, + v_now + ) + ON CONFLICT (id) DO UPDATE SET + from_version = EXCLUDED.from_version, + to_version = EXCLUDED.to_version, + data = EXCLUDED.data; + END IF; + + RETURN v_now; +END; +$$; + +REVOKE EXECUTE ON FUNCTION public.persist_research_snapshot(text, text, text, text, integer, jsonb, jsonb, jsonb) FROM PUBLIC, anon, authenticated; +GRANT EXECUTE ON FUNCTION public.persist_research_snapshot(text, text, text, text, integer, jsonb, jsonb, jsonb) TO service_role; diff --git a/tests/integration/supabase-cache-concurrency.test.ts b/tests/integration/supabase-cache-concurrency.test.ts new file mode 100644 index 0000000..b7d1dba --- /dev/null +++ b/tests/integration/supabase-cache-concurrency.test.ts @@ -0,0 +1,102 @@ +import { describe, it, expect } from "vitest"; +import { createClient } from "@supabase/supabase-js"; + +const testUrl = process.env.SUPABASE_TEST_URL; +const serviceKey = process.env.SUPABASE_TEST_SERVICE_ROLE_KEY; +const anonKey = process.env.SUPABASE_TEST_ANON_KEY; + +const isLiveDb = Boolean(testUrl && serviceKey); + +describe.skipIf(!isLiveDb)("Supabase Research Cache - Integration & Concurrency", () => { + it("resolves domain-only identity concurrently without duplicate rows", async () => { + const first = createClient(testUrl!, serviceKey!, { auth: { persistSession: false } }); + const second = createClient(testUrl!, serviceKey!, { auth: { persistSession: false } }); + + const domain = `race-${crypto.randomUUID()}.example`; + const name = `race ${crypto.randomUUID()}`; + + const [a, b] = await Promise.all([ + first.rpc("resolve_company_identity", { + p_tax_id: null, + p_domain: domain, + p_name: name, + p_candidate_id: crypto.randomUUID(), + }), + second.rpc("resolve_company_identity", { + p_tax_id: null, + p_domain: domain, + p_name: name, + p_candidate_id: crypto.randomUUID(), + }), + ]); + + expect(a.error).toBeNull(); + expect(b.error).toBeNull(); + expect(a.data).toBe(b.data); + + const { data: rows, error } = await first + .from("company_identities") + .select("id") + .eq("normalized_domain", domain); + + expect(error).toBeNull(); + expect(rows?.length).toBe(1); + }); + + it("rolls back persistence when identity conflict occurs post-pipeline", async () => { + const client = createClient(testUrl!, serviceKey!, { auth: { persistSession: false } }); + const taxId = `0101${Math.floor(100000 + Math.random() * 900000)}`; + + await client.rpc("resolve_company_identity", { + p_tax_id: taxId, + p_domain: `comp-a-${taxId}.vn`, + p_name: "company a", + p_candidate_id: crypto.randomUUID(), + }); + + const companyBId = crypto.randomUUID(); + await client.rpc("resolve_company_identity", { + p_tax_id: null, + p_domain: `comp-b-${taxId}.vn`, + p_name: "company b", + p_candidate_id: companyBId, + }); + + // Try to persist companyB with companyA's tax ID -> should fail & rollback + const { error: persistError } = await client.rpc("persist_research_snapshot", { + p_company_id: companyBId, + p_tax_id: taxId, + p_domain: `comp-b-${taxId}.vn`, + p_name: "company b", + p_version: 1, + p_profile_data: { id: companyBId, version: 1, officialName: "company b" }, + p_analysis_report: { companyId: companyBId, generatedAt: new Date().toISOString() }, + p_diff_data: null, + }); + + expect(persistError).not.toBeNull(); + + // Verify profile was not persisted + const { data: profiles } = await client + .from("company_profiles") + .select("id") + .eq("id", companyBId); + + expect(profiles?.length).toBe(0); + }); + + it("denies access to anon role on company_identities and RPCs", async () => { + if (!anonKey) return; + const anonClient = createClient(testUrl!, anonKey, { auth: { persistSession: false } }); + + const { error: selectError } = await anonClient.from("company_identities").select("*"); + expect(selectError).not.toBeNull(); + + const { error: rpcError } = await anonClient.rpc("lookup_company_identities", { + p_tax_id: null, + p_domain: null, + p_name: "test", + }); + expect(rpcError).not.toBeNull(); + }); +}); diff --git a/tests/unit/adapters.test.ts b/tests/unit/adapters.test.ts index f9eb2bd..15a05b5 100644 --- a/tests/unit/adapters.test.ts +++ b/tests/unit/adapters.test.ts @@ -174,5 +174,119 @@ describe("Adapters Unit Tests", () => { expect(diffs.length).toBe(1); expect(diffs[0].summary).toBe("Updated description"); }); + + it("resolves, persists, and retrieves complete research snapshots", async () => { + const identity = { + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }; + + const resolvedId = await storage.resolveOrCreateIdentity(identity, "company-a"); + expect(resolvedId).toBe("company-a"); + + const draftSnapshot = { + profile: createDummyProfile("company-a", 1), + report: { + companyId: "company-a", + generatedAt: new Date(), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Summary", + }, + diff: null, + }; + + const saved = await storage.persistResearchSnapshot(identity, draftSnapshot); + expect(saved.profile.id).toBe("company-a"); + expect(saved.lastSyncedAt).toBeDefined(); + + const candidates = await storage.findIdentityCandidates(identity); + expect(candidates).toEqual([ + expect.objectContaining({ companyId: "company-a", taxId: "0101245486" }), + ]); + + const snapshot = await storage.getLatestCompleteSnapshot("company-a"); + expect(snapshot).toMatchObject({ + profile: { id: "company-a", version: 1 }, + report: { companyId: "company-a" }, + diff: null, + }); + }); + + it("handles version 2 snapshot with matching diff", async () => { + const identity = { + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }; + + await storage.resolveOrCreateIdentity(identity, "company-a"); + + const v1Draft = { + profile: createDummyProfile("company-a", 1), + report: { + companyId: "company-a", + generatedAt: new Date(), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Summary v1", + }, + diff: null, + }; + await storage.persistResearchSnapshot(identity, v1Draft); + + const v2Diff: ProfileDiff = { + companyId: "company-a", + fromVersion: 1, + toVersion: 2, + changes: [{ field: "description", oldValue: "v1", newValue: "v2", changeType: "modified", significance: "medium" }], + summary: "Upgraded to v2", + }; + + const v2Draft = { + profile: createDummyProfile("company-a", 2), + report: { + companyId: "company-a", + generatedAt: new Date(), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Summary v2", + }, + diff: v2Diff, + }; + await storage.persistResearchSnapshot(identity, v2Draft); + + const snapshot = await storage.getLatestCompleteSnapshot("company-a"); + expect(snapshot).toMatchObject({ + profile: { id: "company-a", version: 2 }, + report: { companyId: "company-a", executiveSummary: "Summary v2" }, + diff: { toVersion: 2, summary: "Upgraded to v2" }, + }); + }); + + it("throws IdentityConflictError when tax ID is assigned to another company", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101245486", domain: "vingroup.net", name: "vingroup" }, + "company-a" + ); + + await expect( + storage.persistResearchSnapshot( + { taxId: "0101245486", domain: "other.vn", name: "other" }, + { + profile: createDummyProfile("company-b", 1), + report: { + companyId: "company-b", + generatedAt: new Date(), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Summary", + }, + diff: null, + } + ) + ).rejects.toThrow("Thông tin định danh công ty mâu thuẫn."); + }); }); }); diff --git a/tests/unit/supabase-storage.test.ts b/tests/unit/supabase-storage.test.ts index a164b0e..8aaef0a 100644 --- a/tests/unit/supabase-storage.test.ts +++ b/tests/unit/supabase-storage.test.ts @@ -1,21 +1,25 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, vi, beforeEach } from "vitest"; import { SupabaseStorageAdapter } from "@/adapters/storage/supabase"; +import type { CompanyProfile } from "@/lib/types"; describe("SupabaseStorageAdapter Unit Tests", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + it("throws error when initialized without credentials", () => { delete process.env.SUPABASE_URL; - delete process.env.SUPABASE_ANON_KEY; delete process.env.SUPABASE_SERVICE_ROLE_KEY; expect(() => new SupabaseStorageAdapter()).toThrow( - "Missing Supabase credentials: SUPABASE_URL or SUPABASE_ANON_KEY" + "Missing Supabase credentials: SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY" ); }); - it("initializes client properly with URL and Key", () => { + it("initializes client properly with URL and Service Role Key", () => { const adapter = new SupabaseStorageAdapter( "https://example.supabase.co", - "mock-anon-key" + "mock-service-role-key" ); expect(adapter).toBeDefined(); expect(typeof adapter.saveProfile).toBe("function"); @@ -24,5 +28,157 @@ describe("SupabaseStorageAdapter Unit Tests", () => { expect(typeof adapter.listProfiles).toBe("function"); expect(typeof adapter.saveDiff).toBe("function"); expect(typeof adapter.getDiffs).toBe("function"); + expect(typeof adapter.findIdentityCandidates).toBe("function"); + expect(typeof adapter.getLatestCompleteSnapshot).toBe("function"); + expect(typeof adapter.resolveOrCreateIdentity).toBe("function"); + expect(typeof adapter.persistResearchSnapshot).toBe("function"); + }); + + it("calls lookup_company_identities RPC with normalized values", async () => { + const adapter = new SupabaseStorageAdapter( + "https://example.supabase.co", + "mock-service-role-key" + ); + + const mockRpc = vi.fn().mockReturnValue({ + abortSignal: vi.fn(), + then: (resolve: (val: unknown) => unknown) => + Promise.resolve( + resolve({ + data: [ + { + id: "comp-1", + tax_id: "0101245486", + normalized_domain: "vingroup.net", + normalized_name: "tập đoàn vingroup", + }, + ], + error: null, + }) + ), + }); + + (adapter as unknown as { client: { rpc: unknown } }).client = { + rpc: mockRpc, + }; + + const identity = { + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }; + + const candidates = await adapter.findIdentityCandidates(identity); + expect(mockRpc).toHaveBeenCalledWith("lookup_company_identities", { + p_tax_id: "0101245486", + p_domain: "vingroup.net", + p_name: "tập đoàn vingroup", + }); + expect(candidates).toEqual([ + { + companyId: "comp-1", + taxId: "0101245486", + domain: "vingroup.net", + name: "tập đoàn vingroup", + }, + ]); + }); + + it("calls resolve_company_identity RPC and handles conflicts", async () => { + const adapter = new SupabaseStorageAdapter( + "https://example.supabase.co", + "mock-service-role-key" + ); + + const mockRpc = vi.fn().mockReturnValue({ + abortSignal: vi.fn(), + then: (resolve: (val: unknown) => unknown) => + Promise.resolve( + resolve({ + data: null, + error: { message: "identity_conflict: conflicting domain" }, + }) + ), + }); + + (adapter as unknown as { client: { rpc: unknown } }).client = { + rpc: mockRpc, + }; + + await expect( + adapter.resolveOrCreateIdentity( + { taxId: "0101245486", domain: "vingroup.net", name: "vingroup" }, + "candidate-1" + ) + ).rejects.toThrow("Thông tin định danh công ty mâu thuẫn."); + }); + + it("calls persist_research_snapshot RPC and returns parsed snapshot", async () => { + const adapter = new SupabaseStorageAdapter( + "https://example.supabase.co", + "mock-service-role-key" + ); + + const mockRpc = vi.fn().mockReturnValue({ + abortSignal: vi.fn(), + then: (resolve: (val: unknown) => unknown) => + Promise.resolve( + resolve({ + data: "2026-08-26T08:00:00.000Z", + error: null, + }) + ), + }); + + (adapter as unknown as { client: { rpc: unknown } }).client = { + rpc: mockRpc, + }; + + const dummyProfile: CompanyProfile = { + id: "comp-1", + version: 1, + createdAt: new Date("2026-08-26T00:00:00.000Z"), + lastUpdated: new Date("2026-08-26T08:00:00.000Z"), + input: { name: "Vingroup" }, + officialName: "Tập đoàn Vingroup", + tradingNames: [], + industry: ["Conglomerate"], + description: "Desc", + keyPeople: [], + products: [], + markets: [], + recentActivities: [], + sources: [], + overallConfidence: 0.9, + }; + + const draft = { + profile: dummyProfile, + report: { + companyId: "comp-1", + generatedAt: new Date("2026-08-26T08:00:00.000Z"), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Summary", + }, + diff: null, + }; + + const result = await adapter.persistResearchSnapshot( + { taxId: "0101245486", domain: "vingroup.net", name: "vingroup" }, + draft + ); + + expect(mockRpc).toHaveBeenCalledWith("persist_research_snapshot", { + p_company_id: "comp-1", + p_tax_id: "0101245486", + p_domain: "vingroup.net", + p_name: "vingroup", + p_version: 1, + p_profile_data: dummyProfile, + p_analysis_report: draft.report, + p_diff_data: null, + }); + expect(result.lastSyncedAt).toBe("2026-08-26T08:00:00.000Z"); }); }); From 40a1d2e5883f7edc531db3db69c12cc27c39cc1c Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 14:57:44 +0700 Subject: [PATCH 05/17] feat(cache): implement sprint 03 server read-through flow --- src/app/api/research/route.ts | 518 ++++++++++++++---- src/lib/stream.ts | 10 +- src/lib/types.ts | 4 +- src/modules/cache/index.ts | 182 +++++- src/modules/workflow/index.ts | 85 +-- tests/e2e/workflow-e2e.test.ts | 91 ++- tests/integration/research-workflow.test.ts | 236 ++------ tests/unit/research-cache-route.test.ts | 291 ++++++++++ tests/unit/research-cache.test.ts | 168 +++++- .../unit/research-route-observability.test.ts | 8 +- 10 files changed, 1193 insertions(+), 400 deletions(-) create mode 100644 tests/unit/research-cache-route.test.ts diff --git a/src/app/api/research/route.ts b/src/app/api/research/route.ts index cfebb6a..93f2a52 100644 --- a/src/app/api/research/route.ts +++ b/src/app/api/research/route.ts @@ -1,12 +1,18 @@ // ═══════════════════════════════════════════════════════ // API Route — Research Endpoint (SSE Streaming) -// Thin adapter: passes request to LangGraph ResearchWorkflow and streams events +// Cache-first: checks Supabase research cache before initializing +// expensive paid providers (Search, Scraper, LLM, Registry). // ═══════════════════════════════════════════════════════ import { NextRequest } from "next/server"; -import { CompanyInputSchema } from "@/lib/types"; -import type { StreamEvent } from "@/lib/types"; -import { createSSEStream } from "@/lib/stream"; +import { + ResearchRequestSchema, + type CompanyInput, + type CompanyProfile, + type StreamEvent, + type SourceName, +} from "@/lib/types"; +import { createSSEStream, type SSEWriter } from "@/lib/stream"; import { createLLMAdapter, createSearchAdapter, @@ -15,9 +21,17 @@ import { createStorageAdapter, getGuards, } from "@/config"; +import { + createResearchCache, + normalizeCompanyIdentity, + IdentityConflictError, + type NormalizedCompanyIdentity, + type ResearchCache, +} from "@/modules/cache"; import { createProfileModule } from "@/modules/profile"; import { createAnalystModule } from "@/modules/analyst"; import { createResearchWorkflow } from "@/modules/workflow"; +import type { ResearchWorkflowState } from "@/modules/workflow/state"; import { createLangfuseCallback, emitResearchScores, @@ -27,124 +41,408 @@ import { updateResearchObservationOutcome, updateResearchTraceOutcome, } from "@/observability/langfuse"; -import { slugify, type SourceName } from "@/lib/types"; export const runtime = "nodejs"; export const maxDuration = 300; export async function POST(req: NextRequest) { + let body: unknown; try { - const body = await req.json(); - const input = CompanyInputSchema.parse(body); - - const guards = getGuards(); - const llm = createLLMAdapter(); - const search = createSearchAdapter(); - const scraper = createScraperAdapter(); - const registry = createRegistryAdapter(); - const storage = createStorageAdapter(); - - const profile = createProfileModule({ llm }); - const analyst = createAnalystModule({ llm }); - - const workflow = createResearchWorkflow({ - search, - scraper, - registry, - storage, - profile, - analyst, - guards, - }); - - const { stream, writer } = createSSEStream(); - const researchRunId = crypto.randomUUID(); - const companyId = slugify(input.name); - - const traceContext: ResearchTraceContext = { - researchRunId, - companyId, - requestedSources: [ - "web_search", - "website", - "news", - "registry", - ...(input.linkedinUrl ? ["linkedin" as SourceName] : []), - ], - }; - const langfuseCallback = createLangfuseCallback(traceContext); - - // Setup cancellation & 285s internal deadline - const controller = new AbortController(); - const onReqAbort = () => controller.abort(); - req.signal.addEventListener("abort", onReqAbort); - const deadlineTimeout = setTimeout(() => { - controller.abort("Research deadline exceeded (285s)"); - }, 285_000); - - void (async () => { - try { - await traceResearch(traceContext, async (traceId) => { - try { - for await (const event of workflow.stream(input, { - researchRunId, - signal: controller.signal, - callbacks: langfuseCallback ? [langfuseCallback] : undefined, - onComplete: async (state) => { - updateResearchTraceOutcome(state); - await emitResearchScores(traceId, { - sourceResults: state.sourceResults, - hasProfile: Boolean(state.profile), - hasAnalysis: Boolean(state.report), - overallConfidence: state.profile?.overallConfidence ?? 0, - outcome: - state.outcome === "running" ? "failed" : state.outcome, - }); + body = await req.json(); + } catch { + return new Response( + JSON.stringify({ error: "Invalid JSON body" }), + { status: 400, headers: { "Content-Type": "application/json" } } + ); + } + + const parseResult = ResearchRequestSchema.safeParse(body); + if (!parseResult.success) { + return new Response( + JSON.stringify({ + error: "Validation failed", + details: parseResult.error.flatten(), + }), + { status: 400, headers: { "Content-Type": "application/json" } } + ); + } + + const { input, cache } = parseResult.data; + const action = cache?.action ?? "auto"; + const selectedCompanyId = cache?.action === "select" ? cache.companyId : undefined; + const refreshCompanyId = cache?.action === "refresh" ? cache.companyId : undefined; + + const storage = createStorageAdapter(); + const researchCache = createResearchCache(storage); + + const { stream, writer } = createSSEStream(); + const researchRunId = crypto.randomUUID(); + + // Cancellation & 285s internal deadline + const controller = new AbortController(); + const onReqAbort = () => controller.abort(); + req.signal.addEventListener("abort", onReqAbort); + const deadlineTimeout = setTimeout(() => { + controller.abort("Research deadline exceeded (285s)"); + }, 285_000); + + void (async () => { + try { + // 1. Check Cache / Handle Action + if (action === "auto") { + let resolution; + try { + resolution = await researchCache.lookup(input, { signal: controller.signal }); + } catch (err) { + const message = + err instanceof Error ? err.message : "Cache lookup failed"; + writer.write({ + event: "error", + data: { message, code: "cache_unavailable" }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + if (resolution.kind === "hit") { + writer.write({ + event: "cache:hit", + data: { + companyId: resolution.snapshot.profile.id, + matchedBy: resolution.matchedBy, + version: resolution.snapshot.profile.version, + lastSyncedAt: resolution.snapshot.lastSyncedAt, + }, + } as StreamEvent); + writer.write({ + event: "profile:ready", + data: { profile: resolution.snapshot.profile }, + } as StreamEvent); + if (resolution.snapshot.diff) { + writer.write({ + event: "diff:ready", + data: { diff: resolution.snapshot.diff }, + } as StreamEvent); + } + writer.write({ + event: "analysis:ready", + data: { report: resolution.snapshot.report }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + if (resolution.kind === "suggestions") { + writer.write({ + event: "cache:suggestions", + data: { suggestions: resolution.suggestions }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + if (resolution.kind === "conflict") { + writer.write({ + event: "error", + data: { + message: "Thông tin định danh công ty mâu thuẫn.", + code: "identity_conflict", + }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + // Miss -> proceed with live research + const miss = await researchCache.resolveMiss(input, { signal: controller.signal }); + await executeLiveWorkflow({ + input, + companyId: miss.companyId, + identity: miss.identity, + existingProfile: null, + researchRunId, + controller, + writer, + researchCache, + }); + return; + } + + if (action === "select") { + if (!selectedCompanyId) { + writer.write({ + event: "error", + data: { + message: "Thiếu mã định danh công ty được chọn.", + code: "invalid_cache_selection", + }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + try { + const snapshot = await researchCache.select(input, selectedCompanyId, { + signal: controller.signal, + }); + writer.write({ + event: "cache:hit", + data: { + companyId: snapshot.profile.id, + matchedBy: "user_selection", + version: snapshot.profile.version, + lastSyncedAt: snapshot.lastSyncedAt, + }, + } as StreamEvent); + writer.write({ + event: "profile:ready", + data: { profile: snapshot.profile }, + } as StreamEvent); + if (snapshot.diff) { + writer.write({ + event: "diff:ready", + data: { diff: snapshot.diff }, + } as StreamEvent); + } + writer.write({ + event: "analysis:ready", + data: { report: snapshot.report }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } catch (err) { + const message = + err instanceof Error ? err.message : "Invalid cache selection"; + writer.write({ + event: "error", + data: { message, code: "invalid_cache_selection" }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + } + + if (action === "refresh") { + if (!refreshCompanyId) { + writer.write({ + event: "error", + data: { + message: "Thiếu mã định danh công ty cần làm mới.", + code: "identity_conflict", + }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + let snapshot; + try { + snapshot = await researchCache.prepareRefresh(input, refreshCompanyId, { + signal: controller.signal, + }); + } catch (err) { + const message = + err instanceof Error ? err.message : "Identity conflict on refresh"; + writer.write({ + event: "error", + data: { message, code: "identity_conflict" }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + + const identity = normalizeCompanyIdentity(input); + await executeLiveWorkflow({ + input, + companyId: refreshCompanyId, + identity, + existingProfile: snapshot.profile, + researchRunId, + controller, + writer, + researchCache, + }); + return; + } + + if (action === "bypass") { + const miss = await researchCache.resolveMiss(input, { signal: controller.signal }); + await executeLiveWorkflow({ + input, + companyId: miss.companyId, + identity: miss.identity, + existingProfile: null, + researchRunId, + controller, + writer, + researchCache, + }); + return; + } + } catch (err) { + const message = err instanceof Error ? err.message : "Internal error"; + writer.write({ + event: "error", + data: { message }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + } finally { + clearTimeout(deadlineTimeout); + req.signal.removeEventListener("abort", onReqAbort); + await flushLangfuse(); + writer.close(); + } + })(); + + return new Response(stream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }); +} + +async function executeLiveWorkflow({ + input, + companyId, + identity, + existingProfile, + researchRunId, + controller, + writer, + researchCache, +}: { + input: CompanyInput; + companyId: string; + identity: NormalizedCompanyIdentity; + existingProfile: CompanyProfile | null; + researchRunId: string; + controller: AbortController; + writer: SSEWriter; + researchCache: ResearchCache; +}) { + const guards = getGuards(); + const llm = createLLMAdapter(); + const search = createSearchAdapter(); + const scraper = createScraperAdapter(); + const registry = createRegistryAdapter(); + + const profile = createProfileModule({ llm }); + const analyst = createAnalystModule({ llm }); + + const workflow = createResearchWorkflow({ + search, + scraper, + registry, + profile, + analyst, + guards, + }); + + const traceContext: ResearchTraceContext = { + researchRunId, + companyId, + requestedSources: [ + "web_search", + "website", + "news", + "registry", + ...(input.linkedinUrl ? ["linkedin" as SourceName] : []), + ], + }; + const langfuseCallback = createLangfuseCallback(traceContext); + + let finalState: ResearchWorkflowState | null = null; + + await traceResearch(traceContext, async (traceId) => { + try { + for await (const event of workflow.stream(input, { + researchRunId, + companyId, + existingProfile, + signal: controller.signal, + callbacks: langfuseCallback ? [langfuseCallback] : undefined, + onComplete: async (state) => { + finalState = state; + updateResearchTraceOutcome(state); + await emitResearchScores(traceId, { + sourceResults: state.sourceResults, + hasProfile: Boolean(state.profile), + hasAnalysis: Boolean(state.report), + overallConfidence: state.profile?.overallConfidence ?? 0, + outcome: state.outcome === "running" ? "failed" : state.outcome, + }); + }, + })) { + writer.write(event); + } + + // Persist snapshot & emit final events + if (finalState?.profile && finalState?.report) { + try { + await researchCache.persist( + identity, + { + profile: finalState.profile, + report: finalState.report, + diff: finalState.diff, + }, + { signal: controller.signal } + ); + + writer.write({ + event: "profile:ready", + data: { profile: finalState.profile }, + } as StreamEvent); + if (finalState.diff) { + writer.write({ + event: "diff:ready", + data: { diff: finalState.diff }, + } as StreamEvent); + } + writer.write({ + event: "analysis:ready", + data: { report: finalState.report }, + } as StreamEvent); + } catch (persistErr) { + if (persistErr instanceof IdentityConflictError) { + writer.write({ + event: "error", + data: { + message: "Hồ sơ nghiên cứu bị từ chối do mâu thuẫn định danh phát sinh.", + code: "identity_conflict", }, - })) { - writer.write(event); - } - } catch (err) { + } as StreamEvent); + } else { const message = - err instanceof Error ? err.message : "Internal workflow error"; - updateResearchObservationOutcome( - controller.signal.aborted ? "cancelled" : "failed", - ); - await emitResearchScores(traceId, { - sourceResults: [], - hasProfile: false, - hasAnalysis: false, - overallConfidence: 0, - outcome: "failed", - }); + persistErr instanceof Error ? persistErr.message : "Failed to persist snapshot"; writer.write({ event: "error", data: { message }, } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); } - }); - } finally { - clearTimeout(deadlineTimeout); - req.signal.removeEventListener("abort", onReqAbort); - await flushLangfuse(); - writer.close(); + } } - })(); - - return new Response(stream, { - headers: { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - }, - }); - } catch (err) { - return new Response( - JSON.stringify({ - error: err instanceof Error ? err.message : "Invalid request", - }), - { status: 400, headers: { "Content-Type": "application/json" } } - ); - } + writer.write({ event: "done", data: {} } as StreamEvent); + } catch (err) { + const message = + err instanceof Error ? err.message : "Internal workflow error"; + updateResearchObservationOutcome( + controller.signal.aborted ? "cancelled" : "failed", + ); + await emitResearchScores(traceId, { + sourceResults: [], + hasProfile: false, + hasAnalysis: false, + overallConfidence: 0, + outcome: "failed", + }); + writer.write({ + event: "error", + data: { message }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + } + }); } diff --git a/src/lib/stream.ts b/src/lib/stream.ts index fcb60f1..a376aa6 100644 --- a/src/lib/stream.ts +++ b/src/lib/stream.ts @@ -4,15 +4,17 @@ import type { StreamEvent } from "@/lib/types"; +export interface SSEWriter { + write(event: StreamEvent): void; + close(): void; +} + /** * Create a ReadableStream that accepts StreamEvents and encodes them as SSE. */ export function createSSEStream(): { stream: ReadableStream; - writer: { - write(event: StreamEvent): void; - close(): void; - }; + writer: SSEWriter; } { const encoder = new TextEncoder(); let controller: ReadableStreamDefaultController; diff --git a/src/lib/types.ts b/src/lib/types.ts index fc40d85..4c99c83 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -203,10 +203,12 @@ export type ResearchRequest = z.infer; export type ResearchErrorCode = | "identity_conflict" | "cache_invalid" + | "invalid_cache_selection" + | "cache_unavailable" | "persist_failed" | "research_failed"; -export type CacheHitMatchedBy = "tax_id" | "domain" | "selected"; +export type CacheHitMatchedBy = "tax_id" | "domain" | "selected" | "user_selection"; export interface CacheSuggestion { companyId: string; diff --git a/src/modules/cache/index.ts b/src/modules/cache/index.ts index 8848c3c..9a40f75 100644 --- a/src/modules/cache/index.ts +++ b/src/modules/cache/index.ts @@ -1,4 +1,13 @@ -import type { CompanyInput } from "@/lib/types"; +import type { + CompanyInput, + CacheSuggestion, + ResearchSnapshot, +} from "@/lib/types"; +import type { + StorageAdapter, + StorageReadOptions, + StorageWriteOptions, +} from "@/adapters/storage/types"; const TAX_ID_PATTERN = /^\d{10}(?:\d{3})?$/; @@ -21,6 +30,47 @@ export type CacheDecision = | { kind: "miss" } | { kind: "conflict"; taxCompanyId: string; domainCompanyIds: string[] }; +export type CacheResolution = + | { + kind: "hit"; + snapshot: ResearchSnapshot; + matchedBy: "tax_id" | "domain"; + } + | { kind: "suggestions"; suggestions: CacheSuggestion[] } + | { + kind: "miss"; + identity: NormalizedCompanyIdentity; + cacheInvalid: boolean; + } + | { + kind: "conflict"; + taxCompanyId: string; + domainCompanyIds: string[]; + }; + +export interface ResearchCache { + lookup(input: CompanyInput, options?: StorageReadOptions): Promise; + select( + input: CompanyInput, + companyId: string, + options?: StorageReadOptions, + ): Promise; + prepareRefresh( + input: CompanyInput, + companyId: string, + options?: StorageReadOptions, + ): Promise; + resolveMiss( + input: CompanyInput, + options?: StorageWriteOptions, + ): Promise<{ companyId: string; identity: NormalizedCompanyIdentity }>; + persist( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions, + ): Promise; +} + export class IdentityConflictError extends Error { readonly code = "identity_conflict"; constructor(message = "Thông tin định danh công ty mâu thuẫn.") { @@ -157,3 +207,133 @@ export function decideCacheLookup( // 4. Miss return { kind: "miss" }; } + +export function createResearchCache(storage: StorageAdapter): ResearchCache { + return { + async lookup(input: CompanyInput, options?: StorageReadOptions): Promise { + const identity = normalizeCompanyIdentity(input); + const candidates = await storage.findIdentityCandidates(identity, options); + const decision = decideCacheLookup(identity, candidates); + + switch (decision.kind) { + case "hit": { + try { + const snapshot = await storage.getLatestCompleteSnapshot(decision.companyId, options); + if (!snapshot) { + return { kind: "miss", identity, cacheInvalid: false }; + } + return { + kind: "hit", + snapshot, + matchedBy: decision.matchedBy, + }; + } catch (err) { + if (err instanceof CacheInvalidError) { + return { kind: "miss", identity, cacheInvalid: true }; + } + throw err; + } + } + case "suggestions": { + const suggestions: CacheSuggestion[] = []; + for (const id of decision.companyIds) { + try { + const snapshot = await storage.getLatestCompleteSnapshot(id, options); + if (snapshot) { + const candidate = candidates.find((c) => c.companyId === id); + suggestions.push({ + companyId: id, + officialName: snapshot.profile.officialName, + taxId: snapshot.profile.taxId ?? candidate?.taxId ?? undefined, + domain: candidate?.domain ?? (snapshot.profile.website ? normalizeDomain(snapshot.profile.website) ?? undefined : undefined), + lastSyncedAt: snapshot.lastSyncedAt, + }); + } + } catch { + // Ignore corrupt candidates in suggestions + } + } + if (suggestions.length === 0) { + return { kind: "miss", identity, cacheInvalid: false }; + } + return { kind: "suggestions", suggestions }; + } + case "conflict": { + return { + kind: "conflict", + taxCompanyId: decision.taxCompanyId, + domainCompanyIds: decision.domainCompanyIds, + }; + } + case "miss": + default: { + return { kind: "miss", identity, cacheInvalid: false }; + } + } + }, + + async select( + input: CompanyInput, + companyId: string, + options?: StorageReadOptions + ): Promise { + const resolution = await this.lookup(input, options); + if ( + resolution.kind !== "suggestions" || + !resolution.suggestions.some((s) => s.companyId === companyId) + ) { + throw new InvalidCacheSelectionError(); + } + + const snapshot = await storage.getLatestCompleteSnapshot(companyId, options); + if (!snapshot) { + throw new InvalidCacheSelectionError(); + } + return snapshot; + }, + + async prepareRefresh( + input: CompanyInput, + companyId: string, + options?: StorageReadOptions + ): Promise { + const identity = normalizeCompanyIdentity(input); + const candidates = await storage.findIdentityCandidates(identity, options); + const decision = decideCacheLookup(identity, candidates); + + if (decision.kind === "conflict") { + throw new IdentityConflictError(); + } + if (decision.kind === "hit" && decision.companyId !== companyId) { + throw new IdentityConflictError(); + } + if (decision.kind === "suggestions" && !decision.companyIds.includes(companyId)) { + throw new IdentityConflictError(); + } + + const snapshot = await storage.getLatestCompleteSnapshot(companyId, options); + if (!snapshot) { + throw new IdentityConflictError("Không tìm thấy dữ liệu công ty để làm mới."); + } + return snapshot; + }, + + async resolveMiss( + input: CompanyInput, + options?: StorageWriteOptions + ): Promise<{ companyId: string; identity: NormalizedCompanyIdentity }> { + const identity = normalizeCompanyIdentity(input); + const candidateId = crypto.randomUUID(); + const companyId = await storage.resolveOrCreateIdentity(identity, candidateId, options); + return { companyId, identity }; + }, + + async persist( + identity: NormalizedCompanyIdentity, + snapshot: Omit, + options?: StorageWriteOptions + ): Promise { + return await storage.persistResearchSnapshot(identity, snapshot, options); + }, + }; +} diff --git a/src/modules/workflow/index.ts b/src/modules/workflow/index.ts index 5c32593..c5aa600 100644 --- a/src/modules/workflow/index.ts +++ b/src/modules/workflow/index.ts @@ -9,17 +9,16 @@ import { dispatchCustomEvent } from "@langchain/core/callbacks/dispatch"; import type { Callbacks } from "@langchain/core/callbacks/manager"; import type { CompanyInput, + CompanyProfile, SourceError, SourceExecutionResult, SourceName, StreamEvent, } from "@/lib/types"; -import { slugify } from "@/lib/types"; import type { LLMInvocationContext } from "@/adapters/llm/types"; import type { SearchAdapter } from "@/adapters/search/types"; import type { ScraperAdapter } from "@/adapters/scraper/types"; import type { RegistryAdapter } from "@/adapters/registry/types"; -import type { StorageAdapter } from "@/adapters/storage/types"; import type { ResourceGuards } from "@/config"; import type { ProfileModule } from "@/modules/profile"; import type { AnalystModule } from "@/modules/analyst"; @@ -43,6 +42,8 @@ const SSE_EVENT_NAME = "sse_event"; export interface ResearchWorkflowOptions { researchRunId: string; + companyId?: string; + existingProfile?: CompanyProfile | null; signal?: AbortSignal; callbacks?: readonly unknown[]; sessionId?: string; @@ -53,7 +54,6 @@ export interface ResearchWorkflowDeps { search: SearchAdapter; scraper: ScraperAdapter; registry: RegistryAdapter; - storage: StorageAdapter; profile: ProfileModule; analyst: AnalystModule; guards: ResourceGuards; @@ -97,7 +97,7 @@ export function createResearchWorkflow(deps: ResearchWorkflowDeps): ResearchWork const app = compileResearchGraph(deps, runners, options); const eventStream = app.streamEvents( - createInitialState(input, options.researchRunId), + createInitialState(input, options), { version: "v2", signal: options.signal, @@ -139,8 +139,6 @@ export function createResearchWorkflow(deps: ResearchWorkflowDeps): ResearchWork data: { message: "Không tìm thấy thông tin nào về công ty này." }, } as StreamEvent; } - - yield { event: "done", data: {} } as StreamEvent; }, }; } @@ -154,7 +152,7 @@ async function executeGraph( const app = compileResearchGraph(deps, runners, options); return (await app.invoke( - createInitialState(input, options.researchRunId), + createInitialState(input, options), { signal: options.signal, callbacks: options.callbacks as Callbacks, @@ -179,14 +177,14 @@ function compileResearchGraph( function createInitialState( input: CompanyInput, - researchRunId: string, + options: ResearchWorkflowOptions, ): ResearchWorkflowState { return { - researchRunId, + researchRunId: options.researchRunId, input, sourceResults: [], findings: [], - existingProfile: null, + existingProfile: options.existingProfile ?? null, profile: null, diff: null, report: null, @@ -293,22 +291,6 @@ function buildGraph( outcome: prepared.outcome, }; })) - .addNode("load_existing_profile", tracedNode("profile.load", async (state) => { - if (state.fatalError) return {}; - const companyId = slugify(state.input.name); - try { - const existing = await deps.storage.getLatestProfile(companyId, { signal }); - return { existingProfile: existing }; - } catch (err) { - const message = - err instanceof Error ? err.message : "Failed to load existing profile"; - await dispatchCustomEvent(SSE_EVENT_NAME, { - event: "error", - data: { message }, - } as StreamEvent); - return { fatalError: message, outcome: "failed" as const }; - } - })) .addNode("build_profile", tracedNode("profile.build", async (state) => { if (state.fatalError || state.findings.length === 0) return {}; @@ -317,12 +299,13 @@ function buildGraph( data: { message: "Đang tổng hợp hồ sơ công ty..." }, } as StreamEvent); - const companyId = slugify(state.input.name); + const targetCompanyId = + options.companyId || state.existingProfile?.id || options.researchRunId; try { const profile = await deps.profile.buildProfile( state.findings, state.input, - state.existingProfile?.id ?? companyId, + targetCompanyId, state.existingProfile?.version, llmContext, ); @@ -337,41 +320,16 @@ function buildGraph( return { fatalError: message, outcome: "failed" as const }; } })) - .addNode("persist_profile", tracedNode("profile.persist", async (state) => { - if (state.fatalError || !state.profile || signal?.aborted) return {}; - try { - await deps.storage.saveProfile(state.profile, { signal }); - await dispatchCustomEvent(SSE_EVENT_NAME, { - event: "profile:ready", - data: { profile: state.profile }, - } as StreamEvent); - } catch (err) { - const message = err instanceof Error ? err.message : "Failed to persist profile"; - await dispatchCustomEvent(SSE_EVENT_NAME, { - event: "error", - data: { message }, - } as StreamEvent); - return { fatalError: message, outcome: "failed" as const }; - } - return {}; - })) - .addNode("build_and_persist_diff", tracedNode("profile.diff", async (state) => { + .addNode("build_diff", tracedNode("profile.diff", async (state) => { if (state.fatalError || !state.profile) return {}; if (state.existingProfile) { try { const diff = deps.profile.diffProfiles(state.profile, state.existingProfile); - if (!signal?.aborted) { - await deps.storage.saveDiff(diff, { signal }); - } - await dispatchCustomEvent(SSE_EVENT_NAME, { - event: "diff:ready", - data: { diff }, - } as StreamEvent); return { diff }; } catch (err) { const message = - err instanceof Error ? err.message : "Failed to persist profile diff"; + err instanceof Error ? err.message : "Failed to build profile diff"; await dispatchCustomEvent(SSE_EVENT_NAME, { event: "error", data: { message }, @@ -379,10 +337,6 @@ function buildGraph( return { fatalError: message, outcome: "failed" as const }; } } else { - await dispatchCustomEvent(SSE_EVENT_NAME, { - event: "diff:ready", - data: { diff: null }, - } as StreamEvent); return { diff: null }; } })) @@ -396,11 +350,6 @@ function buildGraph( llmContext, ); - await dispatchCustomEvent(SSE_EVENT_NAME, { - event: "analysis:ready", - data: { report }, - } as StreamEvent); - return { report }; } catch (err) { const message = err instanceof Error ? err.message : "Không thể phân tích hồ sơ."; @@ -421,11 +370,9 @@ function buildGraph( .addEdge("news", "prepare_evidence") .addEdge("registry", "prepare_evidence") .addEdge("linkedin", "prepare_evidence") - .addEdge("prepare_evidence", "load_existing_profile") - .addEdge("load_existing_profile", "build_profile") - .addEdge("build_profile", "persist_profile") - .addEdge("persist_profile", "build_and_persist_diff") - .addEdge("build_and_persist_diff", "analyze") + .addEdge("prepare_evidence", "build_profile") + .addEdge("build_profile", "build_diff") + .addEdge("build_diff", "analyze") .addEdge("analyze", END); } diff --git a/tests/e2e/workflow-e2e.test.ts b/tests/e2e/workflow-e2e.test.ts index 50b3a8a..a71cb1d 100644 --- a/tests/e2e/workflow-e2e.test.ts +++ b/tests/e2e/workflow-e2e.test.ts @@ -35,7 +35,7 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { resetAdapters(); }); - it("handles full E2E research workflow with SSE stream and versioned updates", async () => { + it("handles full E2E research workflow with SSE stream, caching, and versioned updates", async () => { const storage = createStorageAdapter() as MemoryStorageAdapter; storage.clear(); @@ -79,13 +79,15 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { llm.setResponse("Tổng hợp thông tin", JSON.stringify(v1MockProfile)); llm.setResponse("Phân tích và đánh giá", JSON.stringify(mockAnalysisData)); - // 2. Execute First API Request (Version 1) + // 2. Execute First API Request (Initial Miss -> Live Workflow -> Version 1 persisted) const req1 = new NextRequest("http://localhost:3000/api/research", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - name: "Vingroup", - website: "https://vingroup.net", + input: { + name: "Vingroup", + website: "https://vingroup.net", + }, }), }); @@ -101,13 +103,21 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { expect(text1).toContain("event: analysis:ready"); expect(text1).toContain("event: done"); - // Verify storage persistence - const savedV1 = await storage.getLatestProfile("vingroup"); + // Find persisted company ID + const candidates = await storage.findIdentityCandidates({ + taxId: null, + domain: "vingroup.net", + name: "vingroup", + }); + expect(candidates.length).toBe(1); + const companyId = candidates[0].companyId; + + const savedV1 = await storage.getLatestCompleteSnapshot(companyId); expect(savedV1).not.toBeNull(); - expect(savedV1?.version).toBe(1); - expect(savedV1?.officialName).toBe("Tập đoàn Vingroup"); + expect(savedV1?.profile.version).toBe(1); + expect(savedV1?.profile.officialName).toBe("Tập đoàn Vingroup"); - // 3. Execute Second API Request (Version 2 - Updated data with diff) + // 3. Execute Second API Request with Refresh (Version 2 - Updated data with diff) const v2MockProfile = { ...v1MockProfile, markets: ["Việt Nam", "Mỹ", "Châu Âu"], @@ -120,8 +130,14 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - name: "Vingroup", - website: "https://vingroup.net", + input: { + name: "Vingroup", + website: "https://vingroup.net", + }, + cache: { + action: "refresh", + companyId, + }, }), }); @@ -133,15 +149,34 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { expect(text2).toContain("event: done"); // Verify version 2 and diff persistence - const savedV2 = await storage.getLatestProfile("vingroup"); - expect(savedV2?.version).toBe(2); - expect(savedV2?.markets).toContain("Mỹ"); + const savedV2 = await storage.getLatestCompleteSnapshot(companyId); + expect(savedV2?.profile.version).toBe(2); + expect(savedV2?.profile.markets).toContain("Mỹ"); - const diffs = await storage.getDiffs(savedV2!.id); + const diffs = await storage.getDiffs(companyId); expect(diffs.length).toBe(1); expect(diffs[0].fromVersion).toBe(1); expect(diffs[0].toVersion).toBe(2); expect(diffs[0].changes.some((c) => c.field === "markets")).toBe(true); + + // 4. Execute Third API Request (Cache Hit - returns cached snapshot immediately) + const req3 = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Vingroup", + website: "https://vingroup.net", + }, + }), + }); + + const response3 = await POST(req3); + expect(response3.status).toBe(200); + const text3 = await response3.text(); + expect(text3).toContain("event: cache:hit"); + expect(text3).toContain("event: profile:ready"); + expect(text3).toContain("event: done"); }); it("rejects invalid request inputs with HTTP 400", async () => { @@ -149,7 +184,9 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - name: "", // empty name + input: { + name: "", // empty name + }, }), }); @@ -171,7 +208,9 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { new NextRequest("http://localhost:3000/api/research", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name: "EDUZ", website: "https://eduz.vn" }), + body: JSON.stringify({ + input: { name: "EDUZ", website: "https://eduz.vn" }, + }), }), ); @@ -204,7 +243,9 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { const req = new NextRequest("http://localhost:3000/api/research", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name: "FPT", website: "https://fpt.com.vn" }), + body: JSON.stringify({ + input: { name: "FPT", website: "https://fpt.com.vn" }, + }), signal: controller.signal, }); @@ -219,9 +260,15 @@ describe("E2E Workflow Tests - PartnerIQ Research Pipeline", () => { const text = await response.text(); expect(text).toContain("event: research:start"); - // Profile should not have been saved - const saved = await storage.getLatestProfile("fpt"); - expect(saved).toBeNull(); + // Profile / snapshot should not have been saved + const candidates = await storage.findIdentityCandidates({ + taxId: null, + domain: "fpt.com.vn", + name: "fpt", + }); + if (candidates.length > 0) { + const snapshot = await storage.getLatestCompleteSnapshot(candidates[0].companyId); + expect(snapshot).toBeNull(); + } }); }); - diff --git a/tests/integration/research-workflow.test.ts b/tests/integration/research-workflow.test.ts index 6ef2e92..0acb687 100644 --- a/tests/integration/research-workflow.test.ts +++ b/tests/integration/research-workflow.test.ts @@ -1,8 +1,7 @@ -import { describe, expect, it, beforeEach, vi } from "vitest"; +import { describe, expect, it, beforeEach } from "vitest"; import { createResearchWorkflow } from "@/modules/workflow"; import { createProfileModule } from "@/modules/profile"; import { createAnalystModule } from "@/modules/analyst"; -import { MemoryStorageAdapter } from "@/adapters/storage/memory"; import { MockLLMAdapter, MockSearchAdapter, @@ -12,13 +11,11 @@ import type { RegistryAdapter } from "@/adapters/registry"; import type { SearchOptions } from "@/adapters/search/types"; import type { ResourceGuards } from "@/config"; import type { CompanyInput, StreamEvent } from "@/lib/types"; -import * as langfuseObservability from "@/observability/langfuse"; describe("ResearchWorkflow (LangGraph StateGraph)", () => { let llm: MockLLMAdapter; let search: MockSearchAdapter; let scraper: MockScraperAdapter; - let storage: MemoryStorageAdapter; let registry: RegistryAdapter; let guards: ResourceGuards; @@ -26,7 +23,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { llm = new MockLLMAdapter(); search = new MockSearchAdapter(); scraper = new MockScraperAdapter(); - storage = new MemoryStorageAdapter(); registry = { findByTaxId: async () => null, }; @@ -98,7 +94,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { search, scraper, registry, - storage, profile: createProfileModule({ llm }), analyst: createAnalystModule({ llm }), guards, @@ -272,171 +267,52 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { ).toBe(true); }); - it("emits a fatal error and withholds profile-ready when persistence fails", async () => { - guards.maxQueriesPerResearch = 2; - guards.maxScrapePagesPerResearch = 1; - search.setResults("FPT", [ - { - title: "FPT", - url: "https://fpt.com.vn/about", - snippet: "FPT company information", - }, - ]); - scraper.extract = async (url: string) => ({ - url, - title: "FPT", - text: "FPT company website content long enough for profile synthesis.", - }); - storage.saveProfile = async () => { - throw new Error("Profile storage unavailable"); - }; - const observationOutcome = vi.spyOn( - langfuseObservability, - "updateResearchObservationOutcome", - ); - - const events: StreamEvent[] = []; - for await (const event of buildWorkflow().stream( - { name: "FPT", website: "https://fpt.com.vn" }, - { researchRunId: "persistence-failure" }, - )) { - events.push(event); - } - - expect(events).toContainEqual({ - event: "error", - data: { message: "Profile storage unavailable" }, - }); - expect(events.some((event) => event.event === "profile:ready")).toBe(false); - expect(observationOutcome).toHaveBeenCalledWith("failed"); - observationOutcome.mockRestore(); - }); - - it("aborts an in-flight profile write when the run is cancelled", async () => { - guards.maxQueriesPerResearch = 2; - guards.maxScrapePagesPerResearch = 1; - search.setResults("FPT", [ - { - title: "FPT", - url: "https://fpt.com.vn/about", - snippet: "FPT company information", - }, - ]); - scraper.extract = async (url: string) => ({ - url, - title: "FPT", - text: "FPT company website content long enough for profile synthesis.", - }); - let markWriteStarted: () => void = () => undefined; - const writeStarted = new Promise((resolve) => { - markWriteStarted = resolve; - }); - let writeAborted = false; - storage.saveProfile = async ( - _profile, - options?: { signal?: AbortSignal }, - ) => { - markWriteStarted(); - await new Promise((resolve, reject) => { - const timeout = setTimeout(resolve, 30); - options?.signal?.addEventListener( - "abort", - () => { - clearTimeout(timeout); - writeAborted = true; - reject(options.signal?.reason); - }, - { once: true }, - ); - }); + it("uses the supplied canonical company ID and previous profile for versioning", async () => { + const existingProfile = { + id: "stable-company-id", + version: 1, + createdAt: new Date(), + lastUpdated: new Date(), + input: { name: "Original Name" }, + officialName: "Công ty Cổ phần FPT", + tradingNames: [], + industry: ["Tech"], + description: "Old description", + keyPeople: [], + products: [], + markets: [], + recentActivities: [], + sources: [], + overallConfidence: 0.9, }; - const controller = new AbortController(); - const events: StreamEvent[] = []; - - const consume = (async () => { - for await (const event of buildWorkflow().stream( - { name: "FPT", website: "https://fpt.com.vn" }, - { researchRunId: "cancel-persistence", signal: controller.signal }, - )) { - events.push(event); - } - })(); - await writeStarted; - controller.abort(); - await consume.catch(() => undefined); - expect(writeAborted).toBe(true); - expect(events.some((event) => event.event === "profile:ready")).toBe(false); - }); - - it("stops before profile synthesis when existing-profile storage fails", async () => { - guards.maxQueriesPerResearch = 2; - guards.maxScrapePagesPerResearch = 1; - search.setResults("FPT", [ + search.setResults("Different Display Name", [ { - title: "FPT", - url: "https://fpt.com.vn/about", - snippet: "FPT company information", + title: "Company", + url: "https://example.com", + snippet: "Different Display Name information for research findings", }, ]); - scraper.extract = async (url: string) => ({ - url, - title: "FPT", - text: "FPT company website content long enough for profile synthesis.", - }); - storage.getLatestProfile = async () => { - throw new Error("Profile storage read unavailable"); - }; - const events: StreamEvent[] = []; - for await (const event of buildWorkflow().stream( - { name: "FPT", website: "https://fpt.com.vn" }, - { researchRunId: "storage-read-failure" }, - )) { - events.push(event); - } - - expect(events).toContainEqual({ - event: "error", - data: { message: "Profile storage read unavailable" }, - }); - expect(events.some((event) => event.event === "profile:building")).toBe(false); - }); - - it("passes the run signal into the existing-profile read", async () => { - guards.maxQueriesPerResearch = 2; - guards.maxScrapePagesPerResearch = 1; - search.setResults("FPT", [ + const state = await buildWorkflow().run( + { name: "Different Display Name" }, { - title: "FPT", - url: "https://fpt.com.vn/about", - snippet: "FPT company information", - }, - ]); - scraper.extract = async (url: string) => ({ - url, - title: "FPT", - text: "FPT company website content long enough for profile synthesis.", - }); - let receivedSignal: AbortSignal | undefined; - storage.getLatestProfile = async ( - _companyId: string, - options?: { signal?: AbortSignal }, - ) => { - receivedSignal = options?.signal; - return null; - }; - const controller = new AbortController(); - - await buildWorkflow().run( - { name: "FPT", website: "https://fpt.com.vn" }, - { researchRunId: "storage-read-signal", signal: controller.signal }, + researchRunId: "canonical-id", + companyId: "stable-company-id", + existingProfile, + } ); - expect(receivedSignal).toBe(controller.signal); + expect(state.profile?.id).toBe("stable-company-id"); + expect(state.profile?.version).toBe(2); + expect(state.diff).toMatchObject({ + companyId: "stable-company-id", + fromVersion: 1, + toVersion: 2, + }); }); - it("treats diff persistence failure as fatal", async () => { + it("streams research progress and findings without emitting final snapshot events", async () => { guards.maxQueriesPerResearch = 2; guards.maxScrapePagesPerResearch = 1; search.setResults("FPT", [ @@ -451,28 +327,21 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { title: "FPT", text: "FPT company website content long enough for profile synthesis.", }); - const workflow = buildWorkflow(); - await workflow.run( - { name: "FPT", website: "https://fpt.com.vn" }, - { researchRunId: "diff-v1" }, - ); - storage.saveDiff = async () => { - throw new Error("Diff storage unavailable"); - }; const events: StreamEvent[] = []; - for await (const event of workflow.stream( + for await (const event of buildWorkflow().stream( { name: "FPT", website: "https://fpt.com.vn" }, - { researchRunId: "diff-v2" }, + { researchRunId: "stream-progress-only" }, )) { events.push(event); } - expect(events).toContainEqual({ - event: "error", - data: { message: "Diff storage unavailable" }, - }); - expect(events.some((event) => event.event === "analysis:ready")).toBe(false); + expect(events.some((e) => e.event === "research:start")).toBe(true); + expect(events.some((e) => e.event === "research:progress")).toBe(true); + expect(events.some((e) => e.event === "profile:ready")).toBe(false); + expect(events.some((e) => e.event === "diff:ready")).toBe(false); + expect(events.some((e) => e.event === "analysis:ready")).toBe(false); + expect(events.some((e) => e.event === "done")).toBe(false); }); it("reports the terminal workflow state to its completion hook", async () => { @@ -555,7 +424,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { search, scraper, registry, - storage, profile: profileModule, analyst: analystModule, guards, @@ -580,11 +448,8 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { const startEvent = events.find((e) => e.event === "research:start"); expect(startEvent).toBeDefined(); - const profileReady = events.find((e) => e.event === "profile:ready"); - expect(profileReady).toBeDefined(); - - const doneEvent = events.find((e) => e.event === "done"); - expect(doneEvent).toBeDefined(); + const progressEvent = events.find((e) => e.event === "research:progress"); + expect(progressEvent).toBeDefined(); }); it("handles partial source failure without discarding sibling findings", async () => { @@ -603,7 +468,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { search, scraper, registry, - storage, profile: profileModule, analyst: analystModule, guards, @@ -627,12 +491,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { e.event === "research:progress" ); expect(progressEvents.some((p) => p.data.status === "failed")).toBe(true); - - const profileReady = events.find((e) => e.event === "profile:ready"); - expect(profileReady).toBeDefined(); - - const doneEvent = events.find((e) => e.event === "done"); - expect(doneEvent).toBeDefined(); }); it("skips linkedin when no linkedinUrl is provided", async () => { @@ -643,7 +501,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { search, scraper, registry, - storage, profile: profileModule, analyst: analystModule, guards, @@ -703,7 +560,6 @@ describe("ResearchWorkflow (LangGraph StateGraph)", () => { search, scraper, registry, - storage, profile: profileModule, analyst: analystModule, guards: benchmarkGuards, diff --git a/tests/unit/research-cache-route.test.ts b/tests/unit/research-cache-route.test.ts new file mode 100644 index 0000000..97bbd77 --- /dev/null +++ b/tests/unit/research-cache-route.test.ts @@ -0,0 +1,291 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { NextRequest } from "next/server"; +import { POST } from "@/app/api/research/route"; +import { MemoryStorageAdapter } from "@/adapters/storage/memory"; +import type { CompanyProfile, AnalysisReport } from "@/lib/types"; + +const mockLLM = vi.fn(); +const mockSearch = vi.fn(); +const mockScraper = vi.fn(); +const mockRegistry = vi.fn(); +let storage: MemoryStorageAdapter; + +vi.mock("@/config", () => ({ + createLLMAdapter: () => { + mockLLM(); + return {}; + }, + createSearchAdapter: () => { + mockSearch(); + return {}; + }, + createScraperAdapter: () => { + mockScraper(); + return {}; + }, + createRegistryAdapter: () => { + mockRegistry(); + return {}; + }, + createStorageAdapter: () => storage, + getGuards: () => ({ + maxConcurrentResearch: 1, + maxQueriesPerResearch: 6, + maxConcurrentSourceNodes: 4, + maxConcurrentProviderCalls: 4, + sourceTimeoutMs: 5000, + maxRetriesPerSource: 2, + maxTokensPerResearch: 50000, + maxLLMCallsPerResearch: 10, + scraperDelayMs: 0, + maxScrapePagesPerResearch: 5, + maxResearchPerDay: 50, + maxTokensPerDay: 500000, + }), +})); + +vi.mock("@/modules/profile", () => ({ + createProfileModule: () => ({ + buildProfile: vi.fn(), + diffProfiles: vi.fn(), + }), +})); + +vi.mock("@/modules/analyst", () => ({ + createAnalystModule: () => ({ + analyze: vi.fn(), + }), +})); + +vi.mock("@/modules/workflow", () => ({ + createResearchWorkflow: () => ({ + stream: async function* () { + yield { event: "research:start", data: { sources: [] } }; + }, + }), +})); + +vi.mock("@/observability/langfuse", () => ({ + createLangfuseCallback: () => null, + emitResearchScores: vi.fn(async () => undefined), + flushLangfuse: vi.fn(async () => undefined), + traceResearch: async (_context: unknown, task: (traceId: string) => Promise) => + task("mock-trace-id"), + updateResearchObservationOutcome: vi.fn(), + updateResearchTraceOutcome: vi.fn(), +})); + +describe("API Route - /api/research Cache Read-Through", () => { + const dummyProfile: CompanyProfile = { + id: "comp-fpt", + version: 1, + createdAt: new Date("2026-08-26T00:00:00.000Z"), + lastUpdated: new Date("2026-08-26T08:00:00.000Z"), + input: { name: "FPT Corporation" }, + officialName: "Công ty Cổ phần FPT", + tradingNames: ["FPT"], + taxId: "0101248141", + industry: ["Technology"], + description: "Technology Corporation", + keyPeople: [], + products: [], + markets: [], + recentActivities: [], + sources: [], + overallConfidence: 0.95, + }; + + const dummyReport: AnalysisReport = { + companyId: "comp-fpt", + generatedAt: new Date("2026-08-26T08:00:00.000Z"), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Executive Summary", + }; + + beforeEach(() => { + vi.clearAllMocks(); + storage = new MemoryStorageAdapter(); + }); + + it("returns cache:hit and final events without calling search/LLM providers on exact tax-ID match", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: dummyProfile, report: dummyReport, diff: null } + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "FPT", + taxId: "0101248141", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(mockSearch).not.toHaveBeenCalled(); + expect(mockLLM).not.toHaveBeenCalled(); + expect(body).toContain("event: cache:hit"); + expect(body).toContain("event: profile:ready"); + expect(body).toContain("event: analysis:ready"); + expect(body).toContain("event: done"); + }); + + it("returns cache:suggestions on ambiguous name matches", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: dummyProfile, report: dummyReport, diff: null } + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Công ty CP FPT", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(mockSearch).not.toHaveBeenCalled(); + expect(body).toContain("event: cache:suggestions"); + expect(body).toContain("comp-fpt"); + expect(body).toContain("event: done"); + }); + + it("returns identity_conflict error on conflicting tax-ID and domain input", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + await storage.resolveOrCreateIdentity( + { taxId: "0101245486", domain: "vingroup.net", name: "tập đoàn vingroup" }, + "comp-vin" + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Conflicting Corp", + taxId: "0101248141", + website: "https://vingroup.net", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(body).toContain("event: error"); + expect(body).toContain("identity_conflict"); + }); + + it("resolves cache selection when action is select", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: dummyProfile, report: dummyReport, diff: null } + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Công ty CP FPT", + }, + cache: { + action: "select", + companyId: "comp-fpt", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(mockSearch).not.toHaveBeenCalled(); + expect(body).toContain("event: cache:hit"); + expect(body).toContain("user_selection"); + expect(body).toContain("event: profile:ready"); + }); + + it("returns invalid_cache_selection error on invalid selectedCompanyId", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Công ty CP FPT", + }, + cache: { + action: "select", + companyId: "unrelated-uuid", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(body).toContain("event: error"); + expect(body).toContain("invalid_cache_selection"); + }); + + it("bypasses cache when action is bypass", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: dummyProfile, report: dummyReport, diff: null } + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "FPT", + taxId: "0101248141", + }, + cache: { + action: "bypass", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(mockSearch).toHaveBeenCalled(); + expect(body).toContain("event: research:start"); + }); +}); diff --git a/tests/unit/research-cache.test.ts b/tests/unit/research-cache.test.ts index 5e999b1..7e41097 100644 --- a/tests/unit/research-cache.test.ts +++ b/tests/unit/research-cache.test.ts @@ -1,10 +1,13 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, beforeEach, vi } from "vitest"; import { normalizeCompanyIdentity, decideCacheLookup, type IdentityCandidate, type NormalizedCompanyIdentity, + type ResearchCache, } from "@/modules/cache"; +import type { CompanyProfile } from "@/lib/types"; +import type { MemoryStorageAdapter } from "@/adapters/storage/memory"; describe("Research Cache - Normalization", () => { it("normalizes tax ID, domain, and Vietnamese name without dropping legal suffixes", () => { @@ -202,3 +205,166 @@ describe("Research Cache - Decision Logic", () => { expect(decideCacheLookup(nameOnly, [])).toEqual({ kind: "miss" }); }); }); + +describe("ResearchCache - Storage-backed Cache Module", () => { + let storage: MemoryStorageAdapter; + let cache: ResearchCache; + + const validProfile: CompanyProfile = { + id: "company-a", + version: 1, + createdAt: new Date("2026-08-26T00:00:00.000Z"), + lastUpdated: new Date("2026-08-26T08:00:00.000Z"), + input: { name: "FPT Corporation" }, + officialName: "Công ty Cổ phần FPT", + tradingNames: ["FPT"], + taxId: "0101248141", + industry: ["Technology"], + description: "Technology Corporation", + keyPeople: [], + products: [], + markets: [], + recentActivities: [], + sources: [], + overallConfidence: 0.95, + }; + + const validReport = { + companyId: "company-a", + generatedAt: new Date("2026-08-26T08:00:00.000Z"), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Executive Summary", + }; + + beforeEach(async () => { + const { MemoryStorageAdapter } = await import("@/adapters/storage/memory"); + const { createResearchCache } = await import("@/modules/cache"); + storage = new MemoryStorageAdapter(); + cache = createResearchCache(storage); + }); + + it("resolves tax-ID match to an immediate hit with complete snapshot", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "company-a" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: validProfile, report: validReport, diff: null } + ); + + const resolution = await cache.lookup({ name: "FPT", taxId: "0101248141" }); + expect(resolution).toMatchObject({ + kind: "hit", + matchedBy: "tax_id", + snapshot: { profile: { id: "company-a" } }, + }); + }); + + it("returns suggestions for name matches", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "company-a" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: validProfile, report: validReport, diff: null } + ); + + const resolution = await cache.lookup({ name: "Công ty CP FPT" }); + expect(resolution).toMatchObject({ + kind: "suggestions", + suggestions: [ + expect.objectContaining({ + companyId: "company-a", + officialName: "Công ty Cổ phần FPT", + }), + ], + }); + }); + + it("returns miss for unknown companies", async () => { + const resolution = await cache.lookup({ name: "Unknown Company" }); + expect(resolution).toEqual({ + kind: "miss", + identity: { taxId: null, domain: null, name: "unknown company" }, + cacheInvalid: false, + }); + }); + + it("rejects select when requested companyId is not in the suggestion candidate set", async () => { + // Seed company-a and company-b + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "company-a" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: validProfile, report: validReport, diff: null } + ); + + const profileB = { ...validProfile, id: "company-b", officialName: "Vingroup" }; + const reportB = { ...validReport, companyId: "company-b" }; + await storage.resolveOrCreateIdentity( + { taxId: "0101245486", domain: "vingroup.net", name: "tập đoàn vingroup" }, + "company-b" + ); + await storage.persistResearchSnapshot( + { taxId: "0101245486", domain: "vingroup.net", name: "tập đoàn vingroup" }, + { profile: profileB, report: reportB, diff: null } + ); + + await expect( + cache.select({ name: "Công ty CP FPT" }, "company-b") + ).rejects.toMatchObject({ code: "invalid_cache_selection" }); + }); + + it("rejects prepareRefresh when strong keys conflict with target company", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "company-a" + ); + await storage.persistResearchSnapshot( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + { profile: validProfile, report: validReport, diff: null } + ); + + await storage.resolveOrCreateIdentity( + { taxId: "0101245486", domain: "vingroup.net", name: "tập đoàn vingroup" }, + "company-b" + ); + const profileB = { ...validProfile, id: "company-b", officialName: "Vingroup" }; + const reportB = { ...validReport, companyId: "company-b" }; + await storage.persistResearchSnapshot( + { taxId: "0101245486", domain: "vingroup.net", name: "tập đoàn vingroup" }, + { profile: profileB, report: reportB, diff: null } + ); + + // Refreshing company-b with company-a's tax ID must reject with identity_conflict + await expect( + cache.prepareRefresh({ name: "Vingroup", taxId: "0101248141" }, "company-b") + ).rejects.toMatchObject({ code: "identity_conflict" }); + }); + + it("recovers from corrupt snapshot by returning miss with cacheInvalid: true", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "company-a" + ); + + // Mock storage.getLatestCompleteSnapshot to throw CacheInvalidError + const { CacheInvalidError } = await import("@/modules/cache"); + vi.spyOn(storage, "getLatestCompleteSnapshot").mockRejectedValueOnce( + new CacheInvalidError("Corrupted data") + ); + + const resolution = await cache.lookup({ name: "FPT", taxId: "0101248141" }); + expect(resolution).toEqual({ + kind: "miss", + identity: { taxId: "0101248141", domain: null, name: "fpt" }, + cacheInvalid: true, + }); + }); +}); + diff --git a/tests/unit/research-route-observability.test.ts b/tests/unit/research-route-observability.test.ts index a9bb109..0aef6bc 100644 --- a/tests/unit/research-route-observability.test.ts +++ b/tests/unit/research-route-observability.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { MemoryStorageAdapter } from "@/adapters/storage/memory"; const observabilityMocks = vi.hoisted(() => ({ emitResearchScores: vi.fn(async () => undefined), @@ -6,12 +7,14 @@ const observabilityMocks = vi.hoisted(() => ({ updateResearchObservationOutcome: vi.fn(), })); +let mockStorage = new MemoryStorageAdapter(); + vi.mock("@/config", () => ({ createLLMAdapter: () => ({}), createSearchAdapter: () => ({}), createScraperAdapter: () => ({}), createRegistryAdapter: () => ({}), - createStorageAdapter: () => ({}), + createStorageAdapter: () => mockStorage, getGuards: () => ({}), })); vi.mock("@/modules/profile", () => ({ createProfileModule: () => ({}) })); @@ -42,6 +45,7 @@ import { POST } from "@/app/api/research/route"; describe("Research route observability", () => { beforeEach(() => { vi.clearAllMocks(); + mockStorage = new MemoryStorageAdapter(); }); it("marks and scores an unexpected workflow failure", async () => { @@ -49,7 +53,7 @@ describe("Research route observability", () => { new NextRequest("http://localhost:3000/api/research", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name: "FPT" }), + body: JSON.stringify({ input: { name: "FPT" } }), }), ); From 87efa4ee8dda7725be2cb3878037f55694ae5ea9 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 14:59:43 +0700 Subject: [PATCH 06/17] feat(ui): implement sprint 04 client suggestions and refresh ui --- src/app/components/cache-suggestions.tsx | 85 ++++++ src/app/hooks/use-research.ts | 338 ++++++++++++++--------- src/app/page.tsx | 75 ++++- tests/unit/cache-suggestions.test.tsx | 57 ++++ tests/unit/use-research-reducer.test.ts | 210 ++++++++++++++ 5 files changed, 626 insertions(+), 139 deletions(-) create mode 100644 src/app/components/cache-suggestions.tsx create mode 100644 tests/unit/cache-suggestions.test.tsx create mode 100644 tests/unit/use-research-reducer.test.ts diff --git a/src/app/components/cache-suggestions.tsx b/src/app/components/cache-suggestions.tsx new file mode 100644 index 0000000..a68b727 --- /dev/null +++ b/src/app/components/cache-suggestions.tsx @@ -0,0 +1,85 @@ +"use client"; + +import type { CacheSuggestion } from "@/lib/types"; + +export interface CacheSuggestionsProps { + suggestions: CacheSuggestion[]; + onSelect: (companyId: string) => void; + onBypass: () => void; +} + +export function CacheSuggestions({ + suggestions, + onSelect, + onBypass, +}: CacheSuggestionsProps) { + if (!suggestions || suggestions.length === 0) return null; + + return ( +
+
+
+

+ Tìm thấy kết quả trong bộ nhớ đệm +

+

+ Chọn một hồ sơ có sẵn để tải ngay dữ liệu hoặc tiếp tục nghiên cứu mới + toàn diện. +

+
+
+ +
+ {suggestions.map((suggestion) => ( + + ))} +
+ +
+ +
+
+ ); +} diff --git a/src/app/hooks/use-research.ts b/src/app/hooks/use-research.ts index 1aa912c..ecaac59 100644 --- a/src/app/hooks/use-research.ts +++ b/src/app/hooks/use-research.ts @@ -7,12 +7,17 @@ import type { ProfileDiff, AnalysisReport, SourceName, + StreamEvent, + CacheSuggestion, + CacheHitMatchedBy, + ResearchErrorCode, + ResearchRequest, } from "@/lib/types"; export type SourceStatus = "idle" | "started" | "done" | "failed"; export interface ResearchState { - status: "idle" | "researching" | "building" | "done" | "error"; + status: "idle" | "researching" | "building" | "suggesting" | "done" | "error"; input: CompanyInput | null; sourceStatuses: Record; findings: { source: SourceName; summary: string }[]; @@ -20,9 +25,16 @@ export interface ResearchState { diff: ProfileDiff | null; report: AnalysisReport | null; error: string | null; + errorCode?: ResearchErrorCode; + suggestions: CacheSuggestion[]; + cacheHit: { + matchedBy: CacheHitMatchedBy; + version: number; + lastSyncedAt: string; + } | null; } -const INITIAL_STATE: ResearchState = { +export const INITIAL_STATE: ResearchState = { status: "idle", input: null, sourceStatuses: { @@ -37,155 +49,227 @@ const INITIAL_STATE: ResearchState = { diff: null, report: null, error: null, + suggestions: [], + cacheHit: null, }; -export function useResearch() { - const [state, setState] = useState(INITIAL_STATE); - const abortRef = useRef(null); - - const research = useCallback(async (input: CompanyInput) => { - // Abort previous research - abortRef.current?.abort(); - const controller = new AbortController(); - abortRef.current = controller; - - setState({ - ...INITIAL_STATE, - input, - status: "researching", - }); - - try { - const response = await fetch("/api/research", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(input), - signal: controller.signal, - }); - - if (!response.ok) { - const errBody = await response.json().catch(() => ({})); - throw new Error( - (errBody as { error?: string }).error ?? `HTTP ${response.status}` - ); - } - - const reader = response.body?.getReader(); - if (!reader) throw new Error("No response stream"); - - const decoder = new TextDecoder(); - let buffer = ""; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split("\n"); - buffer = lines.pop() ?? ""; - - let currentEvent = ""; - for (const line of lines) { - if (line.startsWith("event: ")) { - currentEvent = line.slice(7).trim(); - } else if (line.startsWith("data: ") && currentEvent) { - try { - const data = JSON.parse(line.slice(6)); - handleSSEEvent(currentEvent, data, setState); - } catch { - // Skip malformed JSON - } - currentEvent = ""; - } - } - } - } catch (err) { - if ((err as Error).name === "AbortError") return; - setState((prev) => ({ - ...prev, - status: "error", - error: (err as Error).message, - })); - } - }, []); - - const reset = useCallback(() => { - abortRef.current?.abort(); - setState(INITIAL_STATE); - }, []); - - return { state, research, reset }; -} +export function reduceResearchEvent( + state: ResearchState, + event: StreamEvent +): ResearchState { + switch (event.event) { + case "research:start": + return { + ...state, + status: "researching", + error: null, + errorCode: undefined, + sourceStatuses: { + web_search: "idle", + website: "idle", + registry: "idle", + news: "idle", + linkedin: "idle", + }, + }; -function handleSSEEvent( - event: string, - data: Record, - setState: React.Dispatch> -) { - switch (event) { case "research:progress": - setState((prev) => ({ - ...prev, + return { + ...state, sourceStatuses: { - ...prev.sourceStatuses, - [data.source as string]: data.status as SourceStatus, + ...state.sourceStatuses, + [event.data.source]: event.data.status as SourceStatus, }, - })); - break; + }; case "research:finding": - setState((prev) => ({ - ...prev, + return { + ...state, findings: [ - ...prev.findings, + ...state.findings, { - source: data.source as SourceName, - summary: data.summary as string, + source: event.data.source, + summary: event.data.summary, }, ], - })); - break; + }; case "profile:building": - setState((prev) => ({ - ...prev, + return { + ...state, status: "building", - })); - break; + }; case "profile:ready": - setState((prev) => ({ - ...prev, - profile: data.profile as CompanyProfile, - })); - break; + return { + ...state, + profile: event.data.profile, + }; case "diff:ready": - setState((prev) => ({ - ...prev, - diff: (data.diff as ProfileDiff) ?? null, - })); - break; + return { + ...state, + diff: event.data.diff, + }; case "analysis:ready": - setState((prev) => ({ - ...prev, - report: (data.report as AnalysisReport) ?? null, - })); - break; + return { + ...state, + report: event.data.report, + }; + + case "cache:hit": + return { + ...state, + cacheHit: { + matchedBy: event.data.matchedBy, + version: event.data.version, + lastSyncedAt: event.data.lastSyncedAt, + }, + }; + + case "cache:suggestions": + return { + ...state, + status: "suggesting", + suggestions: event.data.suggestions, + }; case "error": - setState((prev) => ({ - ...prev, - error: data.message as string, - })); - break; + return { + ...state, + error: event.data.message, + errorCode: event.data.code, + }; case "done": - setState((prev) => ({ - ...prev, - status: prev.error && !prev.profile ? "error" : "done", - })); - break; + if (state.status === "suggesting") { + return state; + } + return { + ...state, + status: state.error && !state.profile ? "error" : "done", + }; + + default: + return state; } } + +export function useResearch() { + const [state, setState] = useState(INITIAL_STATE); + const abortRef = useRef(null); + + const research = useCallback( + async (input: CompanyInput, cache?: ResearchRequest["cache"]) => { + // Abort previous research + abortRef.current?.abort(); + const controller = new AbortController(); + abortRef.current = controller; + + setState({ + ...INITIAL_STATE, + input, + status: "researching", + }); + + try { + const payload: ResearchRequest = { + input, + cache, + }; + + const response = await fetch("/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + signal: controller.signal, + }); + + if (!response.ok) { + const errBody = await response.json().catch(() => ({})); + throw new Error( + (errBody as { error?: string }).error ?? `HTTP ${response.status}` + ); + } + + const reader = response.body?.getReader(); + if (!reader) throw new Error("No response stream"); + + const decoder = new TextDecoder(); + let buffer = ""; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); + buffer = lines.pop() ?? ""; + + let currentEvent = ""; + for (const line of lines) { + if (line.startsWith("event: ")) { + currentEvent = line.slice(7).trim(); + } else if (line.startsWith("data: ") && currentEvent) { + try { + const data = JSON.parse(line.slice(6)); + const streamEvent = { + event: currentEvent, + data, + } as StreamEvent; + setState((prev) => reduceResearchEvent(prev, streamEvent)); + } catch { + // Skip malformed JSON + } + currentEvent = ""; + } + } + } + } catch (err) { + if ((err as Error).name === "AbortError") return; + setState((prev) => ({ + ...prev, + status: "error", + error: (err as Error).message, + })); + } + }, + [] + ); + + const selectSuggestion = useCallback( + (companyId: string) => { + if (!state.input) return; + void research(state.input, { action: "select", companyId }); + }, + [research, state.input] + ); + + const refreshResearch = useCallback(() => { + if (!state.input || !state.profile) return; + void research(state.input, { + action: "refresh", + companyId: state.profile.id, + }); + }, [research, state.input, state.profile]); + + const bypassAndResearch = useCallback(() => { + if (!state.input) return; + void research(state.input, { action: "bypass" }); + }, [research, state.input]); + + const reset = useCallback(() => { + abortRef.current?.abort(); + setState(INITIAL_STATE); + }, []); + + return { + state, + research, + selectSuggestion, + refreshResearch, + bypassAndResearch, + reset, + }; +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 31f717b..0068d46 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,12 +1,22 @@ "use client"; +import Image from "next/image"; import { ResearchForm } from "./components/research-form"; import { ResearchProgress } from "./components/research-progress"; import { ProfileCard } from "./components/profile-card"; +import { CacheSuggestions } from "./components/cache-suggestions"; import { useResearch } from "./hooks/use-research"; export default function HomePage() { - const { state, research, reset } = useResearch(); + const { + state, + research, + selectSuggestion, + refreshResearch, + bypassAndResearch, + reset, + } = useResearch(); + const isLoading = state.status === "researching" || state.status === "building"; @@ -16,10 +26,12 @@ export default function HomePage() {
- PartnerIQ Logo

PartnerIQ

@@ -59,7 +71,7 @@ export default function HomePage() {
research(input)} isLoading={isLoading} initialInput={state.input} /> @@ -86,18 +98,27 @@ export default function HomePage() { ) : ( /* ─── Research / Results state ─── */
- {/* Left panel: form + progress */} + {/* Left panel: form + progress + suggestions */}
research(input)} isLoading={isLoading} initialInput={state.input} /> - + + {state.status === "suggesting" ? ( + + ) : ( + + )} {state.error && (
@@ -108,7 +129,37 @@ export default function HomePage() {
{/* Right panel: profile */} -
+
+ {state.profile && ( +
+
+ {state.cacheHit ? ( + + Đã tải từ bộ nhớ đệm (v{state.cacheHit.version}) + + ) : ( + + Nghiên cứu trực tiếp mới nhất + + )} + {state.cacheHit && ( + + Đồng bộ: {new Date(state.cacheHit.lastSyncedAt).toLocaleString("vi-VN")} + + )} +
+ + +
+ )} + {state.profile ? ( { + const mockSuggestions: CacheSuggestion[] = [ + { + companyId: "comp-fpt", + officialName: "Công ty Cổ phần FPT", + taxId: "0101248141", + domain: "fpt.com.vn", + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }, + { + companyId: "comp-vin", + officialName: "Tập đoàn Vingroup", + taxId: "0101245486", + domain: "vingroup.net", + lastSyncedAt: "2026-08-25T12:00:00.000Z", + }, + ]; + + it("renders suggestions list with names, tax IDs, and domains", () => { + const onSelect = vi.fn(); + const onBypass = vi.fn(); + + const html = renderToString( + + ); + + expect(html).toContain("Công ty Cổ phần FPT"); + expect(html).toContain("0101248141"); + expect(html).toContain("fpt.com.vn"); + expect(html).toContain("Tập đoàn Vingroup"); + expect(html).toContain("0101245486"); + expect(html).toContain("vingroup.net"); + expect(html).toContain("Nghiên cứu mới (Bỏ qua cache)"); + }); + + it("renders null when suggestions list is empty", () => { + const html = renderToString( + + ); + + expect(html).toBe(""); + }); +}); diff --git a/tests/unit/use-research-reducer.test.ts b/tests/unit/use-research-reducer.test.ts new file mode 100644 index 0000000..ee64bed --- /dev/null +++ b/tests/unit/use-research-reducer.test.ts @@ -0,0 +1,210 @@ +import { describe, it, expect } from "vitest"; +import { + reduceResearchEvent, + INITIAL_STATE, + type ResearchState, +} from "@/app/hooks/use-research"; +import type { StreamEvent, CompanyProfile, AnalysisReport, ProfileDiff } from "@/lib/types"; + +describe("useResearch pure reducer - reduceResearchEvent", () => { + const dummyProfile: CompanyProfile = { + id: "comp-1", + version: 1, + createdAt: new Date(), + lastUpdated: new Date(), + input: { name: "Test" }, + officialName: "Test Corp", + tradingNames: [], + industry: ["Tech"], + description: "Desc", + keyPeople: [], + products: [], + markets: [], + recentActivities: [], + sources: [], + overallConfidence: 0.9, + }; + + const dummyDiff: ProfileDiff = { + companyId: "comp-1", + fromVersion: 1, + toVersion: 2, + generatedAt: new Date(), + changes: [ + { + field: "markets", + oldValue: ["Việt Nam"], + newValue: ["Việt Nam", "Mỹ"], + significance: "medium", + }, + ], + }; + + const dummyReport: AnalysisReport = { + companyId: "comp-1", + generatedAt: new Date(), + riskFlags: [], + suggestedActions: [], + executiveSummary: "Executive Summary", + }; + + it("resets errors on research:start", () => { + const errorState: ResearchState = { + ...INITIAL_STATE, + status: "error", + error: "Previous error", + errorCode: "identity_conflict", + }; + + const nextState = reduceResearchEvent(errorState, { + event: "research:start", + data: { sources: ["web_search", "news"] }, + }); + + expect(nextState.status).toBe("researching"); + expect(nextState.error).toBeNull(); + expect(nextState.errorCode).toBeUndefined(); + expect(nextState.sourceStatuses.web_search).toBe("idle"); + }); + + it("updates individual source statuses on research:progress", () => { + let state = reduceResearchEvent(INITIAL_STATE, { + event: "research:progress", + data: { source: "web_search", status: "started" }, + }); + expect(state.sourceStatuses.web_search).toBe("started"); + expect(state.sourceStatuses.news).toBe("idle"); + + state = reduceResearchEvent(state, { + event: "research:progress", + data: { source: "web_search", status: "done" }, + }); + expect(state.sourceStatuses.web_search).toBe("done"); + }); + + it("appends findings on research:finding", () => { + let state = reduceResearchEvent(INITIAL_STATE, { + event: "research:finding", + data: { source: "web_search", summary: "Finding 1" }, + }); + state = reduceResearchEvent(state, { + event: "research:finding", + data: { source: "news", summary: "Finding 2" }, + }); + + expect(state.findings).toHaveLength(2); + expect(state.findings[0]).toEqual({ source: "web_search", summary: "Finding 1" }); + expect(state.findings[1]).toEqual({ source: "news", summary: "Finding 2" }); + }); + + it("sets building status on profile:building", () => { + const nextState = reduceResearchEvent(INITIAL_STATE, { + event: "profile:building", + data: { message: "Building..." }, + }); + expect(nextState.status).toBe("building"); + }); + + it("records profile diff on diff:ready", () => { + const nextState = reduceResearchEvent(INITIAL_STATE, { + event: "diff:ready", + data: { diff: dummyDiff }, + }); + expect(nextState.diff).toEqual(dummyDiff); + }); + + it("handles cache:hit event and records metadata", () => { + const event: StreamEvent = { + event: "cache:hit", + data: { + companyId: "comp-1", + matchedBy: "tax_id", + version: 1, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }, + }; + + const nextState = reduceResearchEvent(INITIAL_STATE, event); + expect(nextState.cacheHit).toEqual({ + matchedBy: "tax_id", + version: 1, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }); + }); + + it("transitions to suggesting state on cache:suggestions", () => { + const event: StreamEvent = { + event: "cache:suggestions", + data: { + suggestions: [ + { + companyId: "comp-1", + officialName: "FPT Corporation", + taxId: "0101248141", + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }, + ], + }, + }; + + const nextState = reduceResearchEvent(INITIAL_STATE, event); + expect(nextState.status).toBe("suggesting"); + expect(nextState.suggestions).toHaveLength(1); + expect(nextState.suggestions[0].companyId).toBe("comp-1"); + }); + + it("preserves suggesting status on done event", () => { + const suggestingState: ResearchState = { + ...INITIAL_STATE, + status: "suggesting", + suggestions: [ + { + companyId: "comp-1", + officialName: "FPT Corporation", + lastSyncedAt: "2026-08-26T08:00:00.000Z", + }, + ], + }; + + const event: StreamEvent = { + event: "done", + data: {}, + }; + + const nextState = reduceResearchEvent(suggestingState, event); + expect(nextState.status).toBe("suggesting"); + }); + + it("records error and error code on error event", () => { + const event: StreamEvent = { + event: "error", + data: { + message: "Thông tin định danh công ty mâu thuẫn.", + code: "identity_conflict", + }, + }; + + const nextState = reduceResearchEvent(INITIAL_STATE, event); + expect(nextState.error).toBe("Thông tin định danh công ty mâu thuẫn."); + expect(nextState.errorCode).toBe("identity_conflict"); + }); + + it("transitions to done when profile and analysis are ready", () => { + let state = reduceResearchEvent(INITIAL_STATE, { + event: "profile:ready", + data: { profile: dummyProfile }, + }); + state = reduceResearchEvent(state, { + event: "analysis:ready", + data: { report: dummyReport }, + }); + state = reduceResearchEvent(state, { + event: "done", + data: {}, + }); + + expect(state.status).toBe("done"); + expect(state.profile?.id).toBe("comp-1"); + expect(state.report?.companyId).toBe("comp-1"); + }); +}); From ae0763a303b851e9c980ad18bda4eec990f6739e Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:00:13 +0700 Subject: [PATCH 07/17] fix(tests): fix ProfileDiff type in reducer unit test --- tests/unit/use-research-reducer.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/use-research-reducer.test.ts b/tests/unit/use-research-reducer.test.ts index ee64bed..4e1f0db 100644 --- a/tests/unit/use-research-reducer.test.ts +++ b/tests/unit/use-research-reducer.test.ts @@ -29,10 +29,11 @@ describe("useResearch pure reducer - reduceResearchEvent", () => { companyId: "comp-1", fromVersion: 1, toVersion: 2, - generatedAt: new Date(), + summary: "Markets updated", changes: [ { field: "markets", + changeType: "modified", oldValue: ["Việt Nam"], newValue: ["Việt Nam", "Mỹ"], significance: "medium", From 4f073d9bd76bb6ab823023daec3ba46c10976086 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:04:34 +0700 Subject: [PATCH 08/17] feat(telemetry): implement sprint 05 telemetry and release hardening --- README.md | 15 ++++++- src/app/api/research/route.ts | 40 +++++++++++++++++ src/observability/langfuse.ts | 33 +++++++++++++- tests/unit/langfuse-observability.test.ts | 43 +++++++++++++++++++ tests/unit/research-cache-route.test.ts | 52 +++++++++++++++++++++++ 5 files changed, 180 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 299114b..8417241 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,11 @@ * 💻 **Trưởng thành số (Digital Maturity - 15%)** * 📈 **Hoạt động gần đây (Recent Activity - 20%)** * 🔍 **"What Changed?" Diff Engine:** So sánh tự động giữa các phiên bản hồ sơ của một doanh nghiệp (v1 → v2), nhận diện biến động về nhân sự, địa chỉ, ngành nghề và quy mô. +* ⚡ **Supabase Server Read-Through Research Cache (Deterministic Identity Resolution):** + * Tự động nhận diện và tái sử dụng hồ sơ đã nghiên cứu qua MST (`taxId`) hoặc Website (`domain`) với độ trễ phản hồi < 100ms. + * Khóa giao dịch phân tán chống tranh chấp đồng thời (`pg_advisory_xact_lock` & `resolve_company_identity`). + * Giao diện tương tác trực tiếp: gợi ý ứng viên cache (`CacheSuggestions`), chọn hồ sơ có sẵn (`select`), làm mới dữ liệu (`refresh`) hoặc bỏ qua cache (`bypass`). + * Lưu trữ nguyên tử toàn bộ snapshot (`persist_research_snapshot`) gồm Profile, Diff và Analysis Report. * 🗄️ **Multi-Version Storage (Supabase PostgreSQL):** Lưu trữ lịch sử hồ sơ dạng JSONB, tối ưu hóa truy vấn và bảo toàn toàn bộ vết thay đổi. * 📑 **Bộ Công Cụ Xuất Bản Báo Cáo Chuyên Nghiệp:** * 📋 **Markdown & JSON Export:** Sao chép vào Clipboard hoặc tải file `.md` / `.json` ngay tức thì. @@ -500,10 +505,18 @@ MAX_SCRAPE_PAGES_PER_RESEARCH=5 # ─── Registry Provider (VietQR) ─── VIETQR_ENABLED=true # Tra cứu MST chính thức với 7-day memory cache -# ─── Storage Provider (supabase | memory) ─── +# ─── Storage & Cache Provider (supabase | memory) ─── STORAGE_PROVIDER=supabase SUPABASE_URL=https://xyz.supabase.co SUPABASE_ANON_KEY=eyJ... +SUPABASE_SERVICE_ROLE_KEY=eyJ... # Dành cho backend atomic transaction / RPC + +# ─── Langfuse Observability & Telemetry Privacy ─── +LANGFUSE_ENABLED=true +LANGFUSE_PUBLIC_KEY=pk-... +LANGFUSE_SECRET_KEY=sk-... +LANGFUSE_BASE_URL=https://cloud.langfuse.com +LANGFUSE_SALT=your-secure-salt-for-hashing-identifiers # ─── Resource & Rate Limit Guards ─── MAX_CONCURRENT_RESEARCH=1 diff --git a/src/app/api/research/route.ts b/src/app/api/research/route.ts index 93f2a52..a9b0d86 100644 --- a/src/app/api/research/route.ts +++ b/src/app/api/research/route.ts @@ -105,6 +105,24 @@ export async function POST(req: NextRequest) { } if (resolution.kind === "hit") { + const traceContext: ResearchTraceContext = { + researchRunId, + companyId: resolution.snapshot.profile.id, + requestedSources: [], + cacheHit: true, + cacheMatchedBy: resolution.matchedBy, + cacheAction: "auto", + }; + await traceResearch(traceContext, async (traceId) => { + await emitResearchScores(traceId, { + sourceResults: [], + hasProfile: true, + hasAnalysis: true, + overallConfidence: resolution.snapshot.profile.overallConfidence, + outcome: "complete", + }); + }); + writer.write({ event: "cache:hit", data: { @@ -185,6 +203,25 @@ export async function POST(req: NextRequest) { const snapshot = await researchCache.select(input, selectedCompanyId, { signal: controller.signal, }); + + const traceContext: ResearchTraceContext = { + researchRunId, + companyId: snapshot.profile.id, + requestedSources: [], + cacheHit: true, + cacheMatchedBy: "user_selection", + cacheAction: "select", + }; + await traceResearch(traceContext, async (traceId) => { + await emitResearchScores(traceId, { + sourceResults: [], + hasProfile: true, + hasAnalysis: true, + overallConfidence: snapshot.profile.overallConfidence, + outcome: "complete", + }); + }); + writer.write({ event: "cache:hit", data: { @@ -350,6 +387,9 @@ async function executeLiveWorkflow({ "registry", ...(input.linkedinUrl ? ["linkedin" as SourceName] : []), ], + cacheHit: false, + cacheMatchedBy: "none", + cacheAction: existingProfile ? "refresh" : "auto", }; const langfuseCallback = createLangfuseCallback(traceContext); diff --git a/src/observability/langfuse.ts b/src/observability/langfuse.ts index 6b0e779..fd0bca7 100644 --- a/src/observability/langfuse.ts +++ b/src/observability/langfuse.ts @@ -12,7 +12,9 @@ import { updateActiveObservation, } from "@langfuse/tracing"; import { NodeSDK } from "@opentelemetry/sdk-node"; +import crypto from "node:crypto"; import type { + CacheHitMatchedBy, ResearchOutcome, SourceExecutionResult, SourceName, @@ -41,6 +43,14 @@ export interface ResearchTraceContext { companyId: string; requestedSources: SourceName[]; sessionId?: string; + cacheHit?: boolean; + cacheMatchedBy?: CacheHitMatchedBy | "none"; + cacheAction?: "auto" | "bypass" | "select" | "refresh"; +} + +export function hashCompanyIdentifier(identifier: string): string { + const salt = process.env.LANGFUSE_SALT || "partneriq-telemetry-salt"; + return crypto.createHmac("sha256", salt).update(identifier).digest("hex"); } export interface DeterministicScore { @@ -180,18 +190,28 @@ export async function traceResearch( let taskPromise: Promise | undefined; try { + const isCacheHit = Boolean(context.cacheHit); + const tags = [ + "workflow:research", + "surface:sse", + isCacheHit ? "cache:hit" : "cache:miss", + ]; return await propagateAttributes( { traceName: "partneriq.research", sessionId: context.sessionId, version: APP_VERSION, - tags: ["workflow:research", "surface:sse"], + tags, environment: process.env.LANGFUSE_TRACING_ENVIRONMENT || "production", metadata: { researchRunId: context.researchRunId, companyId: context.companyId, + companyIdHash: hashCompanyIdentifier(context.companyId), requestedSources: context.requestedSources.join(","), + cacheHit: isCacheHit ? "true" : "false", + cacheMatchedBy: context.cacheMatchedBy || "none", + cacheAction: context.cacheAction || "auto", }, }, () => @@ -316,15 +336,24 @@ export function createLangfuseCallback( } try { + const isCacheHit = Boolean(context.cacheHit); return new CallbackHandler({ sessionId: context.sessionId, version: APP_VERSION, - tags: ["workflow:research", "surface:sse"], + tags: [ + "workflow:research", + "surface:sse", + isCacheHit ? "cache:hit" : "cache:miss", + ], traceMetadata: { researchRunId: context.researchRunId, companyId: context.companyId, + companyIdHash: hashCompanyIdentifier(context.companyId), requestedSources: context.requestedSources, appVersion: APP_VERSION, + cacheHit: isCacheHit, + cacheMatchedBy: context.cacheMatchedBy || "none", + cacheAction: context.cacheAction || "auto", }, }); } catch (err) { diff --git a/tests/unit/langfuse-observability.test.ts b/tests/unit/langfuse-observability.test.ts index 4915716..b495ad6 100644 --- a/tests/unit/langfuse-observability.test.ts +++ b/tests/unit/langfuse-observability.test.ts @@ -51,6 +51,7 @@ import { flushLangfuse, initOpenTelemetry, traceResearch, + hashCompanyIdentifier, } from "@/observability/langfuse"; import type { SourceExecutionResult } from "@/lib/types"; import * as langfuseObservability from "@/observability/langfuse"; @@ -294,4 +295,46 @@ describe("Langfuse Observability & Privacy Minimization", () => { ]); expect(clientMocks.flush).toHaveBeenCalledOnce(); }); + + it("hashes company identifier deterministically with salt", () => { + vi.stubEnv("LANGFUSE_SALT", "test-secret-salt"); + const hash1 = hashCompanyIdentifier("0101248141"); + const hash2 = hashCompanyIdentifier("0101248141"); + const hashDiff = hashCompanyIdentifier("0101245486"); + + expect(hash1).toBe(hash2); + expect(hash1).not.toBe(hashDiff); + expect(hash1).toHaveLength(64); // SHA-256 hex length + }); + + it("includes cache metadata in trace attributes", async () => { + vi.stubEnv("LANGFUSE_ENABLED", "true"); + vi.stubEnv("LANGFUSE_PUBLIC_KEY", "pk-test"); + vi.stubEnv("LANGFUSE_SECRET_KEY", "sk-test"); + + await traceResearch( + { + researchRunId: "run-cache-hit", + companyId: "comp-fpt", + requestedSources: [], + cacheHit: true, + cacheMatchedBy: "tax_id", + cacheAction: "auto", + }, + async () => undefined + ); + + expect(tracingMocks.propagateAttributes).toHaveBeenCalledWith( + expect.objectContaining({ + tags: expect.arrayContaining(["cache:hit"]), + metadata: expect.objectContaining({ + cacheHit: "true", + cacheMatchedBy: "tax_id", + cacheAction: "auto", + companyIdHash: expect.any(String), + }), + }), + expect.any(Function) + ); + }); }); diff --git a/tests/unit/research-cache-route.test.ts b/tests/unit/research-cache-route.test.ts index 97bbd77..9708924 100644 --- a/tests/unit/research-cache-route.test.ts +++ b/tests/unit/research-cache-route.test.ts @@ -288,4 +288,56 @@ describe("API Route - /api/research Cache Read-Through", () => { expect(mockSearch).toHaveBeenCalled(); expect(body).toContain("event: research:start"); }); + + it("returns HTTP 400 when request body has invalid JSON", async () => { + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "invalid-json", + }); + + const response = await POST(req); + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.error).toContain("Invalid JSON"); + }); + + it("returns HTTP 400 when input validation fails", async () => { + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "", + }, + }), + }); + + const response = await POST(req); + expect(response.status).toBe(400); + const body = await response.json(); + expect(body.error).toBe("Validation failed"); + }); + + it("returns identity_conflict on invalid refresh company ID", async () => { + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Vingroup", + }, + cache: { + action: "refresh", + companyId: "nonexistent-id", + }, + }), + }); + + const response = await POST(req); + const body = await response.text(); + + expect(body).toContain("event: error"); + expect(body).toContain("identity_conflict"); + }); }); From 760140ed21cf54ca713b9c0caee701abecb0ed50 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:14:03 +0700 Subject: [PATCH 09/17] refactor(ui): enhance accessibility semantics and unmount lifecycle safety --- src/app/components/cache-suggestions.tsx | 13 ++++++++----- src/app/hooks/use-research.ts | 8 +++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/app/components/cache-suggestions.tsx b/src/app/components/cache-suggestions.tsx index a68b727..b2b1d22 100644 --- a/src/app/components/cache-suggestions.tsx +++ b/src/app/components/cache-suggestions.tsx @@ -16,16 +16,19 @@ export function CacheSuggestions({ if (!suggestions || suggestions.length === 0) return null; return ( -

- Tìm thấy kết quả trong bộ nhớ đệm + Tìm thấy kết quả trong bộ nhớ đệm

-

+

Chọn một hồ sơ có sẵn để tải ngay dữ liệu hoặc tiếp tục nghiên cứu mới toàn diện.

@@ -77,9 +80,9 @@ export function CacheSuggestions({ className="btn-secondary text-xs px-4 py-2 flex items-center gap-2" data-testid="bypass-button" > - 🔍 Nghiên cứu mới (Bỏ qua cache) + Nghiên cứu mới (Bỏ qua cache)
-
+ ); } diff --git a/src/app/hooks/use-research.ts b/src/app/hooks/use-research.ts index ecaac59..85fa3ed 100644 --- a/src/app/hooks/use-research.ts +++ b/src/app/hooks/use-research.ts @@ -1,6 +1,6 @@ "use client"; -import { useState, useCallback, useRef } from "react"; +import { useState, useCallback, useRef, useEffect } from "react"; import type { CompanyInput, CompanyProfile, @@ -264,6 +264,12 @@ export function useResearch() { setState(INITIAL_STATE); }, []); + useEffect(() => { + return () => { + abortRef.current?.abort(); + }; + }, []); + return { state, research, From ee4b9b8c64c9c42f903f16048c64b33d7a57f403 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:17:46 +0700 Subject: [PATCH 10/17] feat(observability): add fingerprintCacheKey and updateResearchCacheOutcome telemetry --- src/observability/langfuse.ts | 45 +++++++++++++++++++++++ tests/unit/langfuse-observability.test.ts | 40 ++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/observability/langfuse.ts b/src/observability/langfuse.ts index fd0bca7..93cf34d 100644 --- a/src/observability/langfuse.ts +++ b/src/observability/langfuse.ts @@ -53,6 +53,51 @@ export function hashCompanyIdentifier(identifier: string): string { return crypto.createHmac("sha256", salt).update(identifier).digest("hex"); } +export function fingerprintCacheKey( + keyType: "tax_id" | "domain", + value: string, + secret: string | undefined = process.env.LANGFUSE_SALT || process.env.CACHE_KEY_HMAC_SECRET +): string | undefined { + if (!secret || !value) return undefined; + return crypto.createHmac("sha256", secret).update(`${keyType}:${value}`).digest("hex"); +} + +export interface ResearchCacheTelemetry { + cacheOutcome: + | "hit" + | "miss" + | "suggestions" + | "refresh" + | "bypass" + | "conflict" + | "invalid"; + matchedBy?: "tax_id" | "domain" | "normalized_name" | "selected" | "user_selection"; + companyId?: string; + version?: number; + lastSyncedAt?: string; + lookupDurationMs?: number; + conflictingCompanyIds?: string[]; + keyType?: "tax_id" | "domain"; + keyFingerprint?: string; +} + +export function updateResearchCacheOutcome( + telemetry: ResearchCacheTelemetry, +): void { + if (!isLangfuseEnabled()) return; + updateActiveObservation({ + output: { + cacheOutcome: telemetry.cacheOutcome, + matchedBy: telemetry.matchedBy, + version: telemetry.version, + lastSyncedAt: telemetry.lastSyncedAt, + lookupDurationMs: telemetry.lookupDurationMs, + keyType: telemetry.keyType, + keyFingerprint: telemetry.keyFingerprint, + }, + }); +} + export interface DeterministicScore { name: string; value: number | string; diff --git a/tests/unit/langfuse-observability.test.ts b/tests/unit/langfuse-observability.test.ts index b495ad6..dc51a82 100644 --- a/tests/unit/langfuse-observability.test.ts +++ b/tests/unit/langfuse-observability.test.ts @@ -52,6 +52,8 @@ import { initOpenTelemetry, traceResearch, hashCompanyIdentifier, + fingerprintCacheKey, + updateResearchCacheOutcome, } from "@/observability/langfuse"; import type { SourceExecutionResult } from "@/lib/types"; import * as langfuseObservability from "@/observability/langfuse"; @@ -307,6 +309,44 @@ describe("Langfuse Observability & Privacy Minimization", () => { expect(hash1).toHaveLength(64); // SHA-256 hex length }); + it("fingerprints low-entropy tax IDs with a keyed HMAC", () => { + const first = fingerprintCacheKey("tax_id", "0101248141", "secret-a"); + const second = fingerprintCacheKey("tax_id", "0101248141", "secret-b"); + + expect(first).toMatch(/^[a-f0-9]{64}$/); + expect(first).not.toContain("0101248141"); + expect(second).not.toBe(first); + expect(fingerprintCacheKey("tax_id", "0101248141", undefined)).toBeUndefined(); + }); + + it("updates active observation with research cache telemetry", () => { + vi.stubEnv("LANGFUSE_ENABLED", "true"); + vi.stubEnv("LANGFUSE_PUBLIC_KEY", "pk-test"); + vi.stubEnv("LANGFUSE_SECRET_KEY", "sk-test"); + + updateResearchCacheOutcome({ + cacheOutcome: "hit", + matchedBy: "tax_id", + version: 1, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + lookupDurationMs: 42, + keyType: "tax_id", + keyFingerprint: "fingerprint-123", + }); + + expect(tracingMocks.updateActiveObservation).toHaveBeenCalledWith({ + output: { + cacheOutcome: "hit", + matchedBy: "tax_id", + version: 1, + lastSyncedAt: "2026-08-26T08:00:00.000Z", + lookupDurationMs: 42, + keyType: "tax_id", + keyFingerprint: "fingerprint-123", + }, + }); + }); + it("includes cache metadata in trace attributes", async () => { vi.stubEnv("LANGFUSE_ENABLED", "true"); vi.stubEnv("LANGFUSE_PUBLIC_KEY", "pk-test"); From d80e24f322512a294fa7afc9c9f1ad36f717ada9 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:17:50 +0700 Subject: [PATCH 11/17] feat(ui): add buildResearchRequest helper and test coverage --- src/app/hooks/use-research.ts | 12 ++++++++---- tests/unit/use-research-reducer.test.ts | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/app/hooks/use-research.ts b/src/app/hooks/use-research.ts index 85fa3ed..a47ee68 100644 --- a/src/app/hooks/use-research.ts +++ b/src/app/hooks/use-research.ts @@ -53,6 +53,13 @@ export const INITIAL_STATE: ResearchState = { cacheHit: null, }; +export function buildResearchRequest( + input: CompanyInput, + cache?: ResearchRequest["cache"] +): ResearchRequest { + return cache ? { input, cache } : { input }; +} + export function reduceResearchEvent( state: ResearchState, event: StreamEvent @@ -174,10 +181,7 @@ export function useResearch() { }); try { - const payload: ResearchRequest = { - input, - cache, - }; + const payload = buildResearchRequest(input, cache); const response = await fetch("/api/research", { method: "POST", diff --git a/tests/unit/use-research-reducer.test.ts b/tests/unit/use-research-reducer.test.ts index 4e1f0db..527392f 100644 --- a/tests/unit/use-research-reducer.test.ts +++ b/tests/unit/use-research-reducer.test.ts @@ -1,11 +1,26 @@ import { describe, it, expect } from "vitest"; import { reduceResearchEvent, + buildResearchRequest, INITIAL_STATE, type ResearchState, } from "@/app/hooks/use-research"; import type { StreamEvent, CompanyProfile, AnalysisReport, ProfileDiff } from "@/lib/types"; +describe("useResearch request builder - buildResearchRequest", () => { + it("builds default, selected, bypass, and refresh requests", () => { + const input = { name: "FPT" }; + + expect(buildResearchRequest(input)).toEqual({ input }); + expect(buildResearchRequest(input, { action: "select", companyId: "fpt" })) + .toEqual({ input, cache: { action: "select", companyId: "fpt" } }); + expect(buildResearchRequest(input, { action: "bypass" })) + .toEqual({ input, cache: { action: "bypass" } }); + expect(buildResearchRequest(input, { action: "refresh", companyId: "fpt" })) + .toEqual({ input, cache: { action: "refresh", companyId: "fpt" } }); + }); +}); + describe("useResearch pure reducer - reduceResearchEvent", () => { const dummyProfile: CompanyProfile = { id: "comp-1", From e8709ee3d84c96473e6ae6fb9d649e3c6f834862 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:22:52 +0700 Subject: [PATCH 12/17] fix(cache): enforce cache preflight, recoverable cache_invalid notice, and sync package-lock.json --- package-lock.json | 208 +++++++++++ src/app/api/research/route.ts | 463 +++++++++++++----------- src/app/hooks/use-research.ts | 23 +- src/app/page.tsx | 13 + supabase/.gitignore | 8 + tests/unit/research-cache-route.test.ts | 48 ++- tests/unit/use-research-reducer.test.ts | 20 + 7 files changed, 554 insertions(+), 229 deletions(-) create mode 100644 supabase/.gitignore diff --git a/package-lock.json b/package-lock.json index 75a013b..9a4a4a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@typescript/native": "npm:typescript@7.0.2", "eslint": "^9", "eslint-config-next": "16.3.2", + "supabase": "2.115.0", "tailwindcss": "^4", "typescript": "npm:@typescript/typescript6@6.0.2", "vitest": "^4.1.11" @@ -306,6 +307,21 @@ "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", "license": "MIT" }, + "node_modules/@ecies/ciphers": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.6.tgz", + "integrity": "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==", + "dev": true, + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2.7.10", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -1621,6 +1637,22 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -2738,6 +2770,130 @@ "node": ">=22.0.0" } }, + "node_modules/@supabase/cli-darwin-arm64": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-darwin-arm64/-/cli-darwin-arm64-2.115.0.tgz", + "integrity": "sha512-yUNw1KG+fyuBqBGvFT8ASC7aAkFkx2Kx+qPjUTW50ttiKKgg8D/eMSCoSjQuaAbL0vafw98bHEZ2dAfdyYyXMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@supabase/cli-darwin-x64": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-darwin-x64/-/cli-darwin-x64-2.115.0.tgz", + "integrity": "sha512-e4bbWADYcjSjXgSSErreoqyEeEjrFQunxCYummUoiGdVanck/itAIFwhtRykKpBtoKqCYh4CFxUiDxOFMBdNHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@supabase/cli-linux-arm64": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-linux-arm64/-/cli-linux-arm64-2.115.0.tgz", + "integrity": "sha512-JBcnnFuVekMR9+EOCcup1QihW+CHMBWcL/+N1Uz4HB6leX8d894VQ+sX0pFgbXLqnpGcc35IiXKwjLz4FCb8Dw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@supabase/cli-linux-arm64-musl": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.115.0.tgz", + "integrity": "sha512-2OCzD4qZx8RFbW2vfOlXpup+1UnVqSauUsSGH4mKiUIveMD/UyMI6Md8CLKLszwV2XwM+6bZG6w7d7Ems9vxCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@supabase/cli-linux-x64": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-linux-x64/-/cli-linux-x64-2.115.0.tgz", + "integrity": "sha512-ZvZ5QbPB3cvenEam6TDgngWPPm9GBO5m/5GYCOsqIfx2Gz5++WsEhlypWy9U4gTFae7ENtih0RqPzgiyWDKE+w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@supabase/cli-linux-x64-musl": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-linux-x64-musl/-/cli-linux-x64-musl-2.115.0.tgz", + "integrity": "sha512-t36QEEQxy0AsOn0rr1L8aEzZKYS+kRH4fvYr5KdB+rHA1x39A1dUeOCTqCucCr0V5oQ3+odJLlO6tinNczHKMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@supabase/cli-windows-arm64": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-windows-arm64/-/cli-windows-arm64-2.115.0.tgz", + "integrity": "sha512-MkYbWrNZXpWZxwglVaTexj+w6EWYUi34c7Y9SRGbcQBC/infteM/8zhVVRUmO3ltSEqnJCacHowLPISXIF6g/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@supabase/cli-windows-x64": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/@supabase/cli-windows-x64/-/cli-windows-x64-2.115.0.tgz", + "integrity": "sha512-jRXsJjbw/h0ssSpFQeClTTO2wtMw0YYS1yMzN2f/px4DehigBZNxSFB4thOtzmIJoisInm1gYV5pQJZla1Ytpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@supabase/functions-js": { "version": "2.112.3", "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.112.3.tgz", @@ -5148,6 +5304,24 @@ "node": ">= 0.4" } }, + "node_modules/eciesjs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.5.0.tgz", + "integrity": "sha512-s0J9SEVYAEPg7J63GFMApLYzPH9VNIQIyC6s15JpnqVc0TqcKWdbgFlnAweEBRyMmko2dcs2sfC83Hj4J43tuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ecies/ciphers": "^0.2.6", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0" + }, + "engines": { + "bun": ">=1", + "deno": ">=2.7.10", + "node": ">=16" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.412", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz", @@ -6957,6 +7131,16 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.10.tgz", + "integrity": "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tiktoken": { "version": "1.0.21", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", @@ -9003,6 +9187,30 @@ } } }, + "node_modules/supabase": { + "version": "2.115.0", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.115.0.tgz", + "integrity": "sha512-8fL9vOd6jOntmU8N5DVlHGE2GWR1r57ulsrOzSyO6IRYq5QMyKie8T8DH+hb+caGhYUVJLvmpY7XYwic60Uafg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eciesjs": "^0.5.0", + "jose": "^6.2.8" + }, + "bin": { + "supabase": "dist/supabase.js" + }, + "optionalDependencies": { + "@supabase/cli-darwin-arm64": "2.115.0", + "@supabase/cli-darwin-x64": "2.115.0", + "@supabase/cli-linux-arm64": "2.115.0", + "@supabase/cli-linux-arm64-musl": "2.115.0", + "@supabase/cli-linux-x64": "2.115.0", + "@supabase/cli-linux-x64-musl": "2.115.0", + "@supabase/cli-windows-arm64": "2.115.0", + "@supabase/cli-windows-x64": "2.115.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/src/app/api/research/route.ts b/src/app/api/research/route.ts index a9b0d86..18bfdf0 100644 --- a/src/app/api/research/route.ts +++ b/src/app/api/research/route.ts @@ -11,6 +11,7 @@ import { type CompanyProfile, type StreamEvent, type SourceName, + type ResearchSnapshot, } from "@/lib/types"; import { createSSEStream, type SSEWriter } from "@/lib/stream"; import { @@ -25,8 +26,11 @@ import { createResearchCache, normalizeCompanyIdentity, IdentityConflictError, + InvalidCacheSelectionError, + CacheUnavailableError, type NormalizedCompanyIdentity, type ResearchCache, + type CacheResolution, } from "@/modules/cache"; import { createProfileModule } from "@/modules/profile"; import { createAnalystModule } from "@/modules/analyst"; @@ -37,6 +41,7 @@ import { emitResearchScores, flushLangfuse, traceResearch, + updateResearchCacheOutcome, type ResearchTraceContext, updateResearchObservationOutcome, updateResearchTraceOutcome, @@ -46,12 +51,13 @@ export const runtime = "nodejs"; export const maxDuration = 300; export async function POST(req: NextRequest) { + // 1. JSON parsing and schema validation let body: unknown; try { body = await req.json(); } catch { return new Response( - JSON.stringify({ error: "Invalid JSON body" }), + JSON.stringify({ error: "Invalid JSON in request body" }), { status: 400, headers: { "Content-Type": "application/json" } } ); } @@ -69,12 +75,118 @@ export async function POST(req: NextRequest) { const { input, cache } = parseResult.data; const action = cache?.action ?? "auto"; - const selectedCompanyId = cache?.action === "select" ? cache.companyId : undefined; - const refreshCompanyId = cache?.action === "refresh" ? cache.companyId : undefined; + const selectedCompanyId = + cache?.action === "select" ? cache.companyId : undefined; + const refreshCompanyId = + cache?.action === "refresh" ? cache.companyId : undefined; const storage = createStorageAdapter(); const researchCache = createResearchCache(storage); + // 2. Preflight Cache Resolution before opening SSE stream + let autoResolution: CacheResolution | null = null; + let selectedSnapshot: ResearchSnapshot | null = null; + let refreshSnapshot: ResearchSnapshot | null = null; + + try { + if (action === "auto") { + autoResolution = await researchCache.lookup(input, { + signal: req.signal, + }); + if (autoResolution.kind === "conflict") { + return new Response( + JSON.stringify({ + error: "Thông tin định danh công ty mâu thuẫn.", + code: "identity_conflict", + }), + { status: 409, headers: { "Content-Type": "application/json" } } + ); + } + } else if (action === "select") { + if (!selectedCompanyId) { + return new Response( + JSON.stringify({ + error: "Thiếu mã định danh công ty được chọn.", + code: "invalid_cache_selection", + }), + { status: 400, headers: { "Content-Type": "application/json" } } + ); + } + selectedSnapshot = await researchCache.select(input, selectedCompanyId, { + signal: req.signal, + }); + } else if (action === "refresh") { + if (!refreshCompanyId) { + return new Response( + JSON.stringify({ + error: "Thiếu mã định danh công ty cần làm mới.", + code: "identity_conflict", + }), + { status: 409, headers: { "Content-Type": "application/json" } } + ); + } + refreshSnapshot = await researchCache.prepareRefresh( + input, + refreshCompanyId, + { + signal: req.signal, + } + ); + } else if (action === "bypass") { + const norm = normalizeCompanyIdentity(input); + if (norm.taxId) { + const candidates = await storage.findIdentityCandidates(norm, { + signal: req.signal, + }); + const taxMatch = candidates.find((c) => c.taxId === norm.taxId); + if ( + taxMatch && + norm.domain && + taxMatch.domain && + norm.domain !== taxMatch.domain + ) { + return new Response( + JSON.stringify({ + error: + "Không thể bỏ qua cache khi thông tin định danh mâu thuẫn với MST đã đăng ký.", + code: "identity_conflict", + }), + { status: 409, headers: { "Content-Type": "application/json" } } + ); + } + } + } + } catch (err) { + if (err instanceof IdentityConflictError) { + return new Response( + JSON.stringify({ error: err.message, code: "identity_conflict" }), + { status: 409, headers: { "Content-Type": "application/json" } } + ); + } + if (err instanceof InvalidCacheSelectionError) { + return new Response( + JSON.stringify({ + error: err.message, + code: "invalid_cache_selection", + }), + { status: 400, headers: { "Content-Type": "application/json" } } + ); + } + if (err instanceof CacheUnavailableError) { + return new Response( + JSON.stringify({ error: err.message, code: "cache_unavailable" }), + { status: 503, headers: { "Content-Type": "application/json" } } + ); + } + const message = + err instanceof Error ? err.message : "Cache preflight failed"; + return new Response( + JSON.stringify({ error: message, code: "cache_unavailable" }), + { status: 503, headers: { "Content-Type": "application/json" } } + ); + } + + // 3. Open SSE Stream const { stream, writer } = createSSEStream(); const researchRunId = crypto.randomUUID(); @@ -88,29 +200,14 @@ export async function POST(req: NextRequest) { void (async () => { try { - // 1. Check Cache / Handle Action - if (action === "auto") { - let resolution; - try { - resolution = await researchCache.lookup(input, { signal: controller.signal }); - } catch (err) { - const message = - err instanceof Error ? err.message : "Cache lookup failed"; - writer.write({ - event: "error", - data: { message, code: "cache_unavailable" }, - } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); - return; - } - - if (resolution.kind === "hit") { + if (action === "auto" && autoResolution) { + if (autoResolution.kind === "hit") { const traceContext: ResearchTraceContext = { researchRunId, - companyId: resolution.snapshot.profile.id, + companyId: autoResolution.snapshot.profile.id, requestedSources: [], cacheHit: true, - cacheMatchedBy: resolution.matchedBy, + cacheMatchedBy: autoResolution.matchedBy, cacheAction: "auto", }; await traceResearch(traceContext, async (traceId) => { @@ -118,7 +215,8 @@ export async function POST(req: NextRequest) { sourceResults: [], hasProfile: true, hasAnalysis: true, - overallConfidence: resolution.snapshot.profile.overallConfidence, + overallConfidence: + autoResolution.snapshot.profile.overallConfidence, outcome: "complete", }); }); @@ -126,174 +224,122 @@ export async function POST(req: NextRequest) { writer.write({ event: "cache:hit", data: { - companyId: resolution.snapshot.profile.id, - matchedBy: resolution.matchedBy, - version: resolution.snapshot.profile.version, - lastSyncedAt: resolution.snapshot.lastSyncedAt, + companyId: autoResolution.snapshot.profile.id, + matchedBy: autoResolution.matchedBy, + version: autoResolution.snapshot.profile.version, + lastSyncedAt: autoResolution.snapshot.lastSyncedAt, }, } as StreamEvent); writer.write({ event: "profile:ready", - data: { profile: resolution.snapshot.profile }, + data: { profile: autoResolution.snapshot.profile }, } as StreamEvent); - if (resolution.snapshot.diff) { + if (autoResolution.snapshot.diff) { writer.write({ event: "diff:ready", - data: { diff: resolution.snapshot.diff }, + data: { diff: autoResolution.snapshot.diff }, } as StreamEvent); } writer.write({ event: "analysis:ready", - data: { report: resolution.snapshot.report }, + data: { report: autoResolution.snapshot.report }, } as StreamEvent); writer.write({ event: "done", data: {} } as StreamEvent); return; } - if (resolution.kind === "suggestions") { + if (autoResolution.kind === "suggestions") { writer.write({ event: "cache:suggestions", - data: { suggestions: resolution.suggestions }, - } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); - return; - } - - if (resolution.kind === "conflict") { - writer.write({ - event: "error", - data: { - message: "Thông tin định danh công ty mâu thuẫn.", - code: "identity_conflict", - }, + data: { suggestions: autoResolution.suggestions }, } as StreamEvent); writer.write({ event: "done", data: {} } as StreamEvent); return; } - // Miss -> proceed with live research - const miss = await researchCache.resolveMiss(input, { signal: controller.signal }); - await executeLiveWorkflow({ - input, - companyId: miss.companyId, - identity: miss.identity, - existingProfile: null, - researchRunId, - controller, - writer, - researchCache, - }); - return; - } - - if (action === "select") { - if (!selectedCompanyId) { - writer.write({ - event: "error", - data: { - message: "Thiếu mã định danh công ty được chọn.", - code: "invalid_cache_selection", - }, - } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); - return; - } + if (autoResolution.kind === "miss") { + if (autoResolution.cacheInvalid) { + writer.write({ + event: "error", + data: { + message: + "Dữ liệu cache không hợp lệ, đang tiến hành nghiên cứu mới.", + code: "cache_invalid", + }, + } as StreamEvent); + updateResearchCacheOutcome({ cacheOutcome: "invalid" }); + } - try { - const snapshot = await researchCache.select(input, selectedCompanyId, { + const miss = await researchCache.resolveMiss(input, { signal: controller.signal, }); - - const traceContext: ResearchTraceContext = { + await executeLiveWorkflow({ + input, + companyId: miss.companyId, + identity: miss.identity, + existingProfile: null, researchRunId, - companyId: snapshot.profile.id, - requestedSources: [], - cacheHit: true, - cacheMatchedBy: "user_selection", - cacheAction: "select", - }; - await traceResearch(traceContext, async (traceId) => { - await emitResearchScores(traceId, { - sourceResults: [], - hasProfile: true, - hasAnalysis: true, - overallConfidence: snapshot.profile.overallConfidence, - outcome: "complete", - }); + controller, + writer, + researchCache, }); - - writer.write({ - event: "cache:hit", - data: { - companyId: snapshot.profile.id, - matchedBy: "user_selection", - version: snapshot.profile.version, - lastSyncedAt: snapshot.lastSyncedAt, - }, - } as StreamEvent); - writer.write({ - event: "profile:ready", - data: { profile: snapshot.profile }, - } as StreamEvent); - if (snapshot.diff) { - writer.write({ - event: "diff:ready", - data: { diff: snapshot.diff }, - } as StreamEvent); - } - writer.write({ - event: "analysis:ready", - data: { report: snapshot.report }, - } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); - return; - } catch (err) { - const message = - err instanceof Error ? err.message : "Invalid cache selection"; - writer.write({ - event: "error", - data: { message, code: "invalid_cache_selection" }, - } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); return; } } - if (action === "refresh") { - if (!refreshCompanyId) { - writer.write({ - event: "error", - data: { - message: "Thiếu mã định danh công ty cần làm mới.", - code: "identity_conflict", - }, - } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); - return; - } - - let snapshot; - try { - snapshot = await researchCache.prepareRefresh(input, refreshCompanyId, { - signal: controller.signal, + if (action === "select" && selectedSnapshot) { + const traceContext: ResearchTraceContext = { + researchRunId, + companyId: selectedSnapshot.profile.id, + requestedSources: [], + cacheHit: true, + cacheMatchedBy: "user_selection", + cacheAction: "select", + }; + await traceResearch(traceContext, async (traceId) => { + await emitResearchScores(traceId, { + sourceResults: [], + hasProfile: true, + hasAnalysis: true, + overallConfidence: selectedSnapshot.profile.overallConfidence, + outcome: "complete", }); - } catch (err) { - const message = - err instanceof Error ? err.message : "Identity conflict on refresh"; + }); + + writer.write({ + event: "cache:hit", + data: { + companyId: selectedSnapshot.profile.id, + matchedBy: "user_selection", + version: selectedSnapshot.profile.version, + lastSyncedAt: selectedSnapshot.lastSyncedAt, + }, + } as StreamEvent); + writer.write({ + event: "profile:ready", + data: { profile: selectedSnapshot.profile }, + } as StreamEvent); + if (selectedSnapshot.diff) { writer.write({ - event: "error", - data: { message, code: "identity_conflict" }, + event: "diff:ready", + data: { diff: selectedSnapshot.diff }, } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); - return; } + writer.write({ + event: "analysis:ready", + data: { report: selectedSnapshot.report }, + } as StreamEvent); + writer.write({ event: "done", data: {} } as StreamEvent); + return; + } + if (action === "refresh" && refreshSnapshot) { const identity = normalizeCompanyIdentity(input); await executeLiveWorkflow({ input, - companyId: refreshCompanyId, + companyId: refreshCompanyId!, identity, - existingProfile: snapshot.profile, + existingProfile: refreshSnapshot.profile, researchRunId, controller, writer, @@ -303,7 +349,9 @@ export async function POST(req: NextRequest) { } if (action === "bypass") { - const miss = await researchCache.resolveMiss(input, { signal: controller.signal }); + const miss = await researchCache.resolveMiss(input, { + signal: controller.signal, + }); await executeLiveWorkflow({ input, companyId: miss.companyId, @@ -385,7 +433,7 @@ async function executeLiveWorkflow({ "website", "news", "registry", - ...(input.linkedinUrl ? ["linkedin" as SourceName] : []), + ...(input.linkedinUrl ? [("linkedin" as SourceName)] : []), ], cacheHit: false, cacheMatchedBy: "none", @@ -411,78 +459,63 @@ async function executeLiveWorkflow({ hasProfile: Boolean(state.profile), hasAnalysis: Boolean(state.report), overallConfidence: state.profile?.overallConfidence ?? 0, - outcome: state.outcome === "running" ? "failed" : state.outcome, + outcome: state.outcome === "running" ? "partial" : state.outcome, }); }, })) { writer.write(event); } + } catch (err) { + if ((err as Error).name === "AbortError") { + updateResearchObservationOutcome("cancelled"); + return; + } + updateResearchObservationOutcome("failed"); + throw err; + } + }); - // Persist snapshot & emit final events - if (finalState?.profile && finalState?.report) { - try { - await researchCache.persist( - identity, - { - profile: finalState.profile, - report: finalState.report, - diff: finalState.diff, - }, - { signal: controller.signal } - ); + // Post-workflow atomic cache persistence and event emission + if (finalState) { + const s = finalState as ResearchWorkflowState; + if (s.profile && s.report) { + try { + await researchCache.persist( + identity, + { + profile: s.profile, + report: s.report, + diff: s.diff ?? null, + }, + { signal: controller.signal } + ); + } catch (persistErr) { + console.error("Failed to persist research snapshot:", persistErr); + writer.write({ + event: "error", + data: { + message: "Không thể lưu kết quả nghiên cứu vào bộ nhớ đệm.", + code: "persist_failed", + }, + } as StreamEvent); + } - writer.write({ - event: "profile:ready", - data: { profile: finalState.profile }, - } as StreamEvent); - if (finalState.diff) { - writer.write({ - event: "diff:ready", - data: { diff: finalState.diff }, - } as StreamEvent); - } - writer.write({ - event: "analysis:ready", - data: { report: finalState.report }, - } as StreamEvent); - } catch (persistErr) { - if (persistErr instanceof IdentityConflictError) { - writer.write({ - event: "error", - data: { - message: "Hồ sơ nghiên cứu bị từ chối do mâu thuẫn định danh phát sinh.", - code: "identity_conflict", - }, - } as StreamEvent); - } else { - const message = - persistErr instanceof Error ? persistErr.message : "Failed to persist snapshot"; - writer.write({ - event: "error", - data: { message }, - } as StreamEvent); - } - } + writer.write({ + event: "profile:ready", + data: { profile: s.profile }, + } as StreamEvent); + if (s.diff) { + writer.write({ + event: "diff:ready", + data: { diff: s.diff }, + } as StreamEvent); } - writer.write({ event: "done", data: {} } as StreamEvent); - } catch (err) { - const message = - err instanceof Error ? err.message : "Internal workflow error"; - updateResearchObservationOutcome( - controller.signal.aborted ? "cancelled" : "failed", - ); - await emitResearchScores(traceId, { - sourceResults: [], - hasProfile: false, - hasAnalysis: false, - overallConfidence: 0, - outcome: "failed", - }); writer.write({ - event: "error", - data: { message }, + event: "analysis:ready", + data: { report: s.report }, } as StreamEvent); - writer.write({ event: "done", data: {} } as StreamEvent); } - }); + } + + writer.write({ event: "done", data: {} } as StreamEvent); } diff --git a/src/app/hooks/use-research.ts b/src/app/hooks/use-research.ts index a47ee68..d11f4af 100644 --- a/src/app/hooks/use-research.ts +++ b/src/app/hooks/use-research.ts @@ -26,6 +26,7 @@ export interface ResearchState { report: AnalysisReport | null; error: string | null; errorCode?: ResearchErrorCode; + notice?: string | null; suggestions: CacheSuggestion[]; cacheHit: { matchedBy: CacheHitMatchedBy; @@ -49,6 +50,7 @@ export const INITIAL_STATE: ResearchState = { diff: null, report: null, error: null, + notice: null, suggestions: [], cacheHit: null, }; @@ -143,6 +145,12 @@ export function reduceResearchEvent( }; case "error": + if (event.data.code === "cache_invalid") { + return { + ...state, + notice: event.data.message, + }; + } return { ...state, error: event.data.message, @@ -191,10 +199,17 @@ export function useResearch() { }); if (!response.ok) { - const errBody = await response.json().catch(() => ({})); - throw new Error( - (errBody as { error?: string }).error ?? `HTTP ${response.status}` - ); + const errBody = (await response.json().catch(() => ({}))) as { + error?: string; + code?: ResearchErrorCode; + }; + setState((prev) => ({ + ...prev, + status: "error", + error: errBody.error ?? `HTTP ${response.status}`, + errorCode: errBody.code, + })); + return; } const reader = response.body?.getReader(); diff --git a/src/app/page.tsx b/src/app/page.tsx index 0068d46..0f33b61 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -120,6 +120,19 @@ export default function HomePage() { /> )} + {state.notice && ( +
+
+ ⚠️ +

{state.notice}

+
+
+ )} + {state.error && (

Lỗi

diff --git a/supabase/.gitignore b/supabase/.gitignore new file mode 100644 index 0000000..ad9264f --- /dev/null +++ b/supabase/.gitignore @@ -0,0 +1,8 @@ +# Supabase +.branches +.temp + +# dotenvx +.env.keys +.env.local +.env.*.local diff --git a/tests/unit/research-cache-route.test.ts b/tests/unit/research-cache-route.test.ts index 9708924..6da423b 100644 --- a/tests/unit/research-cache-route.test.ts +++ b/tests/unit/research-cache-route.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; import { POST } from "@/app/api/research/route"; import { MemoryStorageAdapter } from "@/adapters/storage/memory"; +import { CacheInvalidError } from "@/modules/cache"; import type { CompanyProfile, AnalysisReport } from "@/lib/types"; const mockLLM = vi.fn(); @@ -73,6 +74,7 @@ vi.mock("@/observability/langfuse", () => ({ task("mock-trace-id"), updateResearchObservationOutcome: vi.fn(), updateResearchTraceOutcome: vi.fn(), + updateResearchCacheOutcome: vi.fn(), })); describe("API Route - /api/research Cache Read-Through", () => { @@ -192,10 +194,9 @@ describe("API Route - /api/research Cache Read-Through", () => { }); const response = await POST(req); - const body = await response.text(); - - expect(body).toContain("event: error"); - expect(body).toContain("identity_conflict"); + expect(response.status).toBe(409); + const json = await response.json(); + expect(json.code).toBe("identity_conflict"); }); it("resolves cache selection when action is select", async () => { @@ -252,10 +253,9 @@ describe("API Route - /api/research Cache Read-Through", () => { }); const response = await POST(req); - const body = await response.text(); - - expect(body).toContain("event: error"); - expect(body).toContain("invalid_cache_selection"); + expect(response.status).toBe(400); + const json = await response.json(); + expect(json.code).toBe("invalid_cache_selection"); }); it("bypasses cache when action is bypass", async () => { @@ -335,9 +335,37 @@ describe("API Route - /api/research Cache Read-Through", () => { }); const response = await POST(req); + expect(response.status).toBe(409); + const json = await response.json(); + expect(json.code).toBe("identity_conflict"); + }); + + it("emits non-terminal cache_invalid notice and proceeds to live workflow on corrupt snapshot", async () => { + await storage.resolveOrCreateIdentity( + { taxId: "0101248141", domain: "fpt.com.vn", name: "công ty cp fpt" }, + "comp-fpt" + ); + vi.spyOn(storage, "getLatestCompleteSnapshot").mockRejectedValueOnce( + new CacheInvalidError("Corrupted JSONB") + ); + + const req = new NextRequest("http://localhost:3000/api/research", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + input: { + name: "Công ty CP FPT", + taxId: "0101248141", + }, + }), + }); + + const response = await POST(req); + expect(response.status).toBe(200); const body = await response.text(); - expect(body).toContain("event: error"); - expect(body).toContain("identity_conflict"); + expect(body).toContain("cache_invalid"); + expect(body).toContain("event: research:start"); + expect(mockSearch).toHaveBeenCalled(); }); }); diff --git a/tests/unit/use-research-reducer.test.ts b/tests/unit/use-research-reducer.test.ts index 527392f..7d3fe6e 100644 --- a/tests/unit/use-research-reducer.test.ts +++ b/tests/unit/use-research-reducer.test.ts @@ -205,6 +205,26 @@ describe("useResearch pure reducer - reduceResearchEvent", () => { expect(nextState.errorCode).toBe("identity_conflict"); }); + it("records notice without setting error status on cache_invalid error event", () => { + const researchingState: ResearchState = { + ...INITIAL_STATE, + status: "researching", + }; + + const event: StreamEvent = { + event: "error", + data: { + message: "Dữ liệu cache không hợp lệ, đang tiến hành nghiên cứu mới.", + code: "cache_invalid", + }, + }; + + const nextState = reduceResearchEvent(researchingState, event); + expect(nextState.status).toBe("researching"); + expect(nextState.error).toBeNull(); + expect(nextState.notice).toBe("Dữ liệu cache không hợp lệ, đang tiến hành nghiên cứu mới."); + }); + it("transitions to done when profile and analysis are ready", () => { let state = reduceResearchEvent(INITIAL_STATE, { event: "profile:ready", From 4b3b4db0025e7c3b99ac305e8697c337abb16871 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:25:02 +0700 Subject: [PATCH 13/17] fix(observability): emit failed research scores on stream error --- src/app/api/research/route.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/api/research/route.ts b/src/app/api/research/route.ts index 18bfdf0..512c0ab 100644 --- a/src/app/api/research/route.ts +++ b/src/app/api/research/route.ts @@ -471,6 +471,13 @@ async function executeLiveWorkflow({ return; } updateResearchObservationOutcome("failed"); + await emitResearchScores(traceId, { + sourceResults: [], + hasProfile: false, + hasAnalysis: false, + overallConfidence: 0, + outcome: "failed", + }); throw err; } }); From edd30e0d905565dbdc198e71867d796d0828f777 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:29:35 +0700 Subject: [PATCH 14/17] fix(storage): fallback to SUPABASE_ANON_KEY and encapsulate preflight initialization --- src/adapters/storage/supabase.ts | 9 +++++++-- src/app/api/research/route.ts | 9 ++++++--- src/config/index.ts | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/adapters/storage/supabase.ts b/src/adapters/storage/supabase.ts index e154e16..74eef99 100644 --- a/src/adapters/storage/supabase.ts +++ b/src/adapters/storage/supabase.ts @@ -27,10 +27,15 @@ export class SupabaseStorageAdapter implements StorageAdapter { constructor(supabaseUrl?: string, supabaseKey?: string) { const url = supabaseUrl || process.env.SUPABASE_URL; - const key = supabaseKey || process.env.SUPABASE_SERVICE_ROLE_KEY; + const key = + supabaseKey || + process.env.SUPABASE_SERVICE_ROLE_KEY || + process.env.SUPABASE_ANON_KEY; if (!url || !key) { - throw new Error("Missing Supabase credentials: SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY"); + throw new Error( + "Missing Supabase credentials: SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY / SUPABASE_ANON_KEY" + ); } this.client = createClient(url, key, { diff --git a/src/app/api/research/route.ts b/src/app/api/research/route.ts index 512c0ab..d663410 100644 --- a/src/app/api/research/route.ts +++ b/src/app/api/research/route.ts @@ -22,6 +22,7 @@ import { createStorageAdapter, getGuards, } from "@/config"; +import type { StorageAdapter } from "@/adapters/storage/types"; import { createResearchCache, normalizeCompanyIdentity, @@ -80,15 +81,17 @@ export async function POST(req: NextRequest) { const refreshCompanyId = cache?.action === "refresh" ? cache.companyId : undefined; - const storage = createStorageAdapter(); - const researchCache = createResearchCache(storage); - // 2. Preflight Cache Resolution before opening SSE stream let autoResolution: CacheResolution | null = null; let selectedSnapshot: ResearchSnapshot | null = null; let refreshSnapshot: ResearchSnapshot | null = null; + let storage: StorageAdapter; + let researchCache: ResearchCache; try { + storage = createStorageAdapter(); + researchCache = createResearchCache(storage); + if (action === "auto") { autoResolution = await researchCache.lookup(input, { signal: req.signal, diff --git a/src/config/index.ts b/src/config/index.ts index 838737d..fad659f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -191,7 +191,7 @@ export function createStorageAdapter(): StorageAdapter { case "supabase": _storage = new SupabaseStorageAdapter( process.env.SUPABASE_URL, - process.env.SUPABASE_SERVICE_ROLE_KEY + process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_ANON_KEY ); break; default: From fb66da86d7b8777a0ac14a0fe69e8b8d4ef5fcbf Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:33:05 +0700 Subject: [PATCH 15/17] fix(storage): add graceful fallback for unmigrated Supabase schemas --- src/adapters/storage/supabase.ts | 56 +++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/adapters/storage/supabase.ts b/src/adapters/storage/supabase.ts index 74eef99..9896964 100644 --- a/src/adapters/storage/supabase.ts +++ b/src/adapters/storage/supabase.ts @@ -194,6 +194,18 @@ export class SupabaseStorageAdapter implements StorageAdapter { const { data, error } = await query; if (error) { + if ( + error.code === "PGRST202" || + error.code === "PGRST205" || + error.message.includes("schema cache") || + error.message.includes("Could not find the function") || + error.message.includes("Could not find the table") + ) { + console.warn( + "Supabase stored procedures not found in database. Operating in live research mode without cache." + ); + return []; + } throw new Error(`Failed to lookup company identities: ${error.message}`); } @@ -223,6 +235,13 @@ export class SupabaseStorageAdapter implements StorageAdapter { const { data: profileRows, error: profileError } = await profileQuery; if (profileError) { + if ( + profileError.code === "PGRST205" || + profileError.message.includes("schema cache") || + profileError.message.includes("Could not find the table") + ) { + return null; + } throw new Error(`Failed to get complete profile: ${profileError.message}`); } @@ -241,7 +260,15 @@ export class SupabaseStorageAdapter implements StorageAdapter { const { data: diffRow, error: diffError } = await diffQuery.maybeSingle(); if (diffError) { - throw new Error(`Failed to get snapshot diff: ${diffError.message}`); + if ( + diffError.code === "PGRST205" || + diffError.message.includes("schema cache") || + diffError.message.includes("Could not find the table") + ) { + // Fallback without diff + } else { + throw new Error(`Failed to get snapshot diff: ${diffError.message}`); + } } const rawSnapshot = { @@ -279,6 +306,17 @@ export class SupabaseStorageAdapter implements StorageAdapter { if (error.message.includes("identity_conflict")) { throw new IdentityConflictError(); } + if ( + error.code === "PGRST202" || + error.code === "PGRST205" || + error.message.includes("schema cache") || + error.message.includes("Could not find the function") + ) { + console.warn( + "Supabase stored procedure resolve_company_identity not found. Using candidate ID." + ); + return candidateId; + } throw new Error(`Failed to resolve company identity: ${error.message}`); } @@ -307,6 +345,22 @@ export class SupabaseStorageAdapter implements StorageAdapter { if (error.message.includes("identity_conflict")) { throw new IdentityConflictError(); } + if ( + error.code === "PGRST202" || + error.code === "PGRST205" || + error.message.includes("schema cache") || + error.message.includes("Could not find the function") + ) { + console.warn( + "Supabase stored procedure persist_research_snapshot not found. Skipping persistence." + ); + return { + profile: snapshot.profile, + report: snapshot.report, + diff: snapshot.diff, + lastSyncedAt: new Date().toISOString(), + }; + } throw new Error(`Failed to persist research snapshot: ${error.message}`); } From d5990eab61dbbc3d2415e51c510a683029c43847 Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 15:43:58 +0700 Subject: [PATCH 16/17] feat(ui): add interactive evidence verification links to all profile cards and progress findings --- src/app/components/profile-card.tsx | 244 +++++++++++++++++------ src/app/components/research-progress.tsx | 44 ++-- src/app/hooks/use-research.ts | 3 +- src/lib/types.ts | 2 +- src/modules/research/sources/website.ts | 4 +- src/modules/workflow/index.ts | 1 + 6 files changed, 223 insertions(+), 75 deletions(-) diff --git a/src/app/components/profile-card.tsx b/src/app/components/profile-card.tsx index 759babf..9a2d990 100644 --- a/src/app/components/profile-card.tsx +++ b/src/app/components/profile-card.tsx @@ -137,10 +137,18 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) { {/* Criteria Breakdown (5 Core Criteria) */}
{report.fitScore.criteria.map((c) => ( - @@ -171,9 +179,18 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) {

Nhận định chuyên gia (Executive Summary)

-

- {report.executiveSummary} -

+ +
+ {report.executiveSummary} + Kiểm chứng ↗ +
+
)} @@ -187,21 +204,30 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) {

{report.riskFlags.map((rf, i) => ( -
- - [{rf.type}] {rf.severity} - - {rf.description} -
+
+ + [{rf.type}] {rf.severity} + + + Xem chứng cứ ↗ + +
+ {rf.description} + ))}
@@ -215,24 +241,35 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) {

{report.suggestedActions.map((sa, i) => ( -
-
- - {sa.priority} +
+
+ + {sa.priority} + + + {sa.action} + +
+ + Tìm kiếm ↗ - {sa.action}

{sa.reasoning}

-
+ ))}
@@ -243,9 +280,22 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) { {/* Description */}
-

- {profile.description} -

+ +
+

+ {profile.description} +

+ + Kiểm chứng ↗ + +
+
{/* Key Info Grid */} @@ -253,28 +303,49 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) { {profile.website && ( - {profile.website.replace(/^https?:\/\//, "")} - - } + href={profile.website} + title="Nhấn để mở website chính thức" + value={profile.website.replace(/^https?:\/\//, "")} + /> + )} + {profile.taxId && ( + )} - {profile.taxId && } {profile.foundedYear && ( - + )} {profile.companySize && ( - + )} {profile.headquarters && (
{profile.keyPeople.map((person, i) => ( -
{person.name.charAt(0)}
-
-

{person.name}

+
+

+ {person.name} + + ↗ + +

{person.title}

-
+ ))}
@@ -313,12 +393,17 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) {
{profile.products.map((p) => ( - - {p} - + {p} + + ))}
@@ -329,12 +414,17 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) {
{profile.markets.map((m) => ( - - {m} - + {m} + + ))}
@@ -345,10 +435,22 @@ export function ProfileCard({ profile, diff, report }: ProfileCardProps) {
{profile.recentActivities.slice(0, 5).map((act, i) => ( - + ))}
@@ -443,12 +545,36 @@ function Section({ function InfoItem({ label, value, + href, + title, }: { label: string; value: React.ReactNode; + href?: string; + title?: string; }) { + if (href) { + return ( + +
+

{label}

+ + ↗ + +
+

{value}

+
+ ); + } + return ( -
+

{label}

{value}

diff --git a/src/app/components/research-progress.tsx b/src/app/components/research-progress.tsx index f92117a..27e216c 100644 --- a/src/app/components/research-progress.tsx +++ b/src/app/components/research-progress.tsx @@ -13,7 +13,7 @@ const SOURCE_LABELS: Record = { interface ResearchProgressProps { sourceStatuses: Record; - findings: { source: SourceName; summary: string }[]; + findings: { source: SourceName; summary: string; url?: string }[]; status: string; } @@ -73,19 +73,37 @@ export function ResearchProgress({ {/* Findings log */} {findings.length > 0 && (
- - Xem {findings.length} phát hiện chi tiết + + Xem {findings.length} phát hiện chi tiết (Nhấn để mở nguồn kiểm chứng) -
- {findings.map((f, i) => ( -
- [{f.source}]{" "} - {f.summary.slice(0, 120)}... -
- ))} +
+ {findings.map((f, i) => { + const targetUrl = + f.url || + `https://www.google.com/search?q=${encodeURIComponent(f.summary.slice(0, 80))}`; + return ( + +
+ + [{SOURCE_LABELS[f.source]?.label ?? f.source}] + + + Mở nguồn ↗ + +
+

+ {f.summary} +

+
+ ); + })}
)} diff --git a/src/app/hooks/use-research.ts b/src/app/hooks/use-research.ts index d11f4af..ef44006 100644 --- a/src/app/hooks/use-research.ts +++ b/src/app/hooks/use-research.ts @@ -20,7 +20,7 @@ export interface ResearchState { status: "idle" | "researching" | "building" | "suggesting" | "done" | "error"; input: CompanyInput | null; sourceStatuses: Record; - findings: { source: SourceName; summary: string }[]; + findings: { source: SourceName; summary: string; url?: string }[]; profile: CompanyProfile | null; diff: ProfileDiff | null; report: AnalysisReport | null; @@ -99,6 +99,7 @@ export function reduceResearchEvent( { source: event.data.source, summary: event.data.summary, + url: event.data.url, }, ], }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 4c99c83..b246905 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -406,7 +406,7 @@ export type StreamEvent = } | { event: "research:finding"; - data: { source: SourceName; summary: string }; + data: { source: SourceName; summary: string; url?: string }; } | { event: "profile:building"; data: { message: string } } | { event: "profile:ready"; data: { profile: CompanyProfile } } diff --git a/src/modules/research/sources/website.ts b/src/modules/research/sources/website.ts index b10861a..fc12037 100644 --- a/src/modules/research/sources/website.ts +++ b/src/modules/research/sources/website.ts @@ -103,7 +103,9 @@ async function discoverWebsite( !url.includes("facebook.com") && !url.includes("linkedin.com") && !url.includes("wikipedia.org") && - !url.includes("youtube.com") + !url.includes("youtube.com") && + !url.includes("masothue.com") && + !url.includes("thongtindoanhnghiep.co") ) { return result.url; } diff --git a/src/modules/workflow/index.ts b/src/modules/workflow/index.ts index c5aa600..8ecbc63 100644 --- a/src/modules/workflow/index.ts +++ b/src/modules/workflow/index.ts @@ -433,6 +433,7 @@ async function executeSourceRunner( data: { source: finding.source, summary: finding.content.slice(0, 200), + url: finding.url, }, } as StreamEvent); } From a28ddda59ac365297cec38d53f7983d2d97ac9be Mon Sep 17 00:00:00 2001 From: devonxjz Date: Wed, 26 Aug 2026 16:34:55 +0700 Subject: [PATCH 17/17] docs: rewrite README with light theme architecture diagram, LangChain/LangGraph focus, and simplified newbie-friendly workflow --- README.md | 641 +++++++--------------------------- public/architecture-light.png | Bin 0 -> 536715 bytes 2 files changed, 135 insertions(+), 506 deletions(-) create mode 100644 public/architecture-light.png diff --git a/README.md b/README.md index 8417241..106adce 100644 --- a/README.md +++ b/README.md @@ -1,573 +1,202 @@ # PartnerIQ (TechBridgeAI) 🚀 -> **AI-Powered Corporate Intelligence & Collaboration Intelligence Platform** -> Nền tảng thẩm định doanh nghiệp thông minh tự động: Thu thập dữ liệu đa nguồn độc lập, tổng hợp hồ sơ chuẩn hóa qua LLM, đánh giá điểm phù hợp hợp tác (Collaboration Fit Score), theo dõi biến động lịch sử (Diff Engine) và xuất báo cáo One-Pager PDF chuyên nghiệp. +> **AI-Powered Corporate Intelligence & Collaboration Fit Platform** +> Nền tảng thẩm định doanh nghiệp thông minh: Tự động thu thập dữ liệu đa nguồn từ Internet, chuẩn hóa hồ sơ 360° qua LLM, chấm điểm tiềm năng hợp tác kinh doanh (Fit Score), nhận diện biến động theo thời gian và xuất báo cáo PDF One-Pager chuyên nghiệp. [![CI Pipeline](https://github.com/devonxjz/TechBridgeAI/actions/workflows/ci.yml/badge.svg)](https://github.com/devonxjz/TechBridgeAI/actions/workflows/ci.yml) -[![Tests Passing](https://img.shields.io/badge/Tests-23%20Suites%20%7C%20136%20Passed-success?logo=vitest)](https://vitest.dev/) +[![Tests Passing](https://img.shields.io/badge/Tests-27%20Suites%20%7C%20207%20Passed-success?logo=vitest)](https://vitest.dev/) [![Next.js 16](https://img.shields.io/badge/Next.js-16%20(Turbopack)-black?logo=next.js)](https://nextjs.org/) [![LangGraph](https://img.shields.io/badge/Orchestration-LangGraph%20v1.4-blue?logo=langchain)](https://langchain.com/) +[![LangChain](https://img.shields.io/badge/Framework-LangChain-1C3C3C?logo=langchain)](https://langchain.com/) +[![OpenAI](https://img.shields.io/badge/LLM-OpenAI%20gpt--4o--mini-412991?logo=openai)](https://openai.com/) +[![Supabase](https://img.shields.io/badge/Database-Supabase%20PostgreSQL-3ECF8E?logo=supabase)](https://supabase.com) [![Langfuse](https://img.shields.io/badge/Observability-Langfuse%20Cloud-orange)](https://langfuse.com/) -[![TypeScript](https://img.shields.io/badge/TypeScript-5.x%20%2F%207.0.2-blue?logo=typescript)](https://www.typescriptlang.org/) -[![OpenAI](https://img.shields.io/badge/AI-OpenAI%20Structured%20Outputs-412991?logo=openai)](https://openai.com/) -[![Supabase](https://img.shields.io/badge/Storage-Supabase%20PostgreSQL-3ECF8E?logo=supabase)](https://supabase.com) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) --- -## 🖼️ Tổng Quan Kiến Trúc Hệ Thống (System Overview) +## 🖼️ Kiến Trúc Hệ Thống (System Architecture)
- PartnerIQ System Architecture Overview -

Hình 1: Kiến trúc tổng thể hệ sinh thái PartnerIQ (TechBridgeAI) — Tương tác đa nguồn, xử lý lõi AI, lưu trữ đa phiên bản và xuất bản tài liệu.

+ PartnerIQ System Architecture Overview +

Kiến trúc tổng thể hệ sinh thái PartnerIQ — Quy trình thu thập đa nguồn, điều phối LangGraph, xử lý AI, lưu trữ Supabase và xuất bản báo cáo.

--- -## 🌟 Tính Năng Nổi Bật - -* 🔄 **LangGraph Parallel StateGraph Orchestration:** - * Khởi tạo đồ thị trạng thái song song 5 luồng thu thập độc lập (`web_search`, `website`, `news`, `registry`, `linkedin`) với cơ chế fan-in chuẩn hóa bằng Zod state annotation. - * Giới hạn tối đa 6 câu truy vấn định danh (`buildResearchQueries`) và ngân sách gọi mô hình / token tiền trạm (`createResearchBudget`). - * Tự động cô lập bằng chứng không tin cậy bằng thẻ `` và áp dụng chính sách ưu tiên theo từng trường dữ liệu (Field-sensitive precedence). -* 🔭 **Langfuse Cloud Tracing & Privacy Minimization:** - * Giám sát toàn diện vòng đời đồ thị qua OpenTelemetry (`@langfuse/otel` & `@langfuse/langchain`). - * Tự động làm sạch dữ liệu nhạy cảm (API Keys, Bearer tokens, email cá nhân, số điện thoại, HTML thô) trước khi gửi telemetry ra ngoài. - * Tính điểm chất lượng tất định (`source_coverage`, `profile_schema_valid`, `profile_confidence`, `analysis_schema_valid`, `research_success`). -* 🌐 **Multi-source Research Pipeline (Thu thập đa nguồn thời gian thực):** - * 🔍 **Web Search:** Tích hợp Serper Google Search API và chỉ tổng hợp dữ liệu trả về từ nguồn thật. - * 🛡️ **Tiered Website Scraper (3 cấp độ tự phục hồi):** Chuỗi fallback `SafeDirect → Jina Reader → TinyFish` với cơ chế chống SSRF (Private IP/Localhost block), DNS Pinning, giới hạn luồng 1MB và bộ lọc HTML tuyến tính an toàn. - * 🏛️ **VietQR Official Business Registry:** Tra cứu trực tiếp thông tin doanh nghiệp qua Mã số thuế (MST) với in-memory caching (7 ngày), tự động fallback sang Aggregator Search khi API nghẽn. - * 📰 **Tin tức kinh doanh Việt Nam:** Tự động tìm kiếm các bài viết từ CafeF, Báo Đầu tư, VnExpress, Vietstock... - * 💼 **Bóc tách LinkedIn / Nhân sự:** Thu thập thông tin ban lãnh đạo và đội ngũ cốt cán (tự động bỏ qua khi không có URL). -* ⚡ **Real-time SSE Streaming & Edge/Vercel Safe:** - * API route tối giản `runtime = "nodejs"` với `maxDuration = 300` và hạn chót nội bộ 285s đảm bảo không bị ngắt quãng giữa chừng. - * Hủy bỏ luồng tức thì qua `AbortSignal` khi người dùng ngắt kết nối. -* 🧠 **OpenAI Structured Profile Builder:** Chuẩn hóa thông tin tự động bằng Zod Schema & Structured Outputs (Strict Mode), tính toán độ tin cậy (`overallConfidence`) theo trọng số từng nguồn. -* 📊 **Analyst Module & Collaboration Fit Score (0–100):** Đánh giá mức độ phù hợp hợp tác kinh doanh theo 5 tiêu chí chuẩn hóa: - * 🏢 **Phù hợp ngành (Industry Alignment - 30%)** - * 👥 **Tương thích quy mô (Company Size Match - 20%)** - * 📍 **Phù hợp địa lý (Geographic Relevance - 15%)** - * 💻 **Trưởng thành số (Digital Maturity - 15%)** - * 📈 **Hoạt động gần đây (Recent Activity - 20%)** -* 🔍 **"What Changed?" Diff Engine:** So sánh tự động giữa các phiên bản hồ sơ của một doanh nghiệp (v1 → v2), nhận diện biến động về nhân sự, địa chỉ, ngành nghề và quy mô. -* ⚡ **Supabase Server Read-Through Research Cache (Deterministic Identity Resolution):** - * Tự động nhận diện và tái sử dụng hồ sơ đã nghiên cứu qua MST (`taxId`) hoặc Website (`domain`) với độ trễ phản hồi < 100ms. - * Khóa giao dịch phân tán chống tranh chấp đồng thời (`pg_advisory_xact_lock` & `resolve_company_identity`). - * Giao diện tương tác trực tiếp: gợi ý ứng viên cache (`CacheSuggestions`), chọn hồ sơ có sẵn (`select`), làm mới dữ liệu (`refresh`) hoặc bỏ qua cache (`bypass`). - * Lưu trữ nguyên tử toàn bộ snapshot (`persist_research_snapshot`) gồm Profile, Diff và Analysis Report. -* 🗄️ **Multi-Version Storage (Supabase PostgreSQL):** Lưu trữ lịch sử hồ sơ dạng JSONB, tối ưu hóa truy vấn và bảo toàn toàn bộ vết thay đổi. -* 📑 **Bộ Công Cụ Xuất Bản Báo Cáo Chuyên Nghiệp:** - * 📋 **Markdown & JSON Export:** Sao chép vào Clipboard hoặc tải file `.md` / `.json` ngay tức thì. - * 📄 **Client-side PDF One-Pager (A4 Portrait):** Tạo báo cáo 1 trang tóm tắt chuẩn doanh nghiệp tiếng Việt có dấu với `@react-pdf/renderer` qua Dynamic Import (Zero Server Overhead, tải font Noto Sans cục bộ, hoạt động offline). +## 💡 PartnerIQ Là Gì? (Dành Cho Người Mới Bắt Đầu) ---- +Khi bạn muốn hợp tác với một đối tác hoặc doanh nghiệp mới, bạn thường mất hàng giờ tìm kiếm thông tin trên Google, tra cứu mã số thuế, đọc tin tức và phân tích rủi ro. **PartnerIQ tự động hóa toàn bộ quy trình này chỉ trong 3 bước đơn giản:** -## 🏛️ Lược Đồ Kiến Trúc & Class Diagram +```text + 1. Nhập thông tin 2. AI thu thập & phân tích 3. Nhận báo cáo toàn diện +┌────────────────┐ ┌────────────────────────────┐ ┌────────────────────────────┐ +│ Tên công ty │ ──► │ • Quét 5 nguồn độc lập │ ──► │ • Hồ sơ 360° + Bằng chứng │ +│ (hoặc MST/Web) │ │ • Xử lý qua LangGraph & AI │ │ • Điểm Fit Score (0-100) │ +└────────────────┘ │ • Kiểm tra Cache Supabase │ │ • Tải file PDF 1 trang │ + └────────────────────────────┘ └────────────────────────────┘ +``` -### 1. Kiến Trúc Phân Lớp (Hexagonal / Ports & Adapters Architecture) +--- -Hệ thống tuân thủ nghiêm ngặt nguyên lý **Ports & Adapters**, tách biệt hoàn toàn giữa logic nghiệp vụ lõi (Deep Core Modules) và các dịch vụ bên ngoài (Infrastructure Adapters): +## 🛠️ 5 Trụ Cột Công Nghệ Cốt Lõi (Core Technologies) -```mermaid -graph TB - subgraph Presentation ["1. Presentation Layer (Next.js App Router)"] - UI["Web Dashboard & UI (React, TailwindCSS, Glassmorphism)"] - API["API Route: /api/research (Thin Glue & SSE Streaming)"] - end - - subgraph CoreModules ["2. Deep Core Modules (Domain Logic)"] - RM["ResearchModule (Multi-source Orchestrator)"] - PM["ProfileModule (LLM Structured Builder)"] - DE["DiffEngine (Profile Comparison & Change Tracker)"] - AM["AnalystModule (Collaboration Fit Score & Risk Engine)"] - PDF["PDFExportEngine (Client-side One-Pager Generator)"] - end - - subgraph Ports ["3. Ports & Seams (Interfaces)"] - PortLLM["LLMAdapter"] - PortSearch["SearchAdapter"] - PortScraper["ScraperAdapter"] - PortRegistry["RegistryAdapter"] - PortStorage["StorageAdapter"] - end - - subgraph Adapters ["4. Infrastructure Adapters"] - OpenAI["OpenAI (gpt-4o-mini)"] - Serper["Google Search (Serper API)"] - TieredScraper["Tiered Scraper (SafeDirect -> Jina -> TinyFish)"] - VietQR["VietQR Business Registry API"] - Supabase["Supabase PostgreSQL (JSONB) / Memory"] - end - - UI <-->|SSE Events / JSON| API - UI --> PDF - API --> RM - API --> PM - API --> AM - - RM --> PortSearch - RM --> PortScraper - RM --> PortRegistry - PM --> PortLLM - PM --> DE - AM --> PortLLM - API --> PortStorage - - PortLLM --> OpenAI - PortSearch --> Serper - PortScraper --> TieredScraper - PortRegistry --> VietQR - PortStorage --> Supabase -``` +| Biểu tượng | Công nghệ | Vai trò & Cách ứng dụng trong hệ thống | +| :---: | :--- | :--- | +| 🕸️ | **LangGraph** | **Điều phối luồng công việc (StateGraph Orchestration):** Quản lý trạng thái đa luồng song song (Fan-out / Fan-in) qua 5 nguồn dữ liệu độc lập, kiểm soát hạn mức (Token / Query Budget) và xử lý lỗi mềm. | +| 🦜🔗 | **LangChain** | **Tương tác chuẩn hóa với LLM:** Sử dụng Zod Schemas để ép kiểu dữ liệu đầu ra nghiêm ngặt (Strict Structured Outputs), quản lý tin nhắn và streaming phản hồi theo thời gian thực. | +| ⚡ | **Next.js 16 (Turbopack)** | **Fullstack Dashboard & SSE Streaming:** Giao diện người dùng hiện đại, truyền dữ liệu Server-Sent Events (SSE) trực tiếp từ backend tới dashboard với độ trễ thấp. | +| 🗄️ | **Supabase (PostgreSQL)** | **Lưu trữ & Cache Thông Minh:** Quản lý lịch sử hồ sơ dạng JSONB, hỗ trợ cache read-through với khóa phân tán (`pg_advisory_xact_lock`) chống nghẽn và lưu vết thay đổi đa phiên bản. | +| 🔭 | **Langfuse Cloud** | **Giám sát & Tối ưu AI (Observability):** Ghi nhận toàn bộ Trace của đồ thị LangGraph, đo lường độ trễ, chi phí token, tự động làm sạch dữ liệu nhạy cảm và chấm điểm chất lượng nghiên cứu. | --- -### 2. Lược Đồ Class - Domain Entities & Models (Class Diagram 1) +## 🔄 Sơ Đồ Quy Trình Điều Phối (LangGraph StateGraph Workflow) -Lược đồ mô tả toàn bộ cấu trúc dữ liệu miền (Domain Models) được định kiểu chặt chẽ trong hệ thống: +Dưới đây là sơ đồ chi tiết toàn bộ các **Node** và **Edge** trong đồ thị LangGraph điều phối quá trình nghiên cứu: ```mermaid -classDiagram - direction TB - - class CompanyInput { - +string name - +string website - +string taxId - +string linkedinUrl - +string[] additionalKeywords - } - - class RawFinding { - +SourceName source - +string url - +string content - +Date extractedAt - +number confidence - +Record metadata - } - - class CompanyProfile { - +string id - +number version - +Date createdAt - +CompanyInput input - +string officialName - +string[] tradingNames - +string taxId - +string[] industry - +string description - +number foundedYear - +Address headquarters - +string website - +Person[] keyPeople - +string[] products - +string[] markets - +CompanySize companySize - +RevenueRange revenue - +Activity[] recentActivities - +Date lastUpdated - +SourceCitation[] sources - +number overallConfidence - +boolean lowConfidence - } - - class Address { - +string street - +string city - +string province - +string country - } - - class Person { - +string name - +string title - +SourceName source - +number confidence - } - - class Activity { - +Date date - +string title - +string summary - +string url - +SourceName source - } - - class SourceCitation { - +SourceName source - +string url - +Date accessedAt - +string[] fieldsContributed - } - - class ProfileDiff { - +string companyId - +number fromVersion - +number toVersion - +FieldChange[] changes - +string summary - } - - class FieldChange { - +string field - +unknown oldValue - +unknown newValue - +string changeType - +string significance - } - - class AnalysisReport { - +string companyId - +Date generatedAt - +FitScore fitScore - +RiskFlag[] riskFlags - +SuggestedAction[] suggestedActions - +string executiveSummary - } - - class FitScore { - +number score - +string reasoning - +FitCriterion[] criteria - } - - class FitCriterion { - +string name - +number score - +number weight - +string reasoning - } - - class RiskFlag { - +string type - +string description - +string severity - +SourceName source - } - - class SuggestedAction { - +string action - +string priority - +string reasoning - } - - class PdfPayload { - +string companyName - +string taxId - +string[] industries - +string description - +number fitScore - +string fitReason - +PdfCriterion[] criteria - +string executiveSummary - +string[] risks - +string[] actions - +SourceItem[] sources - +string generatedAt - } - - CompanyProfile *-- CompanyInput : contains - CompanyProfile *-- Address : headquarters - CompanyProfile o-- Person : keyPeople - CompanyProfile o-- Activity : recentActivities - CompanyProfile o-- SourceCitation : sources - ProfileDiff o-- FieldChange : changes - AnalysisReport *-- FitScore : contains - FitScore o-- FitCriterion : criteria - AnalysisReport o-- RiskFlag : riskFlags - AnalysisReport o-- SuggestedAction : suggestedActions - CompanyProfile ..> PdfPayload : maps to - AnalysisReport ..> PdfPayload : maps to +flowchart TD + Start([🚀 Bắt Đầu]) --> InputNode[📥 1. Nhận yêu cầu: Company Input] + + InputNode --> CacheCheck{🔍 Kiểm tra Cache?} + CacheCheck -- Cache Hit --> ReturnCache[⚡ Trả về Snapshot Cache có sẵn] + CacheCheck -- Cache Miss / Bypass --> FanOut[🔀 Điều phối song song 5 nguồn] + + subgraph SourcesGroup [🌐 5 Nguồn Thu Thập Dữ Liệu Độc Lập] + direction TB + S1["🔍 web_search
(Google Search qua Serper API)"] + S2["🌐 website
(Scraper 3 cấp: Direct ➔ Jina ➔ TinyFish)"] + S3["📰 news
(Tin tức báo chí CafeF, VnExpress, Vietstock)"] + S4["🏛️ registry
(Tra cứu Cổng ĐKKD & VietQR theo MST)"] + S5["💼 linkedin
(Thu thập thông tin nhân sự ban lãnh đạo)"] + end + + FanOut --> S1 + FanOut --> S2 + FanOut --> S3 + FanOut --> S4 + FanOut --> S5 + + S1 --> EvidenceNode[📑 2. prepare_evidence
Lọc dữ liệu & Bọc thẻ an toàn] + S2 --> EvidenceNode + S3 --> EvidenceNode + S4 --> EvidenceNode + S5 --> EvidenceNode + + EvidenceNode --> ProfileNode[🧠 3. build_profile
LLM tổng hợp hồ sơ chuẩn hóa 360°] + + ProfileNode --> DiffNode[🔄 4. build_diff
So sánh thay đổi với phiên bản cũ] + + DiffNode --> AnalyzeNode[📊 5. analyze
Chấm điểm FitScore 0-100 & Rủi ro] + + AnalyzeNode --> PersistNode[🗄️ 6. persist_snapshot
Lưu trữ Snapshot vào Supabase] + + PersistNode --> StreamOutput[⚡ 7. SSE Streaming & Cập nhật Dashboard] + + StreamOutput --> PDFExport[📄 8. Xuất Báo Cáo PDF One-Pager A4] + + ReturnCache --> StreamOutput + PDFExport --> End([🏁 Hoàn Thành]) + + classDef startEnd fill:#3B82F6,stroke:#1D4ED8,stroke-width:2px,color:#fff; + classDef nodeStyle fill:#F3F4F6,stroke:#9CA3AF,stroke-width:1.5px,color:#111827; + classDef sourceStyle fill:#ECFDF5,stroke:#10B981,stroke-width:1.5px,color:#065F46; + classDef aiStyle fill:#EEF2FF,stroke:#6366F1,stroke-width:1.5px,color:#3730A3; + classDef dbStyle fill:#FDF4FF,stroke:#D946EF,stroke-width:1.5px,color:#701A75; + + class Start,End startEnd; + class InputNode,EvidenceNode,StreamOutput,PDFExport,ReturnCache nodeStyle; + class S1,S2,S3,S4,S5 sourceStyle; + class ProfileNode,DiffNode,AnalyzeNode aiStyle; + class PersistNode dbStyle; ``` --- -### 3. Lược Đồ Class - Deep Modules & Infrastructure Ports/Adapters (Class Diagram 2) - -Lược đồ mô tả các Interface (Ports), các Deep Modules và các Concrete Adapters thực thi: +## 🌟 5 Nguồn Dữ Liệu Hoạt Động Như Thế Nào? -```mermaid -classDiagram - direction TB - - %% Ports (Interfaces) - class LLMAdapter { - <> - +complete(prompt: string, options?: LLMOptions) Promise~string~ - +completeStructured~T~(prompt: string, schema: ZodSchema~T~, options?: LLMOptions) Promise~T~ - +stream(prompt: string, options?: LLMOptions) AsyncGenerator~string~ - } - - class SearchAdapter { - <> - +search(query: string, options?: SearchOptions) Promise~SearchResult[]~ - } - - class ScraperAdapter { - <> - +extract(url: string) Promise~ScrapedContent~ - } - - class RegistryAdapter { - <> - +findByTaxId(taxId: string) Promise~RegistryRecord | null~ - } - - class StorageAdapter { - <> - +saveProfile(profile: CompanyProfile) Promise~void~ - +getProfile(companyId: string, version?: number) Promise~CompanyProfile | null~ - +getLatestProfile(companyId: string) Promise~CompanyProfile | null~ - +listProfiles() Promise~CompanyProfile[]~ - +saveDiff(diff: ProfileDiff) Promise~void~ - +getDiffs(companyId: string) Promise~ProfileDiff[]~ - } - - %% Deep Modules - class ResearchModule { - <> - +research(input: CompanyInput) AsyncGenerator~ResearchEvent~ - } - - class ProfileModule { - <> - +buildProfile(findings: RawFinding[], input: CompanyInput, existingId?: string, existingVersion?: number) Promise~CompanyProfile~ - +diffProfiles(current: CompanyProfile, previous: CompanyProfile) ProfileDiff - } - - class AnalystModule { - <> - +analyze(profile: CompanyProfile, context?: AnalysisContext) Promise~AnalysisReport~ - } - - %% Concrete Adapters - class OpenAILLMAdapter { - -OpenAI client - +complete() - +completeStructured() - +stream() - } - - class SerperSearchAdapter { - -string apiKey - +search() - } - - class TieredScraperAdapter { - -ScraperAdapter[] tiers - +extract(url: string) Promise~ScrapedContent~ - } - - class DirectScraperAdapter { - -UrlSafetyValidator validator - -number timeoutMs - -number maxBytes - +extract(url: string) Promise~ScrapedContent~ - } - - class JinaScraperAdapter { - -string apiKey - +extract(url: string) Promise~ScrapedContent~ - } - - class TinyFishScraperAdapter { - -string apiKey - +extract(url: string) Promise~ScrapedContent~ - } - - class VietQrRegistryAdapter { - -Map cache - -number ttlMs - +findByTaxId(taxId: string) Promise~RegistryRecord | null~ - } - - class SupabaseStorageAdapter { - -SupabaseClient client - +saveProfile() - +getProfile() - +getLatestProfile() - +saveDiff() - } - - class MemoryStorageAdapter { - -Map profiles - -Map diffs - +saveProfile() - +getProfile() - } - - %% Relationships & Implementations - LLMAdapter <|.. OpenAILLMAdapter : implements - SearchAdapter <|.. SerperSearchAdapter : implements - - ScraperAdapter <|.. TieredScraperAdapter : implements - ScraperAdapter <|.. DirectScraperAdapter : implements - ScraperAdapter <|.. JinaScraperAdapter : implements - ScraperAdapter <|.. TinyFishScraperAdapter : implements - TieredScraperAdapter o-- ScraperAdapter : contains fallback tiers - - RegistryAdapter <|.. VietQrRegistryAdapter : implements - - StorageAdapter <|.. SupabaseStorageAdapter : implements - StorageAdapter <|.. MemoryStorageAdapter : implements - - ResearchModule ..> SearchAdapter : uses - ResearchModule ..> ScraperAdapter : uses - ResearchModule ..> RegistryAdapter : uses - ProfileModule ..> LLMAdapter : uses - AnalystModule ..> LLMAdapter : uses -``` +1. **🔍 Tìm kiếm web (`web_search`):** Sử dụng Serper Google Search API để tìm kiếm các bài viết, hồ sơ doanh nghiệp mới nhất trên Internet. +2. **🌐 Website công ty (`website`):** Trích xuất nội dung trang chủ và các trang giới thiệu (`/about`, `/products`), tự động bảo vệ trước các liên kết độc hại qua cơ chế **Safe Tiered Scraper (Direct ➔ Jina Reader ➔ TinyFish)**. +3. **📰 Tin tức truyền thông (`news`):** Quét các trang báo chí tài chính hàng đầu (CafeF, VnExpress, Vietstock, Báo Đầu tư) để phát hiện sự kiện nổi bật và dấu hiệu rủi ro. +4. **🏛️ Đăng ký kinh doanh (`registry`):** Tra cứu dữ liệu định danh pháp lý chính thức từ Cổng đăng ký doanh nghiệp quốc gia và VietQR qua Mã số thuế. +5. **💼 Mạng lưới nhân sự (`linkedin`):** Khám phá cấu trúc lãnh đạo, nhân sự cốt cán và quy mô đội ngũ. --- -### 4. Sequence Diagram - Luồng Xử Lý Dữ Liệu Thời Gian Thực (Data Flow & Streaming) +## 📊 Tiêu Chí Đánh Giá Điểm Hợp Tác (Collaboration Fit Score 0–100) -```mermaid -sequenceDiagram - autonumber - actor User as 👤 Người Dùng - participant UI as 💻 Next.js Client - participant API as ⚡ API Route (/api/research) - participant RM as 🔍 ResearchModule - participant Sources as 🌐 5 Data Sources - participant PM as 🧠 ProfileModule (LLM) - participant AM as 📊 AnalystModule (Fit Score) - participant DB as 🗄️ Supabase Storage - participant PDF as 📑 PDF Engine (Client) - - User->>UI: Nhập tên công ty / website / MST - UI->>API: POST /api/research (SSE Request) - API-->>UI: Event: research:start - - API->>RM: research(input) - loop Duyệt qua 5 nguồn dữ liệu - RM->>Sources: Tìm kiếm (Web, Scraper, VietQR, News, LinkedIn) - Sources-->>RM: Trả về dữ liệu thô (RawFinding) - RM-->>API: Yield: progress & finding - API-->>UI: SSE: research:progress & finding - end - RM-->>API: Complete (all findings) - - API-->>UI: Event: profile:building - API->>PM: buildProfile(findings, input) - PM-->>API: CompanyProfile (Structured) - API->>DB: getLatestProfile(companyId) - DB-->>API: Previous Profile (nếu có) - opt Có phiên bản trước - API->>PM: diffProfiles(current, previous) - PM-->>API: ProfileDiff - API->>DB: saveDiff(diff) - end - - API->>AM: analyze(profile, context) - AM-->>API: AnalysisReport (FitScore 0-100, Risks, Actions) - - API->>DB: saveProfile(profile) - API-->>UI: Event: profile:ready & analysis:ready & done - UI-->>User: Hiển thị giao diện Dashboard & Fit Score - - opt Người dùng click Xuất PDF - User->>UI: Bấm "Xuất PDF One-Pager" - UI->>PDF: mapToPdfPayload & renderAsync() - PDF-->>User: Tải xuống PartnerIQ_CompanyName_YYYY-MM-DD.pdf (A4) - end -``` +Hệ thống chấm điểm doanh nghiệp dựa trên **5 tiêu chí chuẩn hóa**: + +* 🏢 **Phù hợp ngành nghề (Industry Alignment - 30%):** Đánh giá sự tương đồng trong lĩnh vực hoạt động. +* 👥 **Tương thích quy mô (Company Size Match - 20%):** Đánh giá năng lực tiếp nhận và quy mô nhân sự. +* 📍 **Vị trí địa lý (Geographic Relevance - 15%):** Khả năng triển khai thuận lợi theo vùng miền. +* 💻 **Mức độ số hóa (Digital Maturity - 15%):** Đánh giá mức độ ứng dụng công nghệ và hiện diện trực tuyến. +* 📈 **Hoạt động gần đây (Recent Activity - 20%):** Các dự án mới, sự kiện mở rộng hoặc phát triển trong 6–12 tháng qua. + +> **Tính năng Bằng chứng thực tế (Real-world Evidence):** Người dùng có thể click vào bất kỳ thẻ thông tin nào trên giao diện (Tiêu chí FitScore, Rủi ro, Nhân sự, Mã số thuế, Trụ sở...) để mở tab mới kiểm chứng ngay dữ liệu từ nguồn gốc! --- -## ⚙️ Cấu Hình & Biến Môi Trường (Configuration & Resilience) +## 🚀 Hướng Dẫn Cài Đặt & Chạy Nhanh (Quick Start) -### File `.env.local` mẫu +### 1. Yêu cầu hệ thống +* **Node.js**: Phiên bản `>= 18.17.0` (khuyến nghị Node 20 LTS hoặc Node 24). +* **NPM / PNPM**. +### 2. Cài đặt các gói phụ thuộc +```bash +git clone https://github.com/devonxjz/TechBridgeAI.git +cd TechBridgeAI +npm install +``` + +### 3. Cấu hình biến môi trường +Tạo file `.env.local` từ file mẫu: +```bash +cp .env.example .env.local +``` + +Điền các khóa API cơ bản: ```dotenv -# ─── LLM Provider ─── +# LLM Provider LLM_PROVIDER=openai OPENAI_API_KEY=sk-... -# ─── Search Provider ─── +# Search Provider SEARCH_PROVIDER=serper SERPER_API_KEY=... -# ─── Scraper Provider & Fallback Chain ─── -SCRAPER_PROVIDER=tiered # tiered | tinyfish -SCRAPER_DIRECT_ENABLED=true # Tier 1: Direct HTTP scraper + SSRF Guard -SCRAPER_JINA_ENABLED=true # Tier 2: Jina AI Reader -SCRAPER_TINYFISH_ENABLED=true # Tier 3: TinyFish API -JINA_API_KEY= -TINYFISH_API_KEY= -SCRAPER_TIMEOUT_MS=8000 -SCRAPER_MAX_RESPONSE_BYTES=1048576 # Giới hạn stream 1MB -SCRAPER_MAX_REDIRECTS=3 -MAX_SCRAPE_PAGES_PER_RESEARCH=5 - -# ─── Registry Provider (VietQR) ─── -VIETQR_ENABLED=true # Tra cứu MST chính thức với 7-day memory cache - -# ─── Storage & Cache Provider (supabase | memory) ─── +# Storage (Supabase hoặc Memory) STORAGE_PROVIDER=supabase -SUPABASE_URL=https://xyz.supabase.co +SUPABASE_URL=https://your-project.supabase.co SUPABASE_ANON_KEY=eyJ... -SUPABASE_SERVICE_ROLE_KEY=eyJ... # Dành cho backend atomic transaction / RPC +SUPABASE_SERVICE_ROLE_KEY=eyJ... -# ─── Langfuse Observability & Telemetry Privacy ─── +# Langfuse Observability (Tùy chọn) LANGFUSE_ENABLED=true LANGFUSE_PUBLIC_KEY=pk-... LANGFUSE_SECRET_KEY=sk-... LANGFUSE_BASE_URL=https://cloud.langfuse.com -LANGFUSE_SALT=your-secure-salt-for-hashing-identifiers - -# ─── Resource & Rate Limit Guards ─── -MAX_CONCURRENT_RESEARCH=1 -SOURCE_TIMEOUT_MS=30000 -MAX_RESEARCH_PER_DAY=50 -MAX_TOKENS_PER_DAY=500000 -``` - -### Cơ Chế Fallback & Tự Phục Hồi (Circuit Breakers) - -| Tình huống sự cố | Cơ chế tự động xử lý | Trạng thái hệ thống | -| :--- | :--- | :--- | -| **Direct Scraper bị chặn / WAF** | Tự động chuyển tier sang **Jina Reader → TinyFish** | Không gián đoạn | -| **Jina Reader 429 (Rate Limit)** | Bỏ qua Jina, fallback tức thì sang **TinyFish** | Không gián đoạn | -| **Thiếu API Key Jina/TinyFish** | Tự động bypass tier thiếu key mà không gây lỗi runtime | Tự thích ứng | -| **Target URL là Local IP / Private** | Chặn ngay tại `UrlSafetyValidator` (SSRF Protection) | An toàn tuyệt đối | -| **VietQR API quá tải / lỗi mạng** | Fallback sang tra cứu qua **Aggregator & Google Search** | Bền bỉ | -| **Supabase không khả dụng** | Fallback sang **In-Memory Storage** cho môi trường dev/test | Sẵn sàng chạy offline | - ---- - -## 🛠️ Cài Đặt & Khởi Chạy Nhanh (Getting Started) - -### 1. Yêu cầu môi trường -* **Node.js**: Phiên bản `>= 18.17.0` (khuyến nghị Node 20 LTS hoặc 24). -* **Trình quản lý gói**: `npm` hoặc `pnpm`. - -### 2. Cài đặt các gói phụ thuộc -```bash -git clone https://github.com/devonxjz/TechBridgeAI.git -cd TechBridgeAI -npm install ``` -### 3. Cấu hình biến môi trường -Tạo file `.env.local` từ file mẫu: +### 4. Khởi chạy ứng dụng ```bash -cp .env.example .env.local +npm run dev ``` -*(Điền các API Key cần thiết như `OPENAI_API_KEY`, `SERPER_API_KEY`, `SUPABASE_URL`,...)* +Truy cập [http://localhost:3000](http://localhost:3000) trên trình duyệt để sử dụng. -### 4. Khởi chạy máy chủ phát triển +### 5. Kiểm thử hệ thống ```bash -npm run dev +npm run test # Chạy toàn bộ 27 test suites với Vitest +npm run lint # Kiểm tra chuẩn mã nguồn ESLint +npm run typecheck # Kiểm tra kiểu TypeScript +npm run build # Biên dịch production build với Turbopack ``` -Mở trình duyệt và truy cập [http://localhost:3000](http://localhost:3000). --- +## 📄 Giấy Phép & Bản Quyền (License) -## 📄 Bản Quyền & Giấy Phép (License) - -Dự án được phân phối dưới giấy phép **[MIT License](LICENSE)**. -Phát triển bởi đội ngũ **PartnerIQ / TechBridgeAI** tham dự **Google AI Hackathon 2026**. +Dự án được phân phối dưới giấy phép **[MIT License](LICENSE)**. +Phát triển bởi đội ngũ **PartnerIQ / TechBridgeAI**. diff --git a/public/architecture-light.png b/public/architecture-light.png new file mode 100644 index 0000000000000000000000000000000000000000..566287451d468fd09f1696a819706222e91e37bb GIT binary patch literal 536715 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ℑl$D!9x&C{?sNSFe>`h)d;Ax3=-mj7pwzz{ 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

ycLpVGJg|ehLIrL(85Ez(d zx+eG;;kF{yN&XURkE*U+2+b*9uMSS{13=2k%t&2FkB}V zOangyfa7te#VY)awlIla%`Y+hd_1mGj`W0#*=UTn>Mg6^cP2ikqWs*VYpytm?kD8= zy69ae%?ptVg0Q)8P=YB?2$71EsT4(dd>j=S3m-G<;4Y$Mv))r>Ebyo`#mD4C?|pu8 z0((k7f1EFT;~9w%`WdF#*HBB#81Jdi8?LC|Hc`?A7$CTse{ngal^<}hcGUa=r zeVpwpMl0tmN3J7& zzNE&XpIQ|~dauykBLT3#{m}w^lj|`)a`qlyXc{{Rid~^^!z7GlB!+DKm4f(6pyh)7 z=?fv_epYSRktY*ahlH}^udNDk=duvy!5Of7VGQQ={wBO#6^ytNpK%+oI zji0Nl`1wjcVBUOlpRv6RQSL%1%r0}t!YdMkUim_Bt(Zc*DW%4j*5S=1-SYK!8=zYo-yj^&?R zfjd}Nl$0dR?RR!`J!#wFcDRxyZ~(5d@4ObE5zKrlmgUQ~nCwpzQbku#?BspTQ36F@ zt6U8C27Z4<5$AS#)UM-<`FG4&4OP{WbAg`+VHtZm{Wc3U?OBCn&U5Y5jPvWK_X*2j6_Qd@SIJiB!POQ9zTh zzwW3A_-!)dYR8Ymu6U2jfyWQlj?0$uLko5p`M<&@c#TnZJ7YX<_1{d?2fh!n)4y$4&Fn^s0|ki-$p&C0I{tnJ)2NmkA9;vu zeUCgerF=T~laDg7)O<&IX&l48@08!NXKI9Yt=`g(JoSGNcI9t15=SZ(VH;i=C2O%) z8bQXTWfMUd#RjPeF2DqtmDjudK-9^ z*rfJ2!#aSC2XcJ74mD6Xird<9j?~4D%kAdrh@QYq_(NuRs=d4CI;^XL;Wb!_0$Ocn z4Xh^WdPB6?yF9M<22g79{6;TSS7);SbDkTu>-z0%7MJBM+(u*IA*F^}7Wb`%u;g;^}(Z z2ssdzE`9$&1@L9b{!;Ai6Hq|Ax%1{op8w{x+)y8F`r<1V3oC0y>I{G=3uAz>mtFoA*#4U!L=vjCpfSrzqFf7ZjgC?`vA|8&J{zM9QuH(vGkI@t9AbWRdJC-NvZ zeWk`NjzX>DQ;0Y0etDM*msX;XfBsp4p=snbI+KERCWX_0tTvjN?98{E$1Z>5s!vRC z(wHQ@2D<-f{Z)BO;1G@3x=vu@;Af-cz)sizvRRcea1vko36BJx`S`|t%d5O(tl>Pq zd6CQ5KNj1_Mt92~II(LHD^tgZ|2em4(H7w)9XpJRg?art@3+@)bA%cZG9`@&xXc0= z{ta_8W?0GoSS1j&ZHIXKG*goi%_D}Y?`cpmiqauqH@!b5U1$UiB^hlRYGOG_s7rMt z)$o(TX}_$#44Y@ajRwWkp1)ECi)-)|C+EQH2txpJ3h}D9CDo#TZrH_)#Tf-z&^45J zbi;{ce|W(=O0iVKLB!RfX}{C6B;=#7 z8*8Kl>oS9dHjl_VY$ zVmPG+9xVh+X2@!g3ujBeaJc;7JcKgNk(=j&&@4tUBVCS)NfF zb!%kM$+7?UsR%#+0-*mi_Xnv-ve`m&jwW~k1-hDv-G;o;Po;aE{Qu!g?;B1d0018O zv8gTW%@qD2p&dWI6(3`AT(lz45I*{4vR!n(2_Q`7hyqS#)<0TdDIMyv=a}^v_yP=M z6&u-nNq>lYz<#xXr4NU^bJH>X|2ZxppW}Ck53{rG+VLslc47?wTz0Pac_VI1li5=8ktSbk)uw$j`Qn~&}`d()mr>d@RL9fJ=W zMqCX@cgS@ZXbX*<2q8vgnARRLf8#2&T;YSYBHXKA*jUm`Nx0XuIz+;7+#aQFM$84R zFjsSF4QQjGY|@(rdQbgMiJS=Enl6D=isHw?`y=&}FNo|4#b*4Qv17F7kqB@iw-j~Y z$-7F%=N2Q=_%pe3dj$8IRa78P&YuuikP$+!&1?g;LGXA6uug1qbrMmU>vjmz%kwcf zpwOe5HddHrXZfpK76G2A`f(0)owWa#$q*V%HuvmjDkQ+}vO6b?|7{uYs9cEu60G;b z=8V#b+penbcdotib}x|ycbWjnkAkfE}zwFx%K4cRPSS-@-4kTjx`;~ zvV_v)w$50#`M`={bFJb7hb$|msi*IG-S4aO*gFUE-WlEv*h~Hat3dVJs@ib=W*9B? z^<~&H{h`;Cd`0z`+U|>-Y+$gVS=pw3oF%#_wj7T-IL|TrSuS^D@*xafG^2cJCNr__ zO+c7X@sHC!{jb(fHG-zTe!0|~(X&k1!?V!*RRgE+_84r|rGK4vJWbAT2{HcC#;^k4 z^EFa2o3(BEacRvNHGW=DUn_5`J?9Kn`c4GS*%zLrr{{F!!~k(g2TWblve zjZ&0z-C}8J!ibe??L4|>Wfs03*3hxK-orK2%D(s;>oEieg4!X|iwHKq)R0*!a>5WMHwvE%^b3cP@zK& z|1E1C4-|c}rNkB5)E+Nv%01rn(Ua75Ro*tj*DJR?)GP%soxDs-*s#pY?3sgBWTjP? zGK#1t7HNhJ@TaSEh-X-LgjdQ{tee}$vX)plJ(O!nRoULqjixIu3)fdyTh9{NZm8+# zA^3)TRNbpe7gk3=@n`_~`$F*SS-u`kx?YP_&*|1ns)@?R)di!^f9 z=>FGo|BIT@s~Anb5qlj)@E}N;)KB=>1H(;mgVjS;$onG;QBD7WyfLurLAMu<8cmf_DlrgVlL;lkE1>RuEj`r-^!D|s>+ zc@g(}>-o8R8&;}Z4!QM`g=(E{-@1IIp$c|Gd2LP!3M7JNEHxn$Nm)y@$p%V(Jb#Qa zlJs_HV@1U`Gr*bD^-Hi ziu#I-GX+QP7^ftt!Wk!lC|wOR7_sBX<2_ThqfW5%pPrm!Sbj}P-684qNl`r)1@W$Tvg92G#4yMrLy9j?eI47|S70fU5ahk)P3`NGbL# z6C5O?-TmAj0>fgZL#3lWcwcHvYcN;RGg+1`IEqYFCHPQK6`|~ZblHnMB@{moBb(t(YH$CShGlh5FUiodamu~>srSqVF?YY!c zRG5_$HLQQm>E1xDNbLiY@ASOnY$FGH17mXfJL_s@sTrZ_TA$ z#&ZeHHi~X|$RB5auAzum?;1&zpxr2Nd?|nujw=1iy_(t*aqXe+xOe9^ z<<}Vc9xEQ|U7rT>>>^;wmL$1&-R;xw$L{rIH zTS{nK`2eKuz-nmnhAgJDIhz{aj2zHv&5TPN|6NYmjk(;y1WY6Y-;|G5^NS-aVaVbc zVQ28aRb}9@qC+l>+_+0qRk`)Ae8aetFC8K&jW&#BGk32oW(H38@`%HH;h>z0}aY4+vSjJ~^1KaJF-<-5%3b{ZczdBp@n z3%Ng3TjX!wx8mxD42ue(dcriumG3C*_QJF?8gGt?N7HSSIzM%7`k;lC*G$gri5Eux zER1Vl(%6Te61_Sl16OT>5(m~&Gh;f46@iA08qvM{k5R)w8a?ix;TEXnv}zHxb_a**!Ny*Wymrvw1ul=uXMwRKCC0s7)kjGby;0Kw|gxLJ=$Jslf=hKF?2nB?;n~hBv&oO@sn}nB}(d(ugHj##WwI`-` zks>W_lKA}CDUgLAzQtObvr7UM9L**-`hCWbtkqhS|8u;VIk{|6I&+GA8LfXVZhGRu zUG#n@2OnlX3!o_!@f|n_@jgUbW*4V^Bf7g@- zl8jjStQ~7q8JZHjdOY)HT4S@@#PV#QB^Op<$s?hEGUutY)Q!_(rEfq;3>6tFq6#z` zafbwItt`bzT~UWXZ_NMw=Q|0t$R;3|9g%nD$#Si))qNBHj8k-iM(>KTWtv;>zZ)7j z;VAQg_^jqGH&QGpZ-++OPiaz)hBTmQXt`9Dmzp{;vr;=dS5;-L|EiCQ9Wl+~Amo=w zGM$y?YMFh_-<|KI;U8Xa{DltT>$@Pmt|pzy+YGnC0_v1l~f^A20f4sbAIFBYu zRMxkwwBc%cw;l-O#9ZW-G?>&8!8SE$heMj|6A#kYp=ryvm+q>IK<2fe@kL#@F@ zf;_0ROch9+ucLb>xKrAH1P-@={6IZiq4Nr_Jgb5lfd}Lu`pg*dKHTeFH^PAB@>Nts z_~?lKgYJ4Q&o>jP7igwU+a)y-+|l}%EYaz6=5vSnZ=p2t@ngXEf(wwiM>v?U3vZ8& zhQ3g9-X;G{%4<5+2tCaF|6zqT4T&;h%tCGXKaK{F4tu}a8aupeb)_w;a9wYz@UN6s zg1Y@*sn-w|)tR!#i}imbU3)y!-~ZP~rBZZr4Xad&B)MOgE`(T7?yOR|EaX17ttiT6 zLMX8%mxWla%WZRC62q|EFO&PSW#)}-pWplQ{r%(dn7_`>&Uu~J<$0+@g)gA(ST{?t zHVDi6%UYEi*~e=_`xir3#~}%y+b%Acfkv3}P14J)A&R)0H(`2weQxuHkx56Cku_qT zHwE^gab5JxbHYX|nAZ~8sB zGPZ-~B326u)fyRz*qAeN|M6=+HIKcdD_?&vExBvKOG^5;DYAJB`)x&kON@~i|25Hq zm?3;A_b>fr*Rzo5OB$(0CQ2g}Ic9r^UJ*q^1QAh|wLI(>acW~%^#mJXi8!5y7WGj9 z|G!wjQ;4glt2^tzt~QVrO(t--s@BCa@o zLJh%%4Z~O))gk>LVXz3m-o{TfkY84!Y zhi;B{R_Am0$r*; zH&b~IRJoLXwC;JY+#%b}9#dp{z^2;pg2&apHVz)LjgO?m99$-1&*aY(S-(}hd;6vN zUoGYMyu#eGEjBqa#N7Mlak+Cgc_&lfv1F z0wwupgg$%lRv;p;!Ct|o4MmH1Z~!*FfZ7*%5eQ}bwF}hP=ds884V9!`HlXSXx^DQ1 z;f&hTkbc&Egw<-L+L}l8@QTqzcBfWrDOsEW{iQ|`$boX`wypY7?a&u)=K=KgWn`!u z8`erqS|)T)PsS2HA&pzNVI3L>qEo}}4U0M!|9R~1l^H8j%YnMsiQ$)=@T80!&O1K| zL&MTn4&IUGals0E=R|Sh7`)|DiK=5)b=Wk+^e)z(nuIwv5)pO_Z{yZk7kGOaZyP5w)whq1A;ZXC&?NYN z<1;)t_Qnf=RHPeQ6MYyZ8>v^0(_pVW0=`5I5{lr**Nn zBy+|c)7oIf{_*0`tL(Fvcn9SfbbL9XdlyAZ=^PAlJvVZWmRcksL47#( z*0#=`*FE}GL3?(;yU@L-=pCRH>Cw0)gQy+QI2U&#v_Aj9Ao`wFfSM5R8e1pMSU*yX zJ&6$HJBmr#23Li_GVRU$L`Rc1Z&7Qe6L|o zSe>58Rto~_w&-A>nBFXqZ5$M6MauMn^mgj>DcrFJB^J~HWcH~YL6C$jWvU^UXl-kY zozs%+of+C|-|J3J-bm?`E7JayQL{F2oy;@!6H86zJ-qXFKG8wCA9*vpEdrcdoW7e5 zDK747YS419!?>S~x^kyL=F8OtD>!$~WhRqY8JvE>@k(CR+xzEqJkNa0QzJ0e!`wm=8MN9zNE!L-b`Nv{^dPrHY^jlJtE1TJEg4pxU@xd0srGt89B?Oi z<0J)QpyIw6CBfBcdrGYp4p|N_#U~n{Tim)Lv=xh!h{Z+td>Zynm595Rb}QAqT02$d z@Py{K=LMkBKNd1*s6s$srRiTz|L4VU0HIHas~*?(!G`2~@g#1@`WCsXo2DpO8Z0|9 zXM;ja)qhJD*`V@HGJIubZicw0&kmQ$h3K5~arf*`a?m|HqjGQFCf(xgG)%%2t8(B~ z%bn8LbniQCO_f{O47(%o{jq8XUgtOksVJwPGq6@(P7l=8k))sRf9oN2>bJz%>fqCl zx)mA%j7&JIKz2kx#6I8O>x;`cDuz6N65AF$wc}SHz`BTT?SFT9`S~c(&8*yzv(X*( z(58BQ@u9#8?>kg88t#los7@Bwhd3{fMsYS>88WLeV{%6l4^gjv7p^h$hXL@0dgP?_dgS^h^5@%{tY8ZCpibt4<2rPa$5)N4;4hlV{i zB7JkYUXR`jVoIXXPqwrTKfjVTIG8=3_8eoTxHAT`d$+Ex1UbO|2UXr04WkWXK zpVwoMA^6<+*^Ul-J&MwEo}LoqLR4V0Q9vNZxPo8UX)NTTV1_XincN9x9G322gI@?- z8)jJI@6+$i56~KP96_3+%g7SEWDSC~h+Z{l9GRS~^mn z-r&lrY}=TSU|(~RdToEBzszlDZV=nQ<$NeyOZ1rhW9r?$o;)9f>Re8+tfAGKp-G!o zz)iP*4(0CSsPwg4muilATL&%O_v*V%*^p~SnbFnVQWTUli<%U9`g8M) z_&$Kzx}_G+Qz`SBaXcSP6$9d|YX&)1Zq4}^y@!>9nxkFLqozm(DEavAljIIG291I5 zT~FTS1!cK`>QuYu8KF!GF#m~0S5iPE*YGw2-@-Sbz-1!sX9!2UP?y+dFMWce{B8_I zS{8Mk-T<`>XO2-N%*Ep))let<8?H+xO?NSEWP+mU}(Q!HM2fAx~;jbx&9%9dsn&y$DybFZ7;nZ7&tr5xv$vDfrcqTD0% zo_!YHaz9k73Y32Eom`ZxiVwx=-&M9R9w}13U0}JCpq7_B@1J97TU^xL+&=I$z+zqT z=q&jw`K!ffT-u3|y}>&$gF7F76`pNAe(+cGJ&n8FFFS7hee{FBqIe+BZ~XZFXZq1k z_b?=Q{ycLcpA>nTrNgxbMA%>gNFfh18_dDFIvgxbn*ClVocq9yX8E;?V+1mMoUtPC zV5x`FO!^Pi4_bf%xEpMpS0vdbsjM${cBD%WGnpEWnJi}EOGLgCWdTyN8k5VSwoQU? zql(OCLOe**M330J@Ds`IUCBqFafXN3+pTagr)8INJ^p&wJC^Mjj2~raQ z5*TU;Gc1voQiua~4ji(|14F?tX|3TqZ;e`~E%_3)XU#NB_Pw9Be}A(atY$7u?h%|r z^+H&;)a1dfJxhLsOZpFr)v@{yPYcM|-h-rZUjpf6yp!zT;O62Wyig!>8Y;uP#xhMV z1mZeIu3hpbc7H;KHtR#;wJhNm)|m7S;}e5rOvo*A^}v&F;Lsk&ec0DvI|I4}dpy&c z9Hq|8Gatrrm)OLWaeok%fj)Yn$|s^#c30*rO;wxG)_z2|Y57bnd_;7*51f-uA0q)} z8&83()$1^^#R|`%5ma3Mx&BCm{Zh5Mu3_z+cDcZ zlq^Gbrsf@&uu2z=Ju{2<{l9I}K2OIN=_4bJyo)tIQzY|rhrDd&(r+HK&Mw%iYi*d+ zm18T&H5A^qKq}6_I{p>HK!gmT`2d$rfmCu7cGt2=1!YK`+x?L;#aG!Pruq{v1pC-G zd$_2xo~nw-b$_3ukOy`4@!GW*y)&>+58O}w5&F7$=go^}{%`gw3L9qhL8i}O07>-}rWLa`efdtkSdBquY(iDj`m&amEcO)t*e~%;k(}Y-#V|{VM_mq6(ecc-;SRP*s4B%$|Zj# zf7#Z`?ES~cv&&}(TOYVfN1Wf6qhD;}U85ZfS3Z=T8XZq?me7(wv>f#|puQEf%#QCl z*4KM+(=t!UzUI4M&qB4w+Qv)DQETR%Wa{3Y23{*Qo0 zF2^TVL3Rm7F7m{HWmb$^1HYJOwG9J>P7a{dkdPTnQ3=m5p?%^2KxLJ*I(cA%(FXzV zYi!r?M8Cs}^pR5B%V1R{SbYYhdULE=lyQ)$_5D+ z+m^e{7C9c~E%~PYVPnZEACyX!z*ph&lO~P8YeHeTOyaloy|5p^h_> zl8MmiS^SZ6qb2z0!)%vKO+x3{AGfU|ORgPu;3zJM;BN+ZAh*_Ra^+$MI7aHt5S*R@ zV^U&YkzBf=*rb4bGr%Zpu2A0KsVQ<^gs3Fl$?eb>YP8QIa=7$R@;btSku;uFziFI7 ze}sQrx+!g}EDCmvDX)Y&L9*b5f@}{#)ahBS{C6s(0967!5wCEFp{>+R1&*#PsAJ6- z^l$SrJUrVP9LpJAr3)@+;Vz@+`&~i>-zXwjatTVdxw2e(CqMOJqxs?w=7 z%0!1g19LG-gmC>>VDzN`)9@%=DgY@xlsx`S8}qHnaP8MR_2+oh9^Ixc?!Q72qkk9+ zpx~Sm*5Ik07VvzlCF(*o(VjnQu;Eri{sikVRw+(0Ykp*{uJkH14RfI2d5?(OMN>Vh zyzZAABUZ5n<_1IWco9TL{*v)~k*r_9SD}H(gO^MnvJcHG39r~idSLe66PoWm() zmXN3rpphL!&D|L}(ZrvyMhvzTC|`6Ut~z^oFpv3Ms0%_xaArT#KCd_O<@vux?P@~) z@(wKMfh?9A41x?4>5-O3bvYb>Eb-6DtsUEKEyVaG)=D^)9VA_R5fc$BAL<_c)a*9S z4W}k8j8P?6L*01dH-#~~{jnFrBr0E~Z#V}p`z90JMknoqha)F)Z0tfumR9`?R$fQ; zG|WC2zsjfQwJ(`#gGvPPMrf;31ZK1g?d|<9KGBd-kNcW&S`Cbz?vv0c7nxwxobQvirle!}K!IkwoujRf?Ui-Q0@VlYoi!}S+ z9-+O=z~|ZS0VN9Wze_g@WlxD;{S|&cr?XR$wLHM-1ewk*EPUcq(>rqa!rdX~6p6%- zi#NM=>&bZ#@NRh@SHdb$6~89|2WbOvfHhrEP?# zs$-7r6#KYiK(ZINYsV?vI}cL$tehMU#jkr@Id7z^EBYMw;Y9RWT3S_hQmc%jaM5wE z(Z-3_=vad=avh(j>TF8$T`sG!R^>J_A6C1$-W279!8(nEzAMTx^%S!`lrK|+o~aK} zT1}EXIvhKoGdVrtaE>#NA#dCefLy!{KT=*!$J|(Prjx>icltl`ALwM!p`*r{Mr+f3 zobIxZ`iyM%=NRBi3Hl!b;0}H{uPmGTB2iD8&-kA&8FgqoI+b2wHl4EIzxXGR^Z7UQ z>{M=to_v5=?QyJuhjF#q=3*LkS`ZQXxp3$6=bMbt0e9M(+`P=+I&?GfH?d2wV^Zzq z25rp@3lIf{gqCQLUw=0B<}Y?;N13?9#HKM;!`(PQ!sjj zL8_$A)`9;@?_`7W#D|Q|v}~{fB^%oR7JmWb*??A(7)@0rdbUH0{!=7_#sW!Qr>&+c zlg3N#NhUP->kzS^dz2!ZaF{$%LHdJL^BwzT+2}mzY*#nFSgbFpgh$oRW6_7BU?3>o zR$6^Fd+I5Y;t~P6c=Q;Ydy*Dy3Mz;_SsMi@plp~`J?Y6TCQd0OhGLsI8$(l!S>9G` zc)}bECASg!M}vn(>Q_*HkGX$0OY)f@pr`rzV~p9f`A&@gB<=I!ACXALH(nn=*meK~ zq98m8HSSe@VpR!5gxmrLrzOHD)ksrzlpwdj`v`Jqw9R-=1-o=8S>bu<)h{OaVXv1w zt%pP|G5kWV>dyeTFnjGUz0yz{)qcbIouQy~j%i@3?$*{Cpay26awIzR@g#5#w{m1V z0@M#9w@pe5x^ZI0N5Ltm5!%1WS0}xv$4C;uDz17F$b!MQ?GuyB#jcn>GwcrnpI=6_` z*Es8gCE(W5XfFRTkrh-$7t$=&upF zIaDCa>NkCimRQn9Cu^ej!_h5@N3TEp&>q5FL!y3J?x`L4dU*ZU(o%6m(@lk9=rF6N$~_7~;uwmk9*zy8I&)l};3X<6eRNf*qoSRNl>6|FEK2N7N}KUXRyOt& zOOWit$T?!Rt;vk~jJ)I;BCVKICgwxQw&jAaM|3QT0E?K$q}Et&H>eiJGc! zfx!D;p?jdN14!185u6o#m)x3cM~{k7AX4Oq@Zn5`c$^yPh&QBM=b1n>Qiq@OpdNhh zkxK2*J%UrbJG7ulVIZx|&h12FZJHHJ>sJksKtLuq{4?)J?A0P1L9E%Ytnv=(ObrrR zslXYf750S!)?cVFa@!?yru|=`B=}v{N7iIwwMvn(I&wyIZ-d$CIozYt@#GZ)c}Z1+ z?dt{bl?1Dcj{gdMGJ66mZ{=P6Feg2xDm(Bs=;B(ea&9Go!2KK16T)oRJ6(XLK+u@6-F~*?o*QzlOq6Gus34rfJUp79ZF%a>Am#6*MIxa07oAsg$D0R+&N)rs)9d27+tb%j&pkU0>kKwi1Z? zlD}g?K%8NovlJ{N$K~|-e*eHyi!aM{5^M0HmUQKmxA*tlE;wzY+Tyli(`WpM$h}}| zlOg#j#{yaWZcjiD6-uNkulLIx^P2)s`)mSjm7n%lEon>x5<~%RwAC6WRuq z2O!h<7f?`c&>N)zBoyU{w}d|$%49@|4XwJS*=1fW@6%HCaAlQc<^q8ZL&3TSNG}V4 z54Qams`Ife%U1DoR9l)y{gyN+=Ug#M*tP=CpQyXF86R`#G{3;kO1OOqC3S7fuAnrZ z{(+}2{wmK$&2-S0h%^u!K7JsnZKvp)H&C`YE3`u&Mu7pR@U4(v;?wrcu-97~OlLNf zyV$@+KZ`WE&q`=RvtF4Mc##huc8y?2;M(6+UI(9vI_y@)p zU!x6FL1c@^aU%FqR=qqGk=O1+4R?#YLu+bkLb9N*I0IR(XzQpmeo)c&;WYfdbvwyB z9@5=*cnfkR-mdV7l#{gML|{Ux?7-RURjbf1CZ@_(I>WD~pX@7AzL;@RBG=~joh9pp zBAfK{kM||A{Vfhv@4ZcM`edzoeR}?!_3gw|z6U#wCz;88&`b7#4li?^deuI$?VlU# z@h*V@9g`0X-+#mwN)mAa(({_jc(SbOOfW=6`J=xyGx!>|td3m&8O6`T4uCbi?Tz?h zlR{Vl!W^-E;{2ti6%2@9Decr%wn#~}4tW#tM!mTmeE}2=SU>h(uZ;_$YNCaCFd$`$ z{951R`5gEWIU2dQfv$;;Bb}o^=oOp+CY$>`>y%zJnD`VrSO$OmQJ0($lsRacw^KFW zVQg&4zE?Fpodb3v2IZ&jDj7t!k9{%Oxm$WC>qZB%12&MDwl1gs;j!-KV<(>XO1kX?6t_3xT$4km-l(e6tsjW9>`yh zPkflSXYH()hv+H(;{8~oX1zrkU5-OBu409A6zLcd=x5&9H>T#NrZ$?C3N<*qK>EpC z5hESnp1?%0O{y<{Erk{XcS|1}!bLkHLimyNF{GHP7R{&5XTEQfkaB5#cJ?rI2ZfA> zCPn3Ks|lfGhEs2V_+SF15yW=q*58H3=^6@WyGyWd-N)oB2uV}%Mj~rlfp9NW*B9|t z0~4@=pa@SkEA*dXI&4sW;|SqAMjJq0f21-hkhn-~T)k2`9*U~x8c()zG7oM|smm4; z&|a~j^(d!RA42*13!yzb~21azWIt=MmDV*_LNdHd-h`Yd(eBXPAcU5D`br* zi21Xh^-sV@5G<&O%iLARo&S@)Z!Xy5L>>dOC^;3iv;0~3Jg#-=B4V>6%iHxrDyoi< z>Bct(yG;I;8xlGKxyftkd8FbI$I?}$Ne|vzGVgFQ*nmA&#=8!5PlLwOqCxVa9XM%$ zmZf_h5UVDIe&U#PYzDk8qR)|AAqjzABB4ex` zdALWHKZ)5|Tbyo!DU5|amrO=Cpz zkMbk!b9bhb+V41k1GwcGn-Zsxy94*BHa5P>y_PrftDx_Kz_YA?>JwHHNQpGNWusOp zH_6Y86%>9=VWRX!1bZU3um43)`|*zUptzIr8&mEI$Bw%b{8MN$6wa{GcuwBShQ_Mu z$2-z5l=)0OC{2|?8v#p4R;@NKxce6Y{?{r?K}Uiryg-DYHiGF8i0ezINV zE^zANK2@Nc7U!z$%&u--Lh9!fq|fN3*271D`qDJoXj%PYM0F5agUkX6{pX*VwNFOZ zE~k!rXYsnmtNX zP2>&9(1^;nbeL5z6)y{y6=XrHIVe21h$SsijY&a02D%T%j5?0hic*WAZ(sQ-yxC2e zGIK&)YAb<;7BD3qZ%hm=*MMypl?BSwlErYy$sw4_sPuT_1dx<7mDiGwTZb-^^b>E zvBNcI%0?~ITr;CMORIyKpx#mE6gbM7b#-MT=79D;+w~)Lp_zru5c=kO28#5_5G?AwidedDA1J+J)a)RFBAG`;^c^E53Dp_}GLqUQ1cexpC9Tz?t z4EkiMWu+0V2QCe~j^h#ji5#j^Z~V;OUd#REE?20PJ<-=Pj`Ov{$}=tp=04L~7vAvWI z@#wZ~e6pV5JqUoWfOg6>cfoiE=ehApP!>@jIxla5T;a%vu_kGW+xz{K7q`j24G}1G zvJ!FLS`Q@HudirO?q{#d2WEQ|L96}Y9Hs$Usc>tFLx+Ex6rA>GF=fi(Wvjrjhz0FD zIh)nQOiM2G-3~DG)#K-O&NIZD{TC$F8sI{UlayO zoyr#-V{3Xbr+Ovxn!{#;7t>BO4nX$}{nIh0UFtPd=d}}tyN7U%eMqAl>J4IeH zw4MAl`U5X6(LQ#_B7ZH`TwX>xbpH-;4y)N*?DbGm7MlgqjBwl5P;mpI5%ku%FI^Jy zoA57(=~hu87r`_Bh!xIt^gZ!y^~t0pGgLm9P{6kZ3v=9098Y(R`?{0Tz8d{Z;kRK# zXU85;t;Y{9|Eft2_YS04{cml!Efp}`y(DHlkLl45K+k-T(PPrcWzfQy5-&LCD6O%% zvARdxZ+*P!6uXJpuEZs!W3*g#A@G`!c_UygV*A#bp&N#^&P7y^YL@ZS{jK1d9r%on3Rlm*xH2{|}am|J5+fHcvJFBG(fC6&kBjrzK);;Q#+t zV$d_IJW1XWiX+t!#IjW(IgIjIERJ?|uqrJX$XX=Bi6i?N^o_AVfx@R`rjOh|=<1*z z0DQNqs5fVyt5co?cT#S~SZ>3l`ftIxGvH-ZtG^64g^7&gxW1>o)BbbAv>~^WS2s3Q zlpcW@#m^CNH3y-d_p?E8(0LOhZRDB}3=No4zAYwOJO!709M#(-PnJT7Vp=y)hCOD! zkvcTv+V34O0;eU|2O@~h3I)OAkeV225RZ-jI)w?ugvX<~q_o5&OPC@UrD)Prm(8)i zCJSLo&&LF`cC@Bn{YFs#5C_fTcL)+m;;IU+ZpMDv?MwVoG9zlDrkX}v(*pa2Cj8t{ za0M?LSgPefqAm@hxPTX|ALd1p9tOILe1&|2zx%< zJ+)p|v9*l=b>x0ER*_QLR1l$!E41nlew**1tgEXdN;}allYp{_%R62~b_6)E*P_DQ za4$_KSyv}VAVe@nkDee0Qn0h=pEbHovr+nUQNu&W;7c@;n`xxZ_GoJ^w3jr&?;T%Uk8#X=CaZc_C%PH zB2LVmHS4ic&YsgB9Hpiw=%+nr2;j^ctN#jFg-y+5g9m%+LT+K|Qz5CL+Q+^^LKcB( zi91rQcR`sq?UL$H=FLu9O3O5wiCP})#Q{Xdj6#uirHd2Hp&CISG;rJUEt^=~{0Ik5 ztTOitQ92;e3Z9-Ygsw{f$Y(D`l3Pj5dY2UDh*9>S?-jz3lNnNDsQeW;`^*D9TOduDweJwy74AGn(}fDr&G(?gX{=`am2)HUfr<&_&A;_JOWx|YJ6d`@8iLA-N6hLuFX zDP~W2V(=fR+eE^)rG(G4A4}E8cZggFYF=wx=Qy!mmR@?|+gN!s)Y)0!#*A=un_m+J zkFWv%lh}V(`sjC1uvTS^js4J|mDUlFsNh9rF~IqoE<}p7kMYdh*g)jke ziGfL(!tlTo_EvW`N|?25B^d74*DfiT0$=^tNjQ!s0-awOXxom;LBE2rgrzwzd{37n10y0amde3@amjE^(n4KIs+=tQ#Klq5lO93 zR)T+TAg6iR0y^~yQe}otK<_eHY-SG|H>FKKL*Jam26m%wdN}q`AtPA`cfluZT)%r( zD0e8OL(5ge2t9kkD~0p36?!Xb5$*MWoCO8MQ(d{V2k05c2KkzAvZEc;ZF?#H#4k)K z!7^_%7Y3XZVIG>CS#1t%8Vf?;N?H`HR=Jz~V(b0qR#paYO$mK8Z$=UxHzqgug?(%+VQ&VS{st@TeOrm;azW zXUJLl=wMF8ga9|#mE4!vT+<&Ga$=%0U=H~8hY?z+2q%|V7NfhBc)Hu%7$Yznk`-bI z9FQyQY%EE^O@Eftu|u&G!eS6cq|=fU;k#ccHYn)z7(7JZo7Xk`M@$1wb1i6-dfr{| zQ_9U@H>{E>(a|&+^!4@FM??OKN2&8Zlh(tv%u%)sG+B3Bh04E^o5oN!?V7;&U{Xn$@QzH&MmYTHW)%SblsgB(?zU275I>8gf}~QwVmA_& zcYWIgRP*oh!b-)pl@vu)y4I30BlD=zLPw`pCp{rOyRXdZ?|EDPY}EuCDy6ol}QRzVBpH$DB^SOq_n*Y3MN+ zHviX1cQJ9wPNKMn+-RVkhDbLGj<5a@;lxKB+kie~B|NVX7hhtFnY9Kf->+DDmnf;# zU-GK-6^>YX<>+rEo2q&3Y)_Mb$#h4s^r&XvJ|-OVEQ~oAwZ)lY_d=Wm_sCOKyX?6y z04KAnte|YH2rt=KiPICYM#n^v@d;yL*D|PX&z|m{FrPiVS2&RsRRPVM1XNFqf?r=5 z+&HncUM`#Zn(}d8l~#kKonAVnl)KUz#H|S{4uZA}vC$Q)t=cp`_2A z3S#WXjqXR8K3d$E=^TV(CFb{Gy;Iext5)T;&o9{MxraXce)g5ZZ(_S zZL=%5@*y?4$Swa|`;X4EfmQ_}Id5&lgUT-zNnh|XH&InPXR5D7uijSWYxCXtNkQa8 zYP=)VnK6wlVKyLo)Fc?&LKaJ zIo-MUU+!iDp^>t9^QOx|@1l1?`qb2@Fi=~uvW0J>OZs%9-D2_TMrbvj!OwJ`!Z4yzqS zRyIFdvT-)qTR)(QFzD9&Fkc-%apqfX_1Y{kR$|Qk+FeF4Iv#g4m@Kh~G8iN&MP43^ z^vS)V%7IBw0<^YJlu93SVC+(B0T3HyzU~T2YFwbEdxh_*;KY^r|Uuq#hfzoo6%qQn`9Uv;W{a&Ywg-B%*V!(q*QXGRd@ zD2VCE2}d^{Lm$DfM!~w?8ywD*_07PT3>iJJU$r@^twDg!Ta_#DTD*BN zr$Bct>hAplyRk*(ANECiwSs=1E?eHHoT_Wj7xZ9HfNT0p_KanN8GH?;eOPdYyh8kh z)No?+2gl=*c-JOm`WvFWW`ZI*BSkJe+cM}p@HW7CuOIlf+%2zd@Ct*<0n6>Ey?_Lm zotyzau<)1JuoaL&ZjSa;d-0FF0t-M<+Xdo-!QH~s-I+-jPT)+yW@3)UZT9INe9=;B zcY(K*MytrHe0WuR5bYE?fpie~z*cL@)Gy1M!o5Q2vqEyqMD%~JzHD$NEJ$TFr<$J% z9rV>Kz0&V$VV3Ik@RDm)kDIwP@x5Z@Ao>{4;i}=(m(TWiJ=e#x`h2|vNV}2gI&`|f z;c7`!i$kz$gmo&Lsg-1X?$9aB_N~~tKY9|br)_p2t~TBLfem}_Uam)SPHuJ&yh%8w z?2vfoWU#ufi*acB+_#3^`UziHgNvqEx35UO!6aX&OSf+2S>1X0xI(5l&9_Qv?_Iq1 z@4dCw>Lpjc6b-nHVb0n5C(UfynmeAsaWqQ2Mp}73Y0H24JLWQ0Au?=zDdvs&HC1@3V zWjSKoZPCdn6Eis_-r>%YvY~(c3d86RmHT}5tZ?dNM8J;plt{hi@GqecT247MTz!N$ zWxF73##U>cDamXr(uqiOGC08sUWoKz)RUz=Z#gVX93ILAp@6=TmU*LXR%P$=fWTYM zi&_SvNI~z=kiKuGp*nsk^Nt)C|Std1>bPhG3 z00I0z0-tWhQ=&@-sSC+exQ}8q5RTpNLsCW{-3; zmq(ghuS(B&4*uGGJjEeJd-vT6^pV8uL^*{xxxfCL@BFS-x80dLo;IFOEe&_bhSAmE zE&Xljs5X?)fA^yYU)gfINH0QG`NE}iUDF&L$H5}mTihflVpnvm$N||yZ(WNm^3%1n zU|D|GrE3Nb+l5%G&-q=i{Pke3NVa>n+(G~E9Y{UHwFTV;Cn4PqnBv!tsLe%Q99?Ut z@9+jSFqvRWOH76hz#7YYqs$CZ4eZIkgvRQsNguzu3c61ZzS~EBf<9VPL#HC3s?VQM zo|*}P!qOW?u*i*W6v&!rJ_zzn_r@`011=e=uzE2%?gFI=^(N);S;99(Ma*O@zEI3W zhCw9438c#u|NpV;ts`BxO=lS%FHtgLn=*96$4~vKfny3y973}owZ`oL`z6?H3r-iv z^uSBgNBRnWmyoh}s}hA3`$L!`$*?W7j@iu6Q4%E zj0Zl8syo!V2-lJLB{ct>Ccnj9rrz_%G_pA?fxI?n<3Z`~uA>e0rN;9#-d=_#8Klc6 zRMtdgErP8N*HZKkuKk*t*+r`DbGrPVKR1}8sp{AW`w@7#2vh!(0Bn^W11-vCm?t;j zK*S7rFEV+W(3YfO{&&*OPXt+UOsl@JKQHyhgKv%f)$#^b0|)Y`!fwN@p@{u`E-<+S z)^LEBxG+rwZ?vaLPOO&knGrw9%1DWtAxi-EE!y^WTpc%M_Jx?~4+ixG>7`Hp1oQ2v zH)?8Y9a82lUey}uy7=17{qV&kneR?=?q@r#MpRuJUBA$${}R)OGxsyrT#{b}xG+v~ z5n4^|to5(zC3^-6?COj56yLpn=Ix@L53NY*RQ4HKY9oF5I|+m ziUHBoR`Pvzwv=Vzf|Ne05?%DK&=UtlkryJuyUw7n$j^7{MF1)U$Q102VU)lRBPXXb zpOZbw&&Y>SI=~UL+M!_h^R36Zr3)w{a#b}=wc3lxN$`2lwL%PK;wOf?B^oMCJnjAr zj8FceD1V0z0=P}5tguwy^0&J1Opp}7Y4dDVMCXwoEO24+W>I#Q1Qtm4W3 z+NUZn{;rTmAN<`MPA8vo@-`59)~ZVaoz%SE^WvU*tU+$rM=Jb^xh->D$Vv{)ruFGiHf~9&8ZP z%e?=IcZ1bV#TaV%Zy1Pi#kJI(c|T=za?1dHK=;cwOu; z>Nz(zdaH8qK?TCGKwC9v~_NJ9IgZ()zVWorgxNf$3)?0&kbee7V-O!__v&-)7lgxh$i z_t@xm272@ZC~!1MLC2G;xXCjS&|?6l6!^{E z>IYC#eLg|&m<@UbuHN*t#lG>yx}k4%M&lD5;hXgxkQ(uo4K|glJw%cKn}p(qqT5j9 z_Ou81Ek$@&0TpnM6EVY#eIDG;hXyHFrSkfIiKpKEbTE z27{=qFHkipNG1Y5_4a*KRDZiQQ2WF9cJ5ZQ#d4i zz~*UI;v=;J@>Bs@#u$R?g;(P+0wvyAI(Yu}4H;@p89}XwAU6kt&{g9L6Gv#AZVSlQ z0NbV+8aooN0xR{D&?6itK}+z641S2ynASrrpY? z+W=Aav3B+W%gfc+rLEbe4DQy=9WxDNYX8!{3 zz5;Ge*yF8?oRfLxq5FH?t`&NgTB>zk#vajVo>TaNQMs6FU2tcq|HV>Y zft~l=!)O#TtsUgxbbvquG}jR@V30>eZUY{r0_szqb^jlQuBqf?LmR=#YPL@Nkd>Ov z+mR6y_k5YEq+_swp^=eo#|$-QW<{N6*%L@4d_NR12@ctX$R6kz1#y-Xn2SmyE2lP? z^y0`H&evHJwV%8dlz}_bb43!`6!d88mW?_q*z;))(fuo9US%o6tqA`- zP1_GI(dqFi%Zj|CNwOEoN@G>xB%15MqWW2F)GUin8 z%gmP-`a34(EM3joZ+X)*^{oreX@|Vdg_m-aX^vVQ*0C~f*(KH%Vt?(N7iiV#RUF#G z@nk}iKlBv_yO&3I>>0lnB&(RuHwv{Op6d*z~em#AS@&e?&2 zm=~uO2ZE2*p)x>13Z4IcHJCao+@i3D4)R0ffvajOUU&g@vN8lG6=!&Rzp>&_eq4Jv zsah^1V9|8-S_JM8^8FLHjkeof7snjELNd=hK7SXz=XZzh%8uFX@HJX(PlN)}_$34S z94Bck&pQd2v5D|*AB7cJkmPGIBU@H*>ur7QOo`SX6^oUI{ipdP zr59fUPWYX4#-l%6mgi=1=jw9HEx$sY>pRSYH;RecbSN+mpeyO;{YSh?eo zCO}bb{f*47Kj1{#_fpO%QW+^XLFQtmGSa=Pk3RjH=azH+Fy7N_?>@wYeahEkJBi2A z%|$Rx?ne3*;8e=p@PY9S_cvl=vk?JkG2~Lz^{YoP7ahu7bUy!SPyhty&oT-xU0c$% z7Pk&xqbQIuA{N8GWrJlz+I8Y4B=y_pg>-CIn3hjwA%>s4cucSd8Z!kSG0>f-pv8bi zT8mTDD%Iu}Z;K;%y>jf3kj9U+UUZcR0S&`D2}00QF-^(BsJ@ct$pDO`5Zq$|GE6NU z7iq^MAT!J05a?Qlu(A+i*3Shk5y@4Cld|mlZAL4rQ<;SVCj&A=t5OHRql689ux5wr2U-lM-29scUz@3jaK-Nn{loEH_Gw6N zf8$Rh{wq|LsZ_LXIOU{;AF-Buu}ldp16cbB!; zj^CzZoC2ktv15DWh1M4|C@xRK*OK!rM4;*qL~a&}Gd7-D>x-i;x=`+{wR_-}%}69DydP11GLUeF1_rN%A0F<|z4L`E-=V^-KnCr2{Ju z#IXO5qAQPQ`v2ofCAmV*u!=%L3OQy=z6hbneXLSBmiroJD@1avC`#D34=!jvU8vjMjeUER>&Wq1f!CKOy}{A= zr3`}!v}R>s%x)xrbR2ClRx@;kX?}8V5GnugkyJNY9c?g( zPk5X}&4)-<*FfE<1XV^_yWX0ZXwe|6an-bUPWoH&a@TNxVJ+-jrrZ|h?RCJzZp2jLF{BQ+rb-V5H1Lo0@sE6u*8#dU z{4bO}(33R)y{l6(Yp|K-GO7dhDhX=B(O$9M`mAKpDs`%BwiAL4U2xZ$a95j#*Vlc5 z=W~1ODLDN5!DKq_=K@uysT!Yw-wQG^CH8VXgYD=WkJ}WM-HMey0nIGM-vdrRS}uW^ z*W0g-wj#hH8|j8qE>9BABYv#i&08m5A(tafaUZ&lou|0#9v^LzS?PJI+Owt`g+&@Q~JHoFp~X(fYsZ4ymgxiN3#;jfgM^xPNi;*wS` zBkxpcdoJ1iGELbtcRquiW&B6$&%IwExLM^$b`9^KP19yP~lYs5f6hfusmv+BIcdH;UGyJ!;6hU|rkjUW2xNXA>G3vwOpN z0U`iou=?0$fFqO49&7_FSa!Xj{4typi0U2mq6ql_LS9UR{4U}_Qg#FB2mnGxo?yrS z!ysY$5!Ae9YO!fIfiV3t$_;<{gXD6oDnnVC9hB?M%i&(*T%80OZOWM&y+ucqIX8nLa94As}fN!^7NQqy$YCL?qPf2OHGI(^VR*@7cI9VX3x;i1S{nl?xsfH&u2Rz zQY+I3HVeG3GN1cdsI`BU_cSg$m3`VEV)Dn^k!(eSI~G&dLPeh687Zvnl6zDb;Ci*& z=~<<7X1PPXUU~x7v+~kfquUNO`_#3V^D{&$D_b5^5G1skRF~S#x@9|Zgs3|s8$njb`)H}ejtZBG@ zY7iu7e#gl1$&?SIw6=XqFlli48QpL&0}!0|?Vn?=;%j%n3HJ|FGhIBF1?QX+(Qs?W zEBFmpZy_WAt6sM8QS+Qlr!mB(53-cWoknNRX|HL%c+lC_qV9=&!%?r9i>q~B-aRK< zyR~;7+l#)->E#~KSdRj!qF(?Dx;=ntZzJh2Umgu>1sdJ~T^4{GbwgFr zb+cMSOe3YcH)Co^mVCk3;ut}gW3^=SHSBSwv3Hssyszt%UoS7-Zu*?+Uc`t^FFxn;LaG;G8ZOXi)4nKSECdSXByPoRLCGsh zGBiiAuv2$l-Tfl3t%EpY>1{Sw1-m^}y58%|CTR8^s(&+oAvDm~vtLU>4f4VdUL&Lq&r8^n zUy#qL?LGelc5@?lZ3*WXVKNG$qC!R;)XiPy0f|u|sJ}=F%Dy&SXM;)UcsbM^vPIv|Dj8GD!Mh)@V> zXN^hhLsb;>!c4L!HxLr^J0!YFc<;`C0xprWMC|I+&IEZamNYvA*9U14%6s-IDZyb@^ftNQN>8T8ArrfamT z!#pzu;-4EsCFuQ1n?L>4O?QqR>_z>RSD8tLy)ZK~-6FD7T3bu(1Kv)LbNVOsHx^gP zc2ShSyp5{HDr^oGuD$EKi`T%6LV3O??m2r5V)CH-FWjb{aSELzh3^|7rd7+IsR|~ zf-5yj2PwT9H2<|$jw|5LP6lLH>h#o)#RpmHJ&DfX*|nF8q~g+M&kgHO|NnZ+r!A!W zHD|N!zDsRf|D2F9V|TT-x#h8zanz%U0HQWL3UdbA0q*>OB59gq$51*r#=+hUMlys_ zp-7Gf0AZH`=`7t|tXvD6d6Y_ixQ4z)O8_4ieU=%U4fT4x661RTczIL)bAjU_^T<1u z(@RZ?2Hi2cC145=5RJd{_n9B!LWfC^QGE1bliiB_58^eX_+{I-2!n$h_dhjm^34r* z+g{IoTvfJl{#yIEoMU;#EaCcYPO7SygC^*QaD86Rq2;-;RY^qVJIh|}dOnpUx2nlG z12pww8+36{N2=+XKY~{W-48loVe3!Kf-SD9Ez3WdtNCvit@S{k?Wpz!HKWrl4?L-x z^4|gBTRh890&UFiWA?VEEAzVrU)Y)>?#%`5`8e?JG{_?nT4)MJZ@eXz`KqU%mDblt zl7k&Z;oXxRn#Od{k}N~zTX3m>&-WTVVNZjg#z-wxh{jf{$$ZoUY+z_SUJfa-lWa=cg-pe*DvaqjUUHd9{2XLsqt94#&za|VMgJKd{zDrIP)KgDRJFNb%t9q zM&F(l$}SvX@SdSP5|Pv)`JguA5pDiaRD#2BeviuO`XBWut|W1GK(@A=dwF~-LCj>j zeWxI`W%qh|ut7B|a_R*iyUo{|pu#^h2Vr8A3G^FRs>-%VYEEgVMg z1s~_00_(vvJ_V-5t0GYg@v^sBX&neT%n(Theuo5TTHFr>$ZbgwD6e)NBE)~*b zy;?_y`~+Qu@CU$xea4ziV6LZwAXXPi&Kidicc)mF+usbVSadCbJr(NKh(y2S;H%Maz0};&+KmT9vK- zX=mH|*T$!)xpj^Oan>@|QuOL;Up#)XjvFf?{p+9*io(JAKZfv6W z%nuXyb;Jqwh~>|fvS6QNm3QEh;DXzZ`%%9Kx=3~Lz+;J|Vx1qD)4=pU0D-D)+B5Z> zT}k(mwg++pD|JZG{|4LOQn)PZ& z0<-EGbD@)x1Unt7#61fl2arA$EhZ@oeV$?ZG@3`G-(8|QkAI8svBB~xPd=}CCAzsZHS|d5_DJt8W7mt&_BO*2J)L^rDs9Chqg|{mdz-Ut{}gm< zj+TDEW;eB|?XmlcS;yAl74t>7Hb5;afd*P+H+7v6$ z!6DOgToU>y(jc1@7q>4q3>DKU;^XIZF|>h$t49RY`S{Lwe^*!Fz_6eZ^_|ek1Zo-% z?hi>JiJZCDCWdyNfI3!z-@XHLX1oEeIE`v!^eU?0X+<$zt{++2-{jKvx@Bb6%CT0{vcxCWFEkwGa z^P;aX5XW5Tf}Nd+f=eyUN{-c=sXMXMv(bSSz)|l}I#VU9(r0=}_D{f0EXbOEUICrJ zzrr8E29u`Fvxj-*aB2ovudDLE6p+9{c}6d#(Ir2dFNoUY%Z3y&++n9s;5NU`%+3l2 zEt605IgT_yM1jY%6P;x551im(~$imyu22%e5|!i`KI z=sELh6vK19T62Uq1|eBVWm(GzTf%>BOm^{w13AYr10d-+3ilX)kh`Cufxbx#23yJe zuB8?v%Z(22_EDvUf_9g!(KCXy;$()d6jCuXW!1nvV`}E8k;=}h)2}JQT$qn{Y2RY! zQiNAdcB%ND(_bwP42CCvQ8x8|{nzUJUv+TxX917{K^@{sGkW;O0GOcU1l-bY)8L*S z!JI(CTZ(_ZpG~ z@_Xc9D# zM>F3oDSSOW^!)3gT=0+?Ec82SyOP)DmfkbJIT-$8`Nhl5s;*80@8z+A($zVJ!q?DQ z+mebmxwd+hIsx_1NFh^VZ$+un%X*oT#pc_6*M9yGJO<4zly2|y!!@rAN~j)-mU(?v z0RK#~qc`GQ@07{5YL85%7>WI@})np#7b@5Y3t9eglS^5 zKVdy$R^t}>0$uX|nb&^uN_PkSRz?NO^s@EHkSdtG+T^6<)W~#2-`<1RD`5%jUA_~l zT(ApsOzkQ7qX#>Nel-y>{-~~!LdA6#-XgiS=;3N7pEz>kJq5>b+mgj$W@pb+%5Nf` z0(JbQ+?a*Flcn`+lo_aV^NFw98WPhzV|A~IH?iMC{f$!%f%vr};zlym;Z> zvl8kQFecn6w#hd#d4s$(`mYk_y55t+0`a*&!i%YXg2ee>&N~|GGl}Q3g@?>=U;=_I z*JLU#n6k?mn(%BRPewJc5u?*X_UK4@KS(Q1OA60?iT+23yxJ(Z$yh2jR&v!TJg-=& z60y?Jck{s)D0QoSZZ6ylAuGTiW?tO*jlxm*-m73xn(~*`g9cB_1x{Sc6b*kOKou2C z9)-PFv@&t4Oyi5eVbK)FKrOGC;hhhj@srH4m`c;qw>g`DU&;FO(amwK?U^RadwXpc z#GdFq&Ay*4aja0+!F(NQap=gN`Xn!9oA&9IjwP}JY%bh(CcIuW?31$DN4P_Yx|xu& zeS2gf7PYW!WL7u5IcT&Jw2S=R@_ge?uxb1TrXQ(Bj1DFQD>^pd`m7$(Yo5iHQR5b`mxVDdJx{Py!lr^6b!A|S^-!*qksL& zMccSx=R1_m-s|<71!9MYWnej`PClYde#iZW&!ebM*Ay&GqDPP6*L4!j<@=SAj2XY8 z7u4meIhlJU6t1g6rF(yvLl=JU+Z&k^ll_SxeE+r*q{T>e8Sb*NQSVnV^l{I9v!o|_ zI)OpCmh^y0QUi9XO69eaS`LsgZ!e*!v`T6MvLKt=Ppun zhr?_P)Vq|F{d(`6jbk#^;af{+aq$Q%8%Fuq-46urq1MDNQJ}X<{RWV4gZYy?sToXY zJ2D*~O|Id1s14mPVt`9KG~Z)k7{d(zJNF09L*POh3@mglic83uo1ZeW=NrvXgD5G4tGX8^F6ZAS*+N<^6!$jXr9sg zl-mglU0L^EJZd@v{%5A07|Tc<~lS0RZtpHE?o>DRGSe+UL__Xa}K?@o}8NN~6> zR(`DMSU4H~3M!f@nW^$p{bS{xE|tQ)R&lCNK&zz?VU@9$jHjc;pJ4aFVAejfMKdG8 ztv+RJo@q4kXMOPctDv=3{dw}8vI2tB(){1vN^KKD)Nn#8#;1W&qIBw*l9ceGInBXl zXrDB2JNE%)kMq?}X$RD!j=L+U#!0?67y9kCPeC#A*l2pXIFbTBu6VQ)o--976 z=HbDvFbPcDjuDhvxbP!tO4HPZ6<_&Bf@pi}coMpzOwrNb@O5Goo z-^fYy=fw|+?FQjFvzxvnZujk4;llRv({q$bkGw?97mVWfw${rq<;L#~oH52JE zt4$!ZRV?sDupKwy)FLp@w|i(mR}M7gylla6OvfH>Fs{c5q3&Rm&^{yZMgyZW#Ausf zvNM^xzchetc~5_3iMwWfwJzoFDVb;A)xt@OtrL~{xKBlsFDf?Oo0}QgN6&Z(2%PH^ zP`U56*~1ayM)F!Q3j7Yd2s)5~n+#=#*71tkr1I*bmV|bee&f|0!VdroIC67c*a1~;9i&T8B5!(i4zWaQtl^HyKKu8pS>+fCmPe8 z!8`-o=bxyh0h?;2Tk_e;sj8B`se;mn&c1eD9^=($-kN{ZUEx;orZly*Kebf}I#%K_ z3+C`u^nd(Mz&B^s@kE8xK!Z2bF0b3cL587lTsggWeoyUKaZrMnu}XymjfVk<9h!0x*$7;x#GcDlP53xts<((oG2pesP zERq^{X^%+MtoV8eXv{aQxh(-``gakKT_k1MoxXV4%5s!l*97Je^KO$oVE%f!O}_O< zizXeY7mMGpN~WJ`DhJAY*?i5$jJ`0WO~tR7sexg)%*xMt>^OU*n|6qYc$tJ=k5r< z2<`{}$)WA68Zb`{u@k`z7t}O@>64bh4P;!d;l@@3y6v*YTEeT9L$5dH1PfX+*9#|P z$)c6FNRvtPH_h42uU`7h!JP>6-)@|Vv{%fz?2`A7!Y`Y@v3>&ge~G`g`I`oR{GR~x zvcw~}Y%mnGf!Z$)LR#uv|NrCrmv}iOqvIuFIB9K#!pLJgGd?`f~<{|%1fz$fE$o_`cd^7;Te%!OT+@96V=!Dx;DE7Ik6 z?w6UZyFprsoJsg=jq?7}(oqY_N9{E&|MWM1Tz{S|I#J}UV^SYPcRf-4>-6WZD^sV( zXg+q&OV#ET$~9JtGyWMMRCphMl79EVm5md3zg62wc`xYh;MIjTl;)n;A1Eq(x)^^u zA+osb=YmSIf$-gXPY#&bEM#Izh)|68gBM%ZxUHC%AWXZGnee0JAR_Oq!@%{NseZ)w zQ~8$BWfRaQW4oBQ&!?K#Cq7dK8ryCag7Uw?16Ht?eeb_O=Zc?ojLj+S^b2Jdf5 zsyTIqcIC5s`mQ7_Y(lOLBEfrOGE|86{7q5>)Q2%*ssy61UhlGJtYIy9i^o9wjbd}9 zWUz(2&4JvLu;rMty%d1JdxAG4WKmn9Di2x^?;Z@EPo|7TFCsT_(x8U?r5ztMVREFp z!iKsQLXIz!e(EVt@lPpSC~M?2G*xatB<;RV#^o!7Fa%UytPCc`i}D!XgWje+Qu14q zUHE9Ud36CcgPLQl_?-L*y9U3YD(|f_^Ju~B%dRNdw32~F=U|2p3JSh{X;!zgWh7AO zwwYx%IvQ#}^|uO1sNbmNxTY`qMv@)=Af^Y+6?yqZ4cocED?58m zLYZwAVDe_{HxAJ`7dYqg}~`0yfmFn2pYYjUH`hL?W`+B*?3=fu@^QEIQ&h z-h!w=h0R!+Rja6vhv;(QG#`m;%}t+hmigV%E5p;?-b=2`{vys14NxL0p(;pb=&qeH zHx`62hv7$r$|5^de>~lO2bcQ9D}x=Hw}yyep!?O|<;F2%)kMR8aBe>svP~gA3NZ;P zh>2-R2qchyy^4nny{~k_9lZS`W!|2yc^e4INhuZPK<&|HhiWt5jJ;WKh)mO03?DBm zD)`#Sd(|d*sg#J3228tS>y&p2HnO6I8F=ZS+{1*EzX^1#yd42KRr-sVmqTas-E=Nr zd`jC#VGd&JC~%e^Osd!f+u6o!6Su&Q>8psJ1+&L26Z9B>mH8JT^-W{5Rw3Bfx6LF0 zr6-A;-Gt3wlEB!f)@hO!H2Mxo9QMaVF@G|g--Z?ZwU9e& zzmm8HPSVw%&D&Y$-ylveZxIp=VzRTmqO1Nuqe=d_`~TP09)i;@D+{OyF#9f^Z|0km zcID8b{*Q5dg;6#Z3VA>}6c2h9Ttw^k>Sa3=#!;z^Y@`031Zy~-$S2M7QRYM5vAyu2 ziUqL1vnxP+*NSibPoPTbPhsU+{*7jA^GN*Sc1z-g&zsOYSbyEgznG175Iwns`1XD>R=rXF{guqZi zN|;HWpel?){`4-(2D}W)OJ4<>Hq;QDjNI_=d>ETjEmrDDS118a$ zJ=e>PnvK3{8)!R!8b+UGeJ6Gk(=dlvyrQa~9SjMPMa&6BG~u^hx!t=Z=8X5!h%IPT zQ9c8maQb@ogH4D#_HfPoiyMcHhCZ$*Y_BYvgeCHWcu%LXt1Ra82e%xP*B%XBBvb~8 z-Vbj-7EcAZyL{u6L~1yu)0OA6L3HKsdS z1h^qo1E=|+T^PlE$NmVY|2R}pUQ-op*c_XkH!)pxVH$ZV`^x+$C`s7WL0|9V?scuI zkDo>|)qSu3eY4T;JgITrj-LGNPPBIX7x-Q50K zabt^9HPHX6I``MmnN34q59P@h&3uI_cDxY(vUTSb#%mys=48@tLxCA%N-&)=aYm4_MV>>*t61aTpak8Y}&d}GU;m; zlQ8Wvu>o$|_kUc;I&Sy9bay;WwN8e{d=JWNZTK(_yup0O*8TgJM6=+ks^VKgr1-Y% zK!m`^Wuc}^)}ZM2JEX2;4u2mnl#OA9rp`* zXq5sa(5owkN<|zjBZg|v=I~G3p8z}bf>J`mc;Su-61n#aV%lrg^SeQ1frd`-{O#xP zoDD$`z&V^6SSr+ zBS|7%3U^QrR|n4Rtp1A2AFoi4Q)ACf{vl~i+fVb0z1RV~B7rY}2s4%_fYxWC1R{(e zlM4M)M~RnO!YnUR>Oxo7g&=#{#Ilodsm0=bLSR3M`?6Wg2h)j2VpwWDR*);nTwsSX zch(+6)3c7UlUew7%47*0w`Nd{J_%^Dq+NK$q`S&q?1~Q~rvS%5R$bqFZxr4ZFe?QH zu9EB5AQxdLS>lASKOZQ|Qtm%}roTY_+3H~L^c5A_i>Sx0X>-o-Cor1iC~P_#w3{YQ zX|bJnucBw*pwz8iH+d7~)sZiIn5z!h&~-C*4WD1Qk7r0cvU*Nms&CbGpP+P`jMp2U zqu2KC<}*KvL(8LBRV7eBTA=9R8j&Zgq)crXtjiPc8!BVrZ4KCb_uP z2V=QQ+=m!@@Mm^{Q(xoj-B#;BY zttCbAg02Ry!piN$tfKWl2Y2cX`-<-_v2%Xg@H3iL=Q*(8{|`7#b1+LlFm3-3`gWka zYMIykZc^kGNL~Ia`=T>bmAY<<2VD7*>ie?OIBM)w8EY zAaGBTY><#d;d`+{7zIm~HE6veg7&K64=F-=zyRJZL*MfNgh}ouDMfMIm{$1=-N!JQ zvHF?V&PI`hXn*ajUAu~9li0}y={r4vc%~Qr=lSbQ>+F`q@*oIgV+*nm?Ksl5e@dIU zI?sYJKyeyt!IS0NO4E{fKsr$UdgM0bNSl^EU!|E$M4zO2g!+#_b)(Bx6nlD$Ly9L3 z1$iR1+GbR6$Z31{k@~R;X77P(_H!tdk9isM+4e&CHI5?XpXQC2m%EaX)oBj2@gN|{ zM2C-|4VftDT*QGKhs%A(K%*+Mto8ptzHbA_F&}M*z?4vyVSC^p{GIOBMmPT@q+W`F zaJ|62@EdKybX+s%=%AWAck{c63Yad6EY^=Sbrg)NsMp;Ks#0}XX}zslbpH<0<0jZX za^IsCQ)Ib%CI%8n5(a5s3@u#x5`F&&`oe2ZR0tT;-8Qza`guhCBaa1unv!;kBIxih z$PnrhizoMB#W5Pd`Tt+u!gS>28UkUPA`CRyG)`Qnsc zkDsuYy@`>&KinCi%GTZtb#it~cyUR=KWb<9X$S$?;XfE-7$|2;ZG5I7WMyc0xuAJw zhm(z(dueMAB?^qX*)7+{YpijaM*2M|r)tp6!EbfJ`X(JF$|R?jwc=?0-G2?%#0NTK z-I_eJ%I|X%UI)0%YQ4t_e@`^~KKPP`I0D3ix29)Ws$b#au2t>ZB{xr*nj)3TcU`)p zlCb%GiNE}fmMEYBjrrP&uLNsAu$xp@e}Aua?~cHNf(J~T>tzFb7c2c>Od#%5>md1r zerM*5DD@u^=3fTSI!59NcJb5i>}Ks}C@z=@o8XZ3g#&K;Eo2I4n!fV)V1@;+ZFPT@ z9{PC2z|B@ynL0$W<>9>jh6iM6PZt{g~<R{GMznb1+e55BtupN;^Ixq^?0PL7U3fKM8=^{)pj}fQRXWq3=vz->4&v)JLP4u6VJp3MAd!X* zfzK&oTI&FM>l6Rt@=qWuRARHZ zXbjnhkwLc#Ga;$9y~h2c>5N9&G)ZPP;gQ-6CEUPnEF^7g?@RcMGG}YZtPgvwIPOc2 zT%wm}9NTq;Ori$zGM<;qZHq#h32j7c&gT`enBm23%4_-#q;9Cg>e{I+CdZO_iYU7T z7PK;4gGXpMD9Y!iE2zdK5Z0@*IbXJidu2Umj~3r#GxYqyc$7f=<5 ztZ{s2e!JGAZ(rcs%YOIw7%vrzpG86a)*0otoACR#en1u&KH6f#O0WovxcrNB0Cq$$ zJN~}aMPs-_0NHAtQQ{UeSzfx`+`8I`*{;{`{4@Wz(M42D<0dV$(n}=0)K;(Dp&CYT zYBe4#G`_WbDM~8kZ%Bz|PvEI!cJ<2wGCPjVBk=b=ofbfPR`e^Z+>dAd+?LtNPPyJK zZrpEXq^eXHKIi~@sjz|&P6I6qI(Te1n~u``tau#tOZr zhc9mp{oQY)A5gNhvVaO`vX8^Zbw-B5>H9X`o*Rz^o8IN-^7-Y4I)l)@_15_N@haWX zyP$00{~jl5w=qBPhj8Dlxte7n0BWVXTyWXJM#)ck6ESw`-?wEJQmADRfHyNz+A~@v zu=1Gi2dCDtLcn;vBTsA~%yKm$vTC2NnnGWg9+_w+{Dru<@uvG1^v0K?f!NyL{|W5A z>p?c;4r}{1o34b;pfsn=O2Fp{mz$vc+3+vj`5VN6WZ9-f8}rU@%PS?g)Zb_^986#= zi#vjjDaMt`E^&hb?Z=oxKiFKLIf)*~@j*&2{3;24UaMlXvpf-^Kg1Bd2G>c!5 z98Ba1gqWE`{(xLbn1ibfV{aElvORMblexT7s5&{xstgNZ0&Pq7-g_)~1fhJqxKD?fs)TYE$XN9)Iv*7#BU`)}9EZy`iii*dgYR?q@K?9h{2T<%k zG>tkCOILQU%Uu8oSMTeZ>Raoq8lPB&{P3|4#>NF+mUv=&WYQf_TS^i@OK|=HLd+EETTSVX8THuo2xJ~#ni)L5#qP6x-<-7B6<_c6`K@8h z_h_YE^XYO0qB>5Oy0DtdIm;ixE1|W^(Gp-VvIk7DhneOJl(Ot->&lH&G4;z41`7ud{4yJ=%`)$XAqgg`E#O z4l`jbzHb><(T znwJO?eiFZ|3yR#;zp=y{eg#=-RF%Ecs8rwb&mi4Eq+A8+*muUaiGT;Z4*LLhmV1dM z*F{o6>C$^!X=ctn7{&2=A0aQ6T-wPWQ0JQ`d$T8sp+1KfWpjfxQJ;P5Dt4i8P+?Q55{LgrEaMz9q_Y#)9v>GeI!g}TC(Uf+!L`P8?6*$ z_)u~3UO-|@URdpsN}5;S{Ko?K&(9l6QwCMf=$E=l>Q_u;K3g7sTsF7-=hJMfwd>pK zm47&vW`1&-))dDj2oCar+yzf5)&qP(X~a~}+Ag+B%!U+zPs50gK#^4du4~qSsg8p} zDuJ@WDG9~JSuu;>GG**4mj(4dk3egU9? zMRErw>ENh`LQm`R648vp$pp%&oM&Z`GNP0Z%+!O*rirMZgi{||$3YNFWbEb1sjF0($^k&b)R6782*2}pOFz|*Ut z=cx(yZVnB#AlSueb+oFxptm@2FFhnHH1t-Ls(}}avPKAJ33h>r3Td|iq<@LTR96p0 zqQwOp2YFo9irojlpL_9gHGyU#R>6{Bm$Rk%wI_2e3?1V%F6+o@Y<48mLKOme7Sq)$ zV)_+#K4$jDO$hr-eq9N76QS2`vIh*lEz?S!wO$tv{T%rgUU}(kma%h@t-QOxl3{p| z=k*NSda0`m{T)a1R#;@E7RTnR_DEK+@m!AWgab$*vg7PQAI@i3=G6rgx&OT9y_#lQ zB(pz_M`>lW!dB=?p3X0Cf%W6*QBMy1XU9`nG4l=oBxJ>7@1Bi66# z)u)=fqw7vbOV1_G^GuoQ>{=GQ8%%A$i|Qw8b-+ak`dA6f*K?TfZCcFO?u~T6&d)+U z2;o4hL9hSQHDvAoJqR_Ux89tP8{P_BC9e6-YP(JaTmG{Wjm!XF1VNT=7hD)@GeFFT zL;ZfV-B*NlA%*M1ARS!+ad{X7ZT^Z*Xu4ld@X-z4dI7a#Oebwr10_DW&iU8c?n<7% z+!Qw1JLN;j8Yja=7JM~sLd~gs6?);2Sye`!)_$~^=#YrleifCjJ6ucXKbzkSWvi;v zd&u3x1E;G8OHIb+oh3<;9hrDbbJKXx zHkX~{<=$ggu*A2iaG^d0wG4k2R<@OlKCUNzTu{M3S?P189C(*{cR{5^bXzGRA$rI= zye}wzZZq=+doX^kknwso(fm91?$_NWNn&nIGgQAq!3Wdo zW1H(?;hAph2(ILD9QP!pKGp%P#q1Uk*8?f)BzdY*qT@B(fDE8MVpC4tkZZ;cwA2xK z4(H~}3IY?+JjYk`daykSCwp0C_}Wbm$1EipHaUF84?ll!R6yAW#SsQ5Nr4 zZ1wl6jDP!P7R)d=3vUSaS#0#8`o}+n&fxOZl|%*3_d&yZ!|~U$!)(5cXFFV?qDzES zE4@W`7M3q6MeM<__9G3K-+As?s9zZVFGG0Yr=jFy&)O&3a|JZ16p>8i0-W=t;K-tIbc zt8r>-7C>AXYdAV1<)vBjseato=bSLb5o!0riGK9cKD#6DtI|Z1UNt*`@!JitNcD0b zo%)ql5d76u?V)Qf7)N`8y0X59yiLYhy)<5s^f)x2-^a4FBxZ~^z^jG_ zg-*U=+w&UuCTJ@rn1-1SJI$-EpVrtay3nI?f)(>Tl$l{O&UIl6>T>^ z2&HibL`0@Vs$}JIPL<0P{VrmEY>;nZ(A9f4q)}RI39xRDMOhjYT`*@ZtnCK@Lb(pO zGSr-xTLNpXLhF78GW`7MB#D~lSUKRn*2B#BTYVus8W*vwBZaYf)+@&#@b; zj3W2oC85rs092uk^a3dRzOnG z#2R!AuL&c}b!Ap9!QZDn#NMfDCJF}b6pN46Q&N2uOyDVKtyr42Uwiy&{1rni^NB~0 zV6WS(ODZ8ewk$~T$hng>MLJofe=(|++&G|t5t>F}T2l@IqrI{5>*=hLKG`O`%+@r| z)G7daM?HzJ0|=llA`caK-!&MPVN8m>tW9DiHI^zfuWB*zp1jw5I8f3agB5A9WWA#M zHZd0*lX^!+J3PxXwyP)-rFn(7OoB5TrDi_(x-a$0!#e!pL+-7%yx3oV_K~WVYZu+W zd)!f=6CKAmkW9P%!5e-45pp*_pUM91HJxbXg*(uzvlkT;r@N*ddOO$F%Q|OyLbzX7 z^h$%MQn^Os==|19M8MnK=-An{w~c0J$V-CHt=#jIIu?BUj#>c~;0f%abed)8Ryg+c z#%qnyVz&qFH^X0s9FT5WXEo&4l{!R}k^a>^95^eVOHJb*!Chh^u;33Pkc6+2dW`ibJ%-)0Ft zhPggAp*71Z;YehJfwrw_MwbDhSs}x#yK7{tk-bvY0&foa8mD4Q~JnD5@#w!*^lw{ zE=#>-D&e~X-=Jts-v5Ob_qjquTFz>F`2elq*z)gh z@CSO7#|bNTWsdsq_35);k;JvQ7PEP+y+6Kh5xGaas4n@BF?&dBiIqWcsjUr>WfSX8 z332`)V!w1pBTVWCe?JiE(Gia(Extez4P zwx-KHYzbu$9~Nt-bz{U>a_L+_hV~)5D);C_A?I=E1-i1{ob&$eXLX)trQ4rvn+Y5D z%2d3;sP3~c1tft*TD!)%1WaFMfz}@q+3+K1=QpVSd&gN;88xU;@3E+&5M}KV9}|%^ zo!?8Vo+38%$0*w4DUENneX3PPVqW8&*NZh>%JmrmGd;8RmZuoJi5X&|@4F2zuDS?{ z*cpd?sDMlTOBTP|P#axZNY}JYS!y{n7(Vpkb>gLZSouKO>obBW?}sbXN^rQ1 z#m~`j3oNzBgz|uE%2C61SVwQ9s~iYT#|V#bPcrj1yHx}or4T6d7W>Up9b6EoN$Dc}jEb)cab+TNdma>`QYH!=*8dJ)qu$kVn|E@Lj}IlXBdh zVRT|?6>77?SJ>W@KiyMcnA9Y<7jbxT_saQ!gt8^cnP1;Fz5fYbWqf)$A7$fRjOZwa zv4*+^T3tEqpyQ}Sa%jW%L!AFnbnfv?{eK+SSEUlGQL#T zJ+JZTJH&F~{{46>*8beER@FR?0;$#8WYvK@mhwf-=vnvWE^49F7T8@~c~}QDHL3OQHJzqwDp%j7 zy)VC#8vP*5DYP_7$9|2&&j)8A^17g0JHoF}WfP)YK;eF#H0JzZi#F{-@r8(DlhTeQ zHIEr^`+vsRewvtF(0DO^k#c}it!XlnIX86O(oyiN(=eSB6SjPe4GZl|$t-L}udgnK z?(nEq(fRnis-g?+@U^X{oSI?T+R2egp@}6+>6jHQo&Ncrk_BNNFLk#PKdmw@{c*|&>cTkTh-`% zCyD)^?6Os>@|UAis(e9<{S4$xipmViEU;2AiOr3jvia+jn>p0~b53Lhj={DfJ_G{? zcR;yu;=vENfer2|xH3sk*FLAj_!hQ&@0P)xH z|A4Ou1IyQ?4&iRxaM@?W8)6t8QsRI+@1nFrI;gvQ1}!$cze-PwJCMI@)5k=lItx+n z#l#X^4a!;<*QrFWB+QvVTfeK6>=vyJ8m0^8{Gp*vXSzeuc1(MhYASb!ZK#Ny5%lju z>=0t-10(zs@3Pr+D7u_OwUUxl@85=ViGF-V?Zd5>;7e%ppPc+AJZ(jJ#y%{}f(KokHDjkhTUQOOFnGa6!)uYTwb5R0T+)zqp zt0dvTG*%ss9}Y{}qP^u+B*?6jeIt;y!K|@GewfO>kF*}zr#Uc_HPgd?Av}Fzz&w=)tuI03;gbiaGRf))LodHZS#-Y@{IZSLYqH~p=-B0&TAMiaPmM3`Tq(O0>SJ#KZeD(<1P53^_I z7gynBqIj4+V`&$bN~^*)GaHlb^(B!&72{4*b2FOp^=r0(>0oxLr6Kq3b^3FPYxCFo zmw#QZFN`9rCS9yL7LbXwuZRU3o;05>DrODNo6w#K?ziXz1m3hvVm0ig zlXb6MJx_@U=vlaVeNf@IUdSi0PPY)+`MOfSs8eW^H*FDdKT@{$DUUYsD~fsRc?a@D zXfi(%q#F4vy)}0!O&Ez|AQQc$cs8lVw0Oie_)g$9 zi#{!u9sL}%rrYPt&j8Ap(v9^d`VVJKr1v|&AM?viEqQ-`wWg*%H;S+X_q43*MuC01 zMr!Qlz8NzCE**1gGXfLH#dl5+w*p=iw^98Cj5dq7-wz=JhjKQCB6L>q+wF_SXS6Er z+Iw2=&^}Fd`NAI7=D3V{O)Z)dM|@sJy;*a!;is*K)@f=(bDg=6Wbb_6s1}4V>(jc^#}Ra!`74{Z0f7sb z+D=G!A=dUMLJ~7uqg|gHRtN76aIcHQYFwy1$0+y9?9A0qsmo5^ADNT=^7rrf;fV8! z$H@_v1S-`M`Rs$J3g-_^Jk;w(5gU4lJ5f7?F${aKXe^ZFt|zq|ZY$@+cM5Dep{hbl zZfU|^V^3$2i#zrW!}G|s)bOTCx zJH-cqFvps~RfH;3F(8ncYIyNm(!Id&WOcvF)K$wH$A2ULnPsBBHW@jnw)W^gZz8qo zjn?e$qgUyr8~$r#mLKaY_qfgV9dhsU?sHGsPkwTo%JnX>v)e1rq!c?t5-L)j>A@|= zmEKr>$r}0LbHyc`GgR#K{!_8J^%ZssD{i{fFtNdpF=yar;coQpZ(z|9Xr|f5f5s|+ z@=?5E^0WWTn2YPJUdIMuNS-z;r-KZ^nDc_WDpb!7ui9|?zkZyV(r78vO?5u+X>;{! zV|_ivxW4#X6U7Hv-F6Iydx!f5c~S{e-_*zci<1Qnu;l${Hf!n)57$N9hP&q=$0$i; zEo~K(3QzJBvIX|{#4#Q>{Kxm_e$8}A_Zl%=g!cr0hSvw~&y7g>0UiXCT|03ph<)&5 z9D61*aSFymbwc*o`0Fu8V4js;oXG6#jJns^F4lb8gI}xmDZCgTBW;z@G)|Q}ahTXn zt45G;5&-Hl*Yy$nB--n1B5y;qLH?%sm$hmiETos&uiG$kUh>F!v%g*i+O*q-7hlm7|$#F5M|+i zA;#j2wSxhNPX^@QWB&ysBx};+EUoJZK-zk{Se|>0l!CcXS#oH!DT;7ULBLK5hFR$T z@}t~9doSd0f=e78)aBfciTOc@Q0098lZEwr7Pk#1m%=?6+eW{$8I$3ONWp}$K(B^- z&GRarVmJ0EZ;DVntB|Wu@CjY#IUKQYmVE9@cGP@zUd*IpOkP6urd#FWohQGHY*c?= z>Z$qg@cveZPj9^?Q)DgoMW=`4i!r;zaSSl2C7U~HpUr;#>#iHvuyov7QESitz)0yF zDpk7H*=3id4GB0}ziF~fCCIDa;7YuaKu_%$rQ=Gmmw$zSIQ`RbTK>uv+a9g`SyN^D z`~3Ma7nSa$y-_EtTMDA-g4m0tDc~5|%`_z9h49gg6jSp`&kcj<2hpoa;KMhE3Zrz> z9~7yv(mOSxQ`T5%A&<5PjEji`&f=_7nQr5x`<&U-Vh_#3l$tp*`x|RS?xPHcQCd!g zT7B<764~{jSYdr1#x71uU7}Uu&J)lK92-SO zhM+*}Pn&5!A_za$l>dLjLJ6Tw6S1s?F8|1R1i5W%b;g3!hJHhc7Y2=%i>{KLX-L4K zGk{RsuvGu|Zm!Sw#kzLa!C;}OAabJDc@s7C!0Esa#36RQvJ*Ui&xt~`rj>5=QbHRY zlL~~>GxF1#_AXt|HHY80E@rL_hn`|lDV1oB=eA#}=A4@DLCV_=G)Z}hVzq)|UU0+m zX7A)Z4C2Hr6zAB?;NTXX6YS;)(DE_?a7fj)%?+Isa^Qo1Ro$xetBDmJh&f*5$ zlFlnlr)ba%aR)inI*JMAWK-y;@3g~4V2qJ8>{2;M?kW7<$|0Y7g+j0-_&!li%T}~AKOLy}j1KESeDh5b8or%Cdx~{?xMCSOcS_5l zNiX+VZa|%W(X10X&SiMI@c`=HzZlL#=}hc;`6?!baIb8sthztAX*2~Eh2$S$K_gxS0YB5en2 z3}xLwG0~~I`J{j5#9VE3@I=JkcT?v+`1=KHOd$?4et#tyc6*=5B+PS%lCyM1uRi!% zW*P47^x}w16nL87`dlE;GF~{Z}tbF%7FA3x zCXi$C4z5qmbHkwOyj30|zNXp4B+E1A^0!gF2z6I$4d@8Qq};Cx8TFv-#@=`B)Pn2G zkmR}HFR$DSEGwO^V;uKo={P67Q~8ypXRVQHTGsI8>F;4pDKl)(2-8O1`uctKuSQ*? zH)?doV2)5Zmrvz7w|$V1-g9#9@58`YVI9ktWlD!Jw!8`PPPffF01b3NaW6XuH?+V6<>qAWs3Nj(@x7mI2w_ihA9-R2jLHpU$u(_Pt z!MVNHtA{39b7B!mAngTFtG&SD)$!i|3!7oHI4SX#IgJydlr{306rJ{@Wk0%QllfFZ zrKFVU2J?k>rQ)2W?-PHyz>k?l)=oil7u7>I?M5_*76XmPs;?185jTiFh;&4?D+#1a z)Lj+5{ZHb~(q8NjkiN}SwbJaEQs5bOl6MM`dEF{j{dwvOq=di-gw)uWn;)O=S{YuZ z=W9gPn;H82!|A%WuxAKIuSC^AJ5kBR>i7=C7)QMGF%Qb(=WLo{?(@>ykn(6s2Lg(L zzWwz(z)NwoCiZ0iGTWXilnD+gfj9boXer;0m-$;+`{Lg(R;w$OsW7sg=3qdx^{Onqjd+e~~7?qqb?7?C-{g!uMbV3OhIKiqWq ziQC8L%>5${J}Wl3bpETMjFywJ&RLf*zsU=2J)KM}1ltMaUN%g^T7r8l8tuye0$Qd) z%?7;SW9R}WT@q7libYUmYl}{Rl zo6^wntegdXhRi9F5l44h#q+xN_S$hUlXo*iu>^PSVtM84czi-cjG>Hu=?nyStK1WX z0}q&F#AbkM7ks~4Q}AcBAtSO2A}uuK=5(S!OMp}aEdk7Xfq8Oe5pw9D-cBv0H1;H4E_IP{G|G!OrsIuFFdCP8b&qlU zMe>z+tz9NQJ8b)F+#K`Yx&8G2%rnu}(itu?>#+>6D}W>Wkx#&lgiKFd0F%|)e43>_zczEJ3(V@XNmzAof$s5|E!@LfdX7Q8^Ar3hkzkx00mh*YSNArDT_QFN z+%2^ARakeKePENLM^q7-|6e4gmqqu3&YrEG06$(P!xzMuVbsC(5RAFU?G)hCn&f!T z^@G8qVYwznMkn~Db-PF5sx6eyh;R494R-)onSBoX{BpXy4pG_2XRmS;_`Ez)xUcB`9aQv2*%ujm3#dB8x;^pCk170L&H*d>` zasMMzAW|}A-1c~cb{5j zBpkgQ`puVw9QOtpOOQ^;*n}ex!XhM~zTp>kzQ=^tg5DmisG1m$Qw;p9zwP5tgu@xq zuZo(AW6T|Y2Yp6-s=g@XW;Yj{mCmkxva}46u_nL^PZiaGwE}Q-d%@;qp%Vb%2Dd`D z2(JY4nBcOO=R!$|7?jlfvq;{F@Cv7;%iN$hAKe{pRsKRZtY6IOkS;>|x;Be@22Cc) zCrXC8-voF^3i@~`@vgc}C5$>y%7rjo%D_8IPFfPf(5$YOo$#aFoG!y>R51j&f{3e7 z@N{(tYRZu;)msct4Tme__<8o5!%=H_o@WO~mGykj3H0h}bNGWd#>m_HYJTs%G5-79 z$=+>U&7FQG&7E@AGi9xu2N4F^{v2UUDebt86SM!eWhZTbA{sv-($}CS&lBH zJG@M8ESM>tvaog-%F!$_)XJ(Nhdv0SKC#^W;^e8DqOgn5NM(`~NJ)s9_El0h@85RH z_prQ_GU-5C?1NKjXil6%_x5>Na~AYQSeg_Ag?;5z?PNDProS*j_0%Ay|7^Lt7TH3j zoNsE-caMo$3=`2WNIgfe`YB49^R*wMy?>;q zl?qlRr-&*%e~xtKk*S&!6*QreR#2nq(nh4{T!(eGrm0`^QF?H!=Wuz2nLmGM0h88e zaV2Z^MYJyh+^K6RF~f0jt(y3or&d>$**x;OOO9`TNT?NJU;igzWtDze=$p|E3HueH zUe#|I9c}_d33_8Tig68wLa~Uf4t&iTZrYnke#3;l2pJs$eLEEZ)Js*yD`S?ckq zcuafVqiSQl3L>IfOD61Q*J z8L~i%t@*#to72)_XV4c*)Aj+PF|>0C_6j0Ly^6fQ35zF~FM||uFJ$gx`uTeHXngge?H*nyyW#yN5p^!BZ zmzED2Z2Kibr7Xlm1~?VIzdmz(QS2VxQU(WemNRlTIKgwI+az_VscYu+Ed$<9qPBVy z|0La0Cv9C@!e60MmrM5{xvXRLlW}orQjV6+q0tgHe@Ny6EHpoF4>IqE=RfSQZBuSl zTS-}0`~Qr=uRhHq%^cm@&lOZ3&hoP68^eGRv<=B7)>z6!QE)Si5AwZ?y$Ut?!)mi;@qI%RcbW z9Xcf<;2RYy@i{c#(D(ehhXpSuCTUA+qwG`IGpWlbex~H#SxIjusVB}{y*a(&gfT$y z%z1CSP4Tj87C%huM2%4L>c=&;s5=YYyB;sv(%uMH7`yg_O{ zXEq7y;_r(|nuP_8-m|N-i@(kr}blt0!fi?1tE&Zpc)9hzRp_5qcuxLTrmUJ685GbIhco(*eNxr~E zaWe>Ich|>US|ft5?Y1Un=XnNUO_*0Jl78m7c#)Nx1xT6Ln%T*U(WD=9II(33f9*|y9qO*LOht~uv%T9M|~ z^k3je5J$lIC1lu-5ImNM9sv}`y6@new9*_R!564{+&Udj);!=XaWzOu{a5SOb+T6f zMQ|#YUFY0qXO~YeYYTcL__9@^`M9l;_P`dq2R538@scBrFR!jD^`D4K+tH=1t6THl zpYu1fwzd%!Q#-NCan__}+}^dOu`vLMdNtNiC_1$qtkhOxBak52Gq|pI%764?S&l{Dj?m&>=H3^3;E@Le{q3Ztg*T z)SVACLsuGN{E8-%3?7u()mS(d*hOeM<uZak7n=8zQs2)C9 zcqWuO9-j!3NhO`@^o!>LqMgKpYYx8GlVqHxNZ0Na53er3mnmyfk(VVjHt1?{z$(An z;={_0AHR35svTtqZJDK(dgVt`@rgf=E0sRy1MVlUUo4JyJU0A3Uu#EO(E9>&TWfxk zx2~sVBjmOB@m?_G$G?r4ht5&%v8-6pvCMuMOd86by98{ufyOZC`~uzZ8L@`Y zwge%h<+Rai@hRA2pd{-%fpOl}UpWraFRe5@A>9rP=|6+-(>rR@ZyEh3;p1T+SXN*e z_%e#Ju*)I}K`$g$O*WI}5mr^g(_nf2B=)-3EUjt=u2bU^5P0tFAk5+c3|o2*%FVPJ z4|-fWAEPj^J1p_|n#^vK@hoP6^hF++Tf#{M*3&{gpmZ;DI2Uw0? z(o-*m%*#nB&S{C(O(8mmYizG2eJoggNjzQXla^z2J-4i{&LXV(Pc+!t7uGdP9djM! z1scB}hkF3Zn@2InMTOV{VCT87MWZe$J`v}SNE{Tp0Ut2uDmy;?f+$?r{w^q)QGU^w zBl*GaXl^kp_Bk`q?&jj_#-01`!QUoWew?IA5fEusr&gX=(C>^q`gn#V5h}ZwpFd4k zQwz&q>siMt0yb;2oSEnlPG9s@oq-Ogs}ELhS?Bv$d_3b0TT^}YhofWX^l)yiG17eQ zXN7-Kg@0Aqk$2mUrS>fkJ@xB4vgUsxDJgpF!r`pvA6wbPU;+X4XJ9ug!%<>4_`E3v zU zu>LLT=rZnyRdv;rRplNQEH?Uan`ZGrFV6X)=#ufd>~w0NJP0j5w0-gVG%G*fI|Ow@ zBVHrE@VDsAroI7O)q@RPHbt*yl97P@n)mXWd8?)r?B)teRjp9s#oKXFQ9_rK-lpsYKQ4A+zgWgSmjc0j#n><=I9-zB7x=i>^Ucp7Cazm*sX`T6Ed($R`1>(r7nv+;GOih=E%m2@UDL?TG?+# z8dfs#XDwLrl!?@V%G1hMeyD%hNzdoa&ONaFC#v8wW|oZbd+I9@mzb$6inTCb^%k=1 z-J**dJXwQcd=`0}XU3PSsT`hf)@*6;ixI5D_XVQ`&KrKQ@O!SF!JP1%^9|dr+W1Sf4evCOW#(xKZ-VBq03Rl- znwWZZ^I?CCcS}qRtME7eisM$_EF~+R5CmZrf*0Z5`|4$Cr68as@4hZAm=ioGeC};^ zDs0$S$h}9R2zX*BGxh<P9AtCBaDsgn~&$%Ug_M2rG}1$%LqQ{PHY!NBI}Zb020f z9emGMHMxBIP7SQ?Y)bR+HV&#+`*ifel5Uvim}iA&o`3JdhJXg@6ysjE+xAPxe%2M+GB(^j2OcNT(wzetbsiYxh=gLrO;ADKs$F99%$*z zWv49MZXQ`}hMD9%zRfMB{#vs#BLL2H{4NXvSYm;&ea$lzm_7pnn$4k7#t2y;>_V-F zgW-iCJyk6%^+>?XK!EUR*I9QBc1UU-hc^ip7d){k0tZ4wPh#Ip|XqxJ~DRp(tF8Lv$gJc zy=y~})Vr0L{(}wnBjwGJv3aNqYWk7ZCf+|N&xQ|A_F+gIW7HEv6R76!_ zML9aJyEfZvhbZSWg`ZeADpmttE>l}`>b#)5?eBs&tpip(K_2uMYLivD=S*oIHPHIY z%7x8j`Vyd<)7DGg>5r2WYhw-qV_a8;!q-ih@U*C0ycKhvR{_qeB_Qt69^rTYOm4f^baxlSf+^hsNq_W$+ zlm=kIAB1+IJU9YlCVCIkX3`!NN)I4>to%_=bE#d3#0K)hnNJ)`#nwE61W?pY+_y$} zYBYmAG78h?NjmcGmNOv?}Zk_OE4>L9Y2yscns6wtz6=jKB|^6;9MMp z9|FoRfQKbEB=?B4ccq?gvmu_OGNwCNH5|ePCez0MS?bWx;%U!Qio~RK8uvP=)Z^OR zQC(9SJY=V*2mevR4D}^`3uZ6)N1VX6U*P-HJ2rHx_d-3-Cds+0PHtxC##}AGYK}YZ zVe?5u1pQ*$tm8d_K5jr~Zecoj@I1CcM?Es80Xik=o9<``vm}GAZr>@ufg;E4^nZl= zcm^N@;3FXI4#aO2*u<6cKt=uQB1V(>45ey)p^dVncVw^__ZcijkrO%X|4Gy!l7zlo z0gGYN8T^>Gr%98Jd=3t&P}}hPOoE?-Fdq{dSYIdv37t#O+9XqzoOYZB=KMf-%&$f_8eH`jI|A{9I8g||MF*0Xsw#~D%3ixk-i{FD8i`0b>0vf{KO+nm7C4=#zA6j&=6xw#-Uf+-R z3c*y=6fnaF+F^#OTC6Gp>X^Zgnv9BomK8=9sU4y=r(#f0iFWSDHCg27doAmPLkG!Y zMf^_D2aFGo-Oa-Uiax;q0pzX%KFt3l#x;V+UHLnKEH3zZROiwoyaZg028_X#Frk1l zPr82?EsUFxF$oYebdA6Ib0if%wy3BT||{JA&hI23NtB1n=#E2j0y2Z z*U_0S13@i1=oprD4R^&E?BEt2_I8UA;(rGMBi>Y(@^njy;gi)!-xw?EoyAwqzE&z@ zqr0+>8Q-!CFN)MTTaXu_{&~7sQCndnFL{Xbrl#%>vKs{)A}rw-;^G*%1Fk7$)uT$qjJ5 z@of-urlbHa-8iAc8o3RYJRMN(Rk92)wL&kAYm(kH5k~NQyOxq^Dp)I_)NqS}pUP63 zaxvfrlKrzVx*vt!!FDMVZ*|I=h>y#cZSg19M41PE3ej6we zQ_c~#h>CIaPh!(ig!lh5LYKf&lp6qUBQUpuSnzk@;&uNNHR^VG@ z;^UgORRlCBWR-HJP7DiW%7K>?owbFUfF}c*>Ltg0Ucu9CrLJpe4^-UxKDeeUt$^2U z2yDJHSqrFCQSMYnHjIm8KYAOvHH6POT^)L0+jI7WpMlFX;opfH>(#-B^O7kpAaZ-> z#AdiK5O~9YfCoV8n1D@Vv(6YYpp9CfY#IV{9gJdPQj;M=9|H1G&k<;IxZSJ7U9&5I z`&?{f>(sV^;6P)PvheJPK{!ngh;yP;)_iQzj5R1(winznpRYSUH$;6#airfi$b{% zq5k0Li)&*Vt!*6AWra8kocIiy9pj&Rw@_g=EcJgp%`PW#97n>VaE>@1!*N|^VKXm> zZW_-!)oq5=9YEZN0@_k-gmF^4LS$`jL_u8~Twe&Q1Hk#$aY;w+Z%d!lci*_Q^lCD= zfOZJ|WaD*zPt3F&9uoS%v-!0uKixb!svPZu1P>j1j?l)va%yKYoOjIX*!Dv*w2zLJ z-d#f1U+^05jVXROWHU;G27NYO2;j#y4hQR1Z<@nRt23uiljppB{~@$PlVRkrki(Zd zKNP(C5aD#)AhZ>FWk486pL}@6=Pk%2YWOo==}xugDWre;{c&?5S6aF^@ia+Fx2<*N z0-Cy@`ws2gT}Rx~WW<8(24cEeREM#~RdX6po@r_+I{ti90P-nOK3Ly<#dcIbXx<=q zXl`n1ZkQZ=u&(jB>g%)i`x+?V9!8DXcRiE|h}pCfYEiTg)YMinAX{g!+jw3%p*!yp zs-cR&N%C19Jg#iwqKk*;IlGa-ptl+4NJp%dDdELOK3Q~W%cF~RbxazXBEWE5>IxkB z&qt6!S?PxR_pY*;dc<`#-4RdS`%AHey9E8BGt09TQ=lHtnN>o&q7!iUry)^sI!Rs{Nd)KphpMv!TG_J280n6 zmJUpWqR^a&j|10+5Z=P`9QNBEDK;1iukh!z=ZZgn>5gcukO&CvHZa-Q!-pjI)-mcj zt?wVz*upElPsg>N__S;>b3!;fS~AaaSQYSlUp3E?plKK3lMwmYA|WrG-FGRgFb}ngT!R}LCSTtT3I23z zp?2!0B8oc-SrH@H%?CM!9kBeE*pTis|4AH4i@`Q;h|Fi#rgxoK#F428_RI6z^vdcywt1&u2vm&N_!TnF(Qz^+t$r1Tzkx&jJ0XS$q5t|-Q|1?& z!~c^wDCHP?3bF$1_T^l((Nv2-++Y!g#c{NFZ)FX(kY}?sj3UJ&QK{6@=t%!57x&`9 za@60P=|&kxyFM?M*?`GY+JWj5j=e3D-QKYL2QfHB&`Xz*<`@QSkXuhnbt1+czF7b@ zATHk;E+ahB4JkpE!haF$oX$w6+~J0hwlv0METnASZKBanOEMJ~jy(8a*j1ouwQSQy zdkR=3l9i-j`P>W6FQcvGqE~BvyCl&jCn$q=Cd8vZmWWhcMAiPWi!{b%Hb?YOpkgdxPZ z6yt10FGlT^>}wVYu4$pZvpdQ{Xz43>IHGuzQ=OS*p(Irj(vQ|oC-a1RL`0kqZjX3` zR+Ih|rVV}~oabJhk`onVc2X4ue>2Jg_Dpqz@Q`kU;-1|qo_$RNKVNzt{(IL@`TU85 z<*v;2^rGyDSKOtbf8C`CL z2&D^#xf}oLx?uaknQ31OGyVPVUk2(<9-%~s6Z6EeDZK}J*s!csGL{x!>Yw^nPqytl zl>Z2}s-#XdIMJhQBG?E3WvK^1irpfxPDj4w8Yb^hwm6_L^5UiCD(BbThNEDKlja56 zVZ`IKc1VPCwG%fuKcw8r+R9t82O_}yVSn^GV5<1?7YLKxLKWoB8M8mPDf3A;O1iDUwFwAH_DhbU_*5GZjamY)M#r(#1eLHeBbwU?daIhV~f=3@}Rt!G(k z+)NWZZj7mjx$%*=9Z6LMU5i22#(N`vWG#$EMd8y>_g1xGboPM=bsmMdI$uwFX5OX- zR(AN6S=84K@eUfRi!4Opw?dG>TMUw7(J$LfO?rWAQ^TNll1YSZ8sEY5FUijDtTX{1 ztvw@a)UAsE(&77qzQ4~6xCZJ2Z<+-t@lDt8+!g}(tD|~(DE^Fp_VApkSYv?F+e~a4 z68f3`RJDqpj^NCMN_h$I#`(Wx)TZErjA)OC#YO3adGMg*i3Px20Vj;vZC1p;XLzPa z^>qG{l(LfMI}SMM#YSp`rKiJpnRnxuPk6^?`*G8!^1Zp!g2y&O8}hD^m(16J;A*>j zcYTKPpF{}7(~Fppv-i%X=%JOui}DyR#P6Bfm9N0BuX?c-+K+!Aa6u8ux(5n=|I4pO}PQQ-8t|_Av})ce-etUHY@CH zj1=>MHS#pcO@9$>e%QB!h2Uvmj^Y&MowMeN(;j(|?uLAYm#%iBoRY`lFVp?Bg;&#= z>&;{3y1H)W?t&%i-ArHBoy?86fc+UUMIZ0LYC}ICx7B>)`Xm${bpGha?LSyaT3hjx z)_bFrU`NR?b|soDsmZ?zu2B^s`XY%$lni-+V?O0sBQ0R+s3$uXAz_(a7x7%{C1|+> z96Z@(!Eo(T%sA2&62DbDKZ0+rbR?I@Za3a2>Rhd_zd!zS#Z^~ZT~3hn&^^j3NID@j z==FGXaq#4DgKKK$xa5OaO%(7fE9XJ4@fn`YC|{ke~`cT+o-L61v`gG9w|6oW2a17d~4y6+ZNg> zoY)xl0T%5=F>C!hatdJ zGx1SRl+Do6YRsLw(WYy>BO`jb-Ey}L+Ewf#`D00t=kdscSRLkgc?LZjzU>{fMcb@t=nS@lk zv;vD7dGlG7;h>G;{nbI^0S|{9ptTQWl_i$aWF5Dk*8HQX_QHabMG6WtxhchyHPwh- zBh@}Y>pwD|_O%%PlJGeo3mWb#SvGD&Z3%mcyfP4~Yk2Lb*&WGsWXo^iq~(jS*3|+uoX7y5Ri>Zi3oCA)CzDVOb+^|Dzad};r0jmhz>$Ygg zdU?{;ybmMT@$&_`RiofEjFrzC4wA@UC%1@yg>@5aauy6EP#Xrs{q;%`c;J#rTt5A# zY^b?8TUm*a-_@~4$y+il{!bf;O|f!3qb{pBTSg4c8#@-_`p}~r5$~`z*MO&+sXD*< z-jTQ(kOS`nKT8%vEAai}4`cg&{keK}%eL>`*JZ=Hqc=C#SxKVrqV$%NGcCHJETQ}+ z97Kpzr$0m7A&jSY;PytO%LRNxlP?av2PwJR;Kd(lX3tkS1SQ(+c*=v<_k+RZLGDVRJrlK)PW z2W37hZJ)GvZU$n&5&ae&@asgNp-W?o=v;e?(f4m2ey}qiJa7EuS#kXBL$ed_?d#pN z9`L7JxaWA|lg`ing-Cag-c@Q1xt8mJXnjz+kHvF)9+Kl@%p1Fak?y>E<=>h15674f zB91w|do#@|PyZR>xWCxxxj{B`ucK33JN3g1HP4I3gpQ>l5^;NR_X*=>{AJJ^+upVb zcN3b|@o*ieu{oOEpehrY(&V4~VP;Be$Rse7eU0MO+bQ;X^J!}Cc;Ef;g14z zlJTb1c88mlSs@>B0i;`DTWzasHm|jPkMGnh8Ya>|@G$4VfKhR93SzsS(3Pvnf5a_) z0#hy*9vf&vMZF7{k{=Q)_r`|MJzdZ9-v8PeR5Nj^*_scAxn&2a?pPx97xKT2x%*ui z_gsc6i@u9B0A1#KQUr1b_TMQvP&1gHWd%P8;L3I5wpKSs%$tBA-8PEBhRG&G^g~NZrltLGym@4_(V&#` zt3`|Jx;L$Lr0n&bsKS3LquyNom*>`fE#E$Y7nQ%FH(nxX(n)P&d;#q8t@I8r7pHU_ zjfu{#WIQT$3C)-hZxKBv`a)8?Ohqr60=XTXD0w}s1eT(45XhmY;yR{u8o4x~9$o^V zFlf(&t`#O(bqV*4Cl;igC?XLmEz`)JhZ<>JUxYF)a zSH>M;wM}RGGgOsLyq+;iZARrt9wQG0Dxpd(1~lORV{FYi9riXZlvTs6CiwX7@(}np zS2ueVFczQ7}_f1EBIzb-+2U>1I1#5^-0?gXE<(D3tt!7C88JYnbv} zJM|%*xu)d=bbPm>iswl4hg#pxoT@Q*l6P5H2u^jWt_$#+5sY z+ttBJHEN?Nwy2E(D;Kz>ot#)Wjj8z9#C*|()Cu3sv!y8Ls!mL5Fg>(4eVX=`DV7ZP z#PE>|y0l@$UJMe*q?5;wuzyaTL3ABEYL-77Sv^i8*zWRI;XeU4Do*4V>4 z*%kb*ld2G@HBL4BIKi~wx+B{0>JDy6$!1KJ1bw$Nu`6b#HgZ}9= zSa&h;*=2TPD7h(f#DB=O3KW-Q#^V+3k^(?Mf8rJ*Z`u*Iiy!>h7Q`kJ|r!2WKx~S7*D;Z&>Mn{2l{M9tQJ*&hhW^P_4x6*kMp%S4_m3 z0<3PDD#DMZj4>E7#47nuDSx3f*?*n`8}6ID;(y&oGj750#XIg3YB=vo-x#j#Tny9; z437!$)6n&p0_%z=SfLc4&s>@?>V~Q`xq{w$s3h(#C~&REH(9(zA7h>U0>rUmgPGZx z3{k-OFT)Yd);p?`FA5O>OowCL0(7A9$jFCm&+F=@(&tJ%g=7|FoYNYOZ-atrzhsjP z7w5taekN8H4^crKu-ZHrc5mv?a?`1Zn9rW>fHJ|LdS*>Gx$$De#R_Bsp|aAQKECtA zw=j#H>ve6oal~~|E&QZ-%w3+)N|r${F^HseX^bh)x`M0PiBhf*s|(M!@T59Y4bO2$ zb3S2AcnO2Vst6eu?ZHFCYkBrr15WKz>N1q;nLoc*jhtY2QUdFov%ZhH_0T3qPTd=+ z@C$dXa);W!y2>S=>iXm6pr>K~nmw4d<<{4$%m0%0RH;o7cC%0Z8xmENt;}3e)7KMZ zySf)!zB+cT;E&V(V_{UMLBM45fKY?W?j}5n#?Pu=?sEBv%2c;LLZ0 z5otCcFoqF7)MOTEQWtMxrO`cp%Ja-kYQ_8lK_%AS?n^Tz!rmtFL*5UpytoVX^%ZV6 z?lw+Ac$ zPw%W-ccM9z+4TmLl7kUL;85j)C*5%D|H^Qyu9YN%8XvR---rFtzy>!em}_d2?mewVtMhO3P|f7#!V~w+$8Q+Pxy{Liieu${ri75H%Am=O`sU(w_Vg*x zM@LPbH2vpcXZGbJ((i*Rhg?rdn963~;l6GDB?D77Q9yL+&kr#CS?emtN3J-$nIBE6 znD2x;3yy(n_zs{o0nQZ&W-oHy{|sGagjyoRs=dfyZ_=E}?>5mPiBN9%(31bE4E z*l7Yt$n?#?<<{#y?}21GEk&;D`>j(EaUMSmJT;vt!T0JCCY9qGo-Ho%$a@!u-)`$@z|Nr|2zNGc#y*L)qjlJv;q|0nNacrey4wU(e6q#+x}rW z)W*3`;d=6!+J)HrkGUI1t{C5y{1#;z#f~GxON>ZnBlnT3`Kb;tXm2EmMba1!JmQTh zu+pQcpuOm}mFjOK%^p3n_5e%1W(fJa>%E6-_B5{l_|{@wP98M>gDtHtR+lx&{vY0x zJP#A?GEM_5(SF}cAY8b>qF21s;=l+QRd5EKZG!$^E_#;)Iu2u!-YSE7B6@2<7zC%W zzEI9H)Q`%(32>b!Hub4O8T&_~6qfXo!={*}xVb`yX#-ZZrg`E(s%iLE$Phkse{SXV zKz0K?tfC+bSHfKQPY%OQR|28cfB9+G=JMOSXhz1w9RE$79KWGQuhmzran5|Tmpw{& z10L_Ej}ip>=+1MJ5KhtC{bGN`KCE^LxwJWBfu*P-T$bOUp0BdTZ15tym29{=O~}#( zPVZ;&k^!rmPtXk8-jHD|fNQ;4@>8ZipErp9h)=52RrsoE*fA^A4e&vvL=vpWX$xTN zJpXQ8Vof*_V2DbG$Je?CHvw%K#NUULL)#@&&25%Ucn3)iuVR39m2eiWddgeqn>m}ETQo|xE(GyP`hv&45xeeGmbY34ln`_xQy>Uyp#-Fcl>l~*+4HppJzsTt4`RFyGScQBwN03H)a ze_!LEbaLO7gJX&}`p?B5izq+jl^C-3wTEQt>Xh^#a4# z?a0Rr59*6YV-o6`j0M4^SN&cATKdjkb2np8S?TVvN>MT{zL7h9Mhh=bZQt_T{w|1J70C7T?!aGBvDG>t_Cn{s$>EqlPfaIn zT7v}!QBHB4j4FxYhx7&I=ot#`Xy8g^Si<)ryHCU88_Q?fhkP9y@lSr*tp+9XAN#uz zI^g4%7>V7ucmgMH5vz*Qc>q_fg~@}QH7CFlDbKwq+NDHv%Dn2Xy;?eNp*B)cD}>oo z@FTJ}4}9MYA;XYRk%l@h~$sDOa@_L4ux?O<4S5Ymm{tRR9`l`K2YL5d25gw+6PMXCJ_Sc-LG*PmYl%ybk8$Y zU!F76i@Yn!d#xbLFv< zKvp6&9;FR>n79@JFrFV*CZW|H1d7}rlzHK$JAyZOFsaq_g=>|mkvaF+&%|8xyxzs) zzz=3e6#VHs%FG?$(%lN3cu-JQ3;t;YBa^rkVA=>Bat+H4n-9<)>IWrB6RR z`77rBXpM7I@>yw0N7+ru&e}3+;Xz@MrkTB)!I`&6w+~*4u#wO*ROhwu1)J%zkuqVO z=B|$E`Oh8Q-=Hm9(KxN#9@%6fYn+FEA zJlq2G-2BTM=SxeigNy8sXX#Vnp@Q?k%c7g;PAK13@Cfkdlsiyw3$v@%;Bum2loGI7 z65yr4JvG26J^Eds$)G)h((fHrEF8>*}-_R&#GbJLau{=`$ez-hsj=nKkxuzX>>H3Jynw{sxt(gAUqjbm7)2Hj)0sWuHQhWBa*-i(H zF`wRReBC>feArS;LPp$N@AW@%m{0fC`k1LrWZs}1>`c)cpVtFg{c`V!x*Z_k+j-tG z%frLZ@n&vn)<0Pwt57?}ieX!48xmL+n(vs&k5(H!^RcWS^U2@RB_k>6&G4OnH}C%L z*VO!XX}JzFlP`{W&Rhr)Mf2G0?=d0ykq`zOgD$=@wZmH_8YB{lunA{VPhz`lu4gv9 zwz{w;et3rYfyDIs7>Vc8tZJ8A8U})Y4n6*HEMlg%ZZZ$<@X~-Pc=`R0m>DYzOt4~n zx!dtHFfk5fij>QF!6dZe0>9~78t@V6ECV`wznGJB(BmGs6Z+jZJzv*c+5y@|O-1yH zxzgwydvG+PNZ}i<>`4Rc4elg!(9^HD*Gbb7k}BBC2RW0+0~l=Ov4ut_^R^MftmljQ z;#`sPL~+!uLU8o@FRG00jEfB;d__fwz|q`t-sRa7+_c6vX;WRxbz>%^YxlvX#}faF zxz1>)%*e%q_c*#CE^d4<4*Yak@{=>4D=E;(c_BX0xX=>H-O4i^cpZn% znW;I;hq0S@9f5g@eEPFnW6q%&|2B3U#h(|xMNj2}{j24J;IRW}f<>tA_y%vJ0qEvK zk^4C4c6usWg5V3^i9Eoe6Wvd=+Ie&MMEOZExGI8TKT@S@<|_I8;oj^V^X}PqAyHLh zr%U&?Zfy@3FZ4iLVzf9p@q&O454_~L4GBmnh)t3TINL3OW8e<@<@&y8g_c{Ya3@I8 zB_9`f4b47+Yrkk6;VP~XH5!rox=oG_>^J#I(YM#L`a)T&RO%$z4Aqt^dm7pft+g2G z3-#^tFIr7hsZfX_xT#0R{ z^WwEatLq=MUWcD_ez4zb^vwrNMRx~xY#)1x&5r_u{MQ5!ak6vrJL(*0`Has3%LyeB zm0tTCEhCcqb#()!2fT}|^v4);Fz<@z)YKZDMd5UU1vXLAX=M+bPMNr5spQBjRgSc^ z`j7`rte2X&l#X1qK)H|)0#s#NL8~96FZG@fC%6;b$<-)`<)F8cPy(AevtpeR{0ruNA4Q%Sh z%p&DGaob4Dli{?Nxl!xy%&y@fqF=Fhqb5pnoWSW^gN>;^**N`O-j6!;4#b`=3w2Jz zD}nVCS^|jcQIm+77syQygm!M72YpW^21-93FVu}USmZ0GxDqoSoJc3d9#4t9xA7t= zM@Q?k_;241AkVe{>0^AN@8YvUhaycSxqs*GA8cAE%U`MdY?ZI72^(waJ3o=rCVldQ zgKgNEI(4nruW(wF5|`7p=f>au<4|B|W9jHMdc#+dZ|yV{=*Egi^@IHL(*ju_Q1cmH z4>e?QR#+}X}J=SAApL>T9jiEHA@=_NO!b#~_pZ_k)!#=yfyJIkFYupIB#2dchI(*TGzmH2{R1R%H!!ObIe8Ai z%aW2;X7^7E>`MKk@$WttcwhU0yYPcSuvBfUIA_p0_xp`5ueG7%aG;{pl_L%avKe#7 z{&OK|JlG_}G=TmUx|B9u8Q{V-1lz+t@UfOI)G_M%o;Em~E78U=WF+7!7o+Px=y^U+ zDy}YnpmwMctk0DfU-~q*;@cV4z+5TQu_bsY|(_KlFZ;ZsSrpH^EZ(NR69sN{X zWbfo!R<2dqz3T9%Y-Y$!e|_~&>tw2QREpcT!t6V{PTI-9_S#$aRA^ba?f>BT-08Ya zk*!(7kK^eA90+u1g>%Y#ptW=`D>;ZFPig@1Ty-Lp(}&tKjGij=G6LjF{Tsf6W)Lb| z`EJ~EDqf~-c)k2s#yG#rLZhb5OW{q*BIhmyS@ z)OhOqFrrPr8a^~yzzJr^BqxT@q1Hh0XJrDMGJ*!@E_?^+7ge0?LjOD`s z+7c9ft6Zvmad%W;_;F5SF4`il?ATyVvA-n${I?%V_&s%h2M zkT^DRgd387>|HZX*7l~y)#{&1$83i^G)fsG2lam+(|Y;s&G4JD?wCnxu76NYW!&^t zsS`cPqq@>x-m2U%0R_!jFADsxWyCYBew3?O|Kpg`q!dxS8Rom{&#{6^_i+M5y9R>! zZX&j5_b|OKc)3OY()5o43k8E@hZ@u%bJ{N865miHOX3;|j>lbw?T0Mgs`YR6S6#sF zJ^#r%S2XkkY2GA!OPH#RmRwpI73Zn7g3tb?s5b(-T_(;)lkvt&7f%t~0n^-%kOaq{ z)jx79_ggX&>qVWJe1k9p&YN6+PlF0YLep|EgG8hsL}-f+;*#*HAnby$H$Fc5jfENz z*CA3H5lH6-z5SdUgu%!S>+{oONZ!Stu8~T{i!onrox1+&-b>emw$NwTop-@D1*cOc z4m=5`fxwTRG&q=iGl8h7bypkPQ3sk*tifl~1 z{O(nO7U%TIj_X^?in`uPkIUxmM;{{;Zu?v&kNd~F@O~7pj5t(Iy!$b|Wm}G0ckAN^ zq3@PozGk<`R#p1;Pr;}T{HIBSt@CE3@*LU#&2a)(SPV~2_zvo?3uxF)3S@;C3pv3K zu)Jsg=W`;Dt0Qj*oM0TQBG9ZtxxoU@FF!cJU#_3FWoEYl8Kvoe!!Hsuh5clWbG zrPMqx)>JJ{xg2vYbtx}ZDr<{!NuK`H#B`#E5>ea<$PcB(4Ql7ao##?63*N@IQZWPT#(JN z;=Bk~Dx2b&Yxc9R&9prDH>4%3VeU5KlP}Rioebrw55G|8?WoBIJrzP4#6NF^ZnZJu zy9=;l=#Oss_wJ#lPjRter*Nwpo@JpahVX?OgD-Wz`TsDxK~*`^)G^j~&bAKo@^x?G z|C9qRJq1yS+3%Adp&w(Kcs#O~9y}RQqs@2>MtoREior+VOza^#YmB{xPRO~jiXyah zc+S&hBZJ|X0o6~}UmI)O-Y)62o2ULOj#?j*^fumDRYo$+DL=Ev6oLv}zjB3!dAo#` zqp3d9OHWoa`6oyHdrSnAp6vUXd?EG;j<|$F;akitTz{bQu$7UE!f-Zqi{2$VC1wEK zv27K#(e<{21of`&k&ZQG9ei1`Pu%n9!sD4tYPYGOUn3_qw(sOn=^+LO*BJ{Mw`cz`l$<@sF zYh@vZ-jv^pAd1!4cs_q-OUheiq}$kQ8_9eyq~|Q1o*X0NDV(i$rFe$X@;=gw^W1$) zwL(&t*Xru$(ZpCbkgB@+3{6bOG_qcsieKo{{7u4N(D*NZ78$djMEfeRj7YN~WCH$* z*iiDezGad}ehk_oaWUC>JXQmb)`C*F z>)=HMIrUs~A$rQ6#iITmgK9qV2)c3G!b65(HpBgc(ZgJs?{&aGZI&acZb@-TiX@;Z zPd(B>!d0Q8o%t-3`aP6hwQp@PtqMv8UAu>B(A0hh;d!@(img0SUgDvu`L;|>}voXt=6T{V{s5;S0}9iohw=@g8)qzA^RC9Lp|G{O8h|_2Lw+)KT*(}l_#i4VdETUZ2l-BEQVx^l_ zH#2*C$9MJJJuhwLChQRYY$yM#moGhlDd8-y7*%WzUU(>@>$j&z+2}?`ugblBFjX;& z^`Lu|#r2Ej8?Fv~+BdhhXXd(fR8ba$@IU^;b>n4A%am7Y@5Q!}QD=M3$n1F9+q>g_ z1rqGMy(XmK@hXaonLN)6`H)pX{3R4YBT?~iDojhla^-C5tNh0iRp*Rz7zsbhfuHwc z+0xwn!0t-W>^84SJ1zdHJ>2bE%U#{&|N|4CvUf)KS?okhrY+hs@ml0$0zDz zq|fN3;`7%Q+gTLO=ifcw7364XW!%4F3nchA7>tZl7l1pz@$;<0wHR+RwM2CYPO-;$ zak3S+NLd#(s}8wA7e&g{5+`!2J9cT=P7yK}`70V-*iW@+jlPdR4FqsZiQ@z!Zu^7G z0FyPx1+RUa<)!MbFt(0H(@@}XpBDz{k6w6`N!&knq5pg++#kS&d>KCHU_pvTK-QK| zE}eV)2r8At4UPkKojOYZf2w3V{s7C^Y%Uw2pmAIJf*A|f7R#2Jy}3j<-Boc&t5|t_ z`jLn-cFXwl5YDf!b|iD;80!y^dbpqSb_uoLs7!ycM*(SUht)#)4kHxn#nV~IXTe*NJ2Y=Yr@@Tv}vOh32I94E}XGejp6kK z&=o$)ylYo~Vl!x?A-2s(BlAgNMs)OW!Z%^U3}s!yahues(-E(&ET!&Bim5!4kThE- zHJm;%=uaD;U1zY2DM`WhZ4<8H0b{~RH^#rkaB0uo=)`dYUc|^>v8AQoCngKSU-@vqqXpigIMOY$117~@5e%0G{)hh@~S4yT2P;zHM?s90}r{;9;PJL^Bshdza4B$SH8mur5}t1^T)kh`RBc&{r~(xmn()>d>tuj zb{_qOkDgpy8>6x40C;#wtt*YTV1CU{`O<`YN>p27ln~&WG59NH*%o6o4<3k1zNw%L zr7@dgz^30Be%Ve5KZ12vPL7_z>WY_=LsQon-k<-9eb;jYFSc+sZ_|VPUR;UBt=E9x z@nPV`>;mNv{qh7na7%KlCPxQabAz4*(+<>~k5&`25JNna+1wkjn0BcoLS2+ky2{h6 zmyCAeND}??157q>(6O$Wb=K1J^&P7;1!f6e_{zy_ubw0GiG3CcU4c*&cw=@QeSLG` zurFWFQRBAEnrVhP@CT5j7VSw|4TLft6cg6|Uzutl^tIET(awMBRyOKH z{MZ5{xC`VHCD7C1xcrEog82+Yt?D`GrL=3vot!zd028hz%>g{Nx<4?R%)I~LkcZrI zgyIHrAOhD_oeI8hOhKX(rTFsKVVI|$qY7m6aTECqv}J8wxEK8W07GHJ^>T23N7?b4 z1FOcPtGdAc)24UbpXEB>fwsULAF+TFQqemhVgjSklmIVrsuE(*1QU2g!5lX8C<amF`Vt40v+}hd0v53J(eko`SEoatczYoyqD+q^vvv&FpWaqjx++(;^j9Hsg_4= z3ukR^Fw<>BYYz`nOBBV_E|f^07<}NRznVk|4p$t?&h?tV+!p3m7i{_fyZ84aSB$5^ zCc!UDNHd2Al+@#z%}xonac0jMEF-)j#1iR8QRFdc8S3HN`(-XopvZ@0{ncdfL(3CI z2}BZo|HH!U>o4ym(R;Rj*$!!GeMqlQp&hget(8t}zTbIfh6Pd#)_UhRS&JUQ?a!p2 z;{I5%U!o82{@9QNnz<{+eeC4asJ;d59OHq|Qz%$cQA&?X7bPBPaK#Flojg>o%&&$y zudh?RTTb+UF;b!LJ*Bhj#~TjtcJSKcQ8OUce3n;7hnf1tY6oVgpr7v)KkS`helGOa zBV%zO)MK=`1nI4w_cq!)jjrqU7BVw4<$5_-^ch(a@A-2$6?d_ERDfn(#^MdW_1S2Jc>FY4m^%B5ACg>zmyc6bDeqt{&z+=cWH|GL=*U*q_HX| z6zdQ(nzrObZoj`s?tzUvC3}N9*w`d-D#prMp#prn>X6!ojzi#j3FR)DMd%3*8!yFv z-PR)=?R>tiB5zdC92QBjcg=94d+L4=a2=uCSNEjnO=kjpV$UKRUIr9;)=`=k9~dd_ zNX_SBLcnuRQ7xe$^XERj)OS5lH5RCQ}zZ3L~*p91KNvCdqxV^0i;X_9 zs9)wcMc88V&txhNSY2N;FNY297(AQrO<;kU*^23PP`F}~3|uoNGiieL^^a+bTcX#? zH4Xjv@zV{Fgty=wi_k?jvR&1(aWB2WK=gDMw&PVzX!;d-mTj4(AOML?DiM+9!3*Q1V?DECF4$AQ6tRfbjkc) zvYeWl@ez$Yl=l>-%Nzy+y0<{r(b^oT%r#@G$fl=x0VObi=Q7IkJRi)WdXmxcI>M(J znlb46Q#i0m{r*8@w0{+AbX#X<;q0_Q$JD+Hs@3_oZ*Lf@f57Fpo`@7tuyUQR?U4Hc zmruC8wTO-BpE}8*eb;~8_Qt{9?`ip%8kOuj{6$fPIlSLUWnB%c18lLKfE(NZ8=$w@ zp{5cryv9i(5!o*z@DR$%dS&G0p{Y^W3o}jTD^(d-0@I0l3Ta&WYjHgC#oEB}@kLi9 z*B7ogDN8tL^@>h0%fJ6!cz{>7?YMSejQclX@+v6qoija~N4#CV7C?RDlo_wsiHLIc zH}rGN3ifxgIi~a|Gx|9BJg~`W>5kT2&f+VIC>UiqfHbXme^1AjR-i@N}9bXSOcN{|}6a z299ZV>Fxwx@$LfXY5FVZ4x}evN7REi;X+@cBx&OU$&oO^c*`x0D(g938&GeDid$$1 zd>euI&%pU$F@ZUU-96ys&@^gx41}Xz8EK7`iEq_DUg=%rnd0{6uh`lsomVA%$9j1j z-0XZ#F)ezA7|ZmOw{LxszG@v>vS8@az{@;s+t$`y>(s^1xcP(Xmw+Cx;rV)C0YAud zRWL*+@rfkr5U|$4)L7ften9J@){g=TzG3Wh<&AaQepj|CJL#NTn6rA`@76N2YP9=@ zWA|4oqgqS{%1@`bJENS4U{mZBmz<0`G>6`XG8X-ClX;1kLrM!XM4BQJ;cFWeydu6i zF*rFM={1Nu>XSQ~*L40}y*oMB)^7cRq(NXTk*6a2JCmn+CBLt*LA#H`J2K0nTc#Y;#2UmtfkT#QJ z5XKUFpmKG4&49cqJ)|oE=;vw@@#c8pd(@c+SVaOujGHJphT6~RseF$k!lf3eYP$+r zt+(pwb?BvUk73K(I8jaJW@yIJR{qnN@7~`zMkF_1fdPD}_g@0{HDs5VFSM%yx zHyrEjksMx^%-QK`RfRt^l7}zVad#UV=e1$8^RbH&E}sYtxK4(>4QcV_y`>DIo36;q953djA9&SYh`i3p-9Q4;X`1peKutyMOr|~cPS&>#n;A~QB>Z!8Kx1Kbe{_vQ`E(0(=kbpXB7LB(lm_H=)c}QE z__MhyqdfE;|C1owy)%-Rn=+fwkQuOXpEj2z1jT6XADj`Ofs3(cnP45X-p%E$???^# zJ_rqfjlBeeZ}Kz1?A!%Ku(CXRpwLI9EFev^bF6>PcB_?KYoYsexTcC3s*vW)LpdCqrjj#mmue%FmRv+U) zTmg0|@;p_U;Zn{36#O3V( zCK2snOiVmSZ`wD`Eb8-56fJU633%#Vg>>dLkYdF<&Z|iP-5h?065+YPxK!1>3$K;r zX}RvhnZPX!qw8{(T7RL57X>P(qSJtzYAU}#A&dd)dnVNOEoy}DRfL{lSjc#P2lzyK zseF4h6H=*duRrYb)%C%~SDR1l7@FNgT|lI%clRy#w3a-ddStzwVxsMQ_k^a!#)S5! z2687~6Wwt}aEWh%ybQQ<mE)T@eH8M z*od!eb*dTztchr;bx1;_jE{b?h}o5ERUSkQxe3zxeob&>z>WP~SVZT!fShyFNd66^ zNgB`Zhe!$$2l7sJwCw@)a?P|n`$pg?BTW_|-UUj`*`c}O*Q=>@=;wF|j&OrGphjXu zyqi)r?8#9(+Q!8tF;inqWuz}{J^pX2wdOZ0@8KX+z)9ls4dR&DAIYs?Rcjp1()qry z679pl0==*}mU&v588lxig73RXTZGZww2QR0A1)0Cs~&1W?Fm(}KHw7=YG>H5l3{Q7 zvh&-3In!So8cQpqIi&&N9GNbNG|C+H6Rz_ASi=S?ycS=|3BJXdjf*OrsPNj=23+Pu zkQr~TEjFaXPjU}pYD#Z*S1PQ{g}n?srMR~I=ht#8j{IMXmt#ejKuW>K_DpLM*$~fh z6a_1e444L0Y+?XP=>l4=u1%Bok|H{fP@g^>$9M40CP*8gU$}}u9iULIU5}dit7)}Y zpZ%Jx!c=`bQIH0Fnp5gY4M?#r-(B>tq`F9H4`Y`Zn8+WTj9X{PTc^&)s|xnOiS5T51`D23gLQW2(A7{GQz4*7#} z_I;pYiw!rEh~HNI{WKDW0*4YS0%?&UVd!?VQr#crmcnWA!UeA*&5bcx2%7V)-+aeS z>vs0o21!j(SdQN{KH;wf0Cs$FRJ)SyQYKh4zsOxl5}iM8u%6x-sk@xZyoy#r+K#m96fc-m(x0xEKj%w|loolnp{tJ|yB}SdX9g;W z;Pa}f=S;$9oG82+;oDhq4wubvuGqHiHaFtksl-&VR7KC3JH0n9+|1wMXutP-Mb%bh zeL5|`jrw)5E_*)2%E@*7vaj*O39o>ryPvCEPrLWUEc9pLorNRlh`}Wby>jw2dTegz zk;I;Wp|f)YNsChwW;1FgDjS+#bk?k}yKnYPSy?$mKMSE@Hm8`cL(}29^OHya)B4_T zricUXlAK@((7jl=wG5vCY|=9jJ=vU5X-mHmS9X?L9V79ugUBH%F_DW$PgEu6n)m z7sG2jJkS45>|%mrup6|Dij#~pwMCAZU)u~_f@T+_ z=JlN3Uonm217Bj#Z3oF!OP0)^C|y~wLZeO}IlO;z8yEZk1bA;}DCR6hl0xed?EybP z1V10e6lp=m`ZKomLbq4Sr+Gg!Qp3NeRtw-AV55IeDVRaX^mv!x5)8Zw+N`h21YHih zD;N3-y}FQQue$EA(S-iNoPg&=+u^?fAQPJ;~hI0EQ)j^n7 zz6k!3MN1V7{YOd7wakBf7VLItxpmQTETKERZBMER$_(TgEU&4CP%PfOX-liJR%=&7 zlPCl?TgeS5QLrxfc^gAgdmp)Eq`u{A?x(13WF&%Lr!MvwFZhWHpx`o6GGET@yxfwQkp1_)(Z4OgUyP!Sv)XdwWAU2vw-`tp*ZY56{K{?e>|A#8x(}QWl zTcE_Y6X`eE=o1x@Bw2?;E|&av*1E zy(fh4juh|cn^KK*T2nU@?6WZ|<8O5-v&Q?oWVD=*^sp&r%h*fF*5;cEaL1w8{Z0~vVR z2^}e1lDhbpyG>*x7Bo8qjvZ&F^B?~b`w1o*)^$cCq3hlYAv7~-3D_#Gg5fNxNvhB3 zFK2^4HHkIV5Us8+VOH8uo}yBh72en-2mZ?Pd@St!Z`rO73807HpDSCLB(PT)6d>jh@ zU`3S(bIF4y2v)~wJ9MW%Lwf4zbgWEcP!qwtZ%0_qu6go#!VZhQtN%@{dleQr8yzGS zo~D(0hRnpqV)CnMuU_m2rE{L( zH9@!6-+f9gcQl$dl`^s%;N=1qH0lo#)D90;4{E zg$`1udI?O1`CCRH(YphBWSR8y; zjwxOic}CS5Dr0u^ED4_idM%|sj;gXi5^||sNMV6!U=8M4Ue_=mbI$lnx%+5~TluBP zz~%GxbXV$|T4c!7Q}FoTjeTyU2;M;9#Wk=+OxoO7ka08@yTrtauLmmuHX%JSFW2^) z529-&U?|eh85ezmBuY9J20n?+`82?Fa-XUmMWjL8j1-jGaY7k8u&XvA`88uQk-TgF zIkAN>^zZe>0smdUHiNjh0)Y12Ci%SlB(MbpVUtBejYLk!>I%1T{93`d0Bb zfpHai1f>6$;^k%OA{}~M-}<631>BmW({GUSfW$s%gKYqjKM?8)J!?L!O94uIIAq3Mj15+YhM$7&T6Shr=V12dKXl@d!MNGzxK=igVGcHbgOM%X&#n(Ml(q;qYxs z05=`e9H~oS!`;n4R@QGV&l5spL~>|xYzq@DY1qhhhy$5e5)y@I6@;n@Ka+*Qnc^!8YTeaJlrqY35$QP^2kCJVJ2-!7Ies5#aOb;2mQ&M$f;jXSPb1x zE+$cQkR8|9D+Xeg?}`~MSQ(KK71y76_mlG*a5wGLmGdDOIA`^D)> zkK}~0kR4w;XCk+eEj~6`fm%;@Iwm-oj2PccP(*H{<5wt2&=EratY|I_=oOt19O1Bu z=siH@G^v9iJ#VptOMa>!d#U@Yzvw_s17{;abkt9z{1xjmDv)RD%J_$j`;C_^M%x#y zXQ$wP-2|J+0Q^=q5hV6>SGd+IB?j*LT~{JyZqk@!pk;gZ`GV8)QsagLS+`=YG&@fS z7s?Ed?Nkf0(WyE^bk2D0<9IHsP~M1`lj~9MSc^SfHBh1x82&?Oe-9H-vg6OELo;H# z78g@5m|?r9$%1nps4)WM;yU~Zq^92bdKz%mLcdz=IkGIKM)rFc&XvO}@hP9%a%d?w z?JHSybHP^JG+l8GFn1VKrn%g9xRE>Lv3 z1J`3B|5R@*zYEqb^j6vX9`kBW!p2<2E3q=vgrOzlWs7-#`;ZtGsqzH9%{@ZT^nYif z#?PFS*|=b)!ji;YWtBK(mRk7?E`?vsfZ6x|To=oi&5y94=NHf`MJaJ zYVyYBmMP736GaL9xjpCzM`MAR{`$;}DX44BlyMh0qxUc05- z=1OcnUmE6}Ha*+6|43uo*T%PH&yQF}jJI6hUiKOO3Bte$3WlKI=l(`eamo`4H5)vPDX7T;O3@8%xYmG6?B9yhu<3$!|zUyJf6 zGub2Sb)zl6?9@Z^kIDB(hP`79zu|lZyWRVJQ+ie?RiV7M&40z{m(0RgT92YYBGT>T9vfTlF1!p;Y*AH3j-$1 z`zz3ii!i>IZnWbVT6?TV7}iEmt&C|D?%}%FhZODnI5IOZ|1lcvS3>crFY7Pi7*SuZ z@&Biw3FWSCGai&Y#(iC)R;Xwea>L6mbfZcqE#xHw%C|VXy!np^6i{Kf2|#e#NA#5C z92$IM1T}#_yae78$00MQ=)}qGJGKDbPS!k`cWuj;_Ilf=8b3|6&+*!ToAjbOsy#cv zez38+0ac4{vD3pi_`n|&qe*jz zN2cql>CAW#vSd>a>bHc*G6@r*81v@n2$8^=NJxD31Svj+WNC^{kfl-y_9syJTfFkK z;O#`vS7Cy^SU7OW>>LakyR4gvCnfV3731d{zGk?&-zXlhYi`2@Ta-9%$usYPiE{2) z`${jn|3<(KtHwW}v_JtHiQ8aYk>n-&jcHf`ahweqImwhH#3g4b@p9R$+n1y` zQ_r?dq5s$Xc-mlxcdE4*q^$>`Kt^rio)LT}p!Pq|{y5rkk}V}nc)ZU5IyOc0F23b9 z&+{7+9B;~SNgW?Nq4NXdIr#giXIIAK0CtS#zxTu(#QKbw>gK>A6-zZ#D_AOw>v)ki44>s&G&&KNauU^!c^}Jk9(X)<*I8A8nk{#}=X&^!(+@EH1s(m_ zjyYOCY{q9%#WY8c8Lji|&sSl8^6MvOk_0YF2YSML&xTCEuc%3aU~gBg>R#KM4( zOMDG~vXsea)}3;6WCX)WX8pz^EAF8i$i z`GWDbq2JM!`p{(O;XofrAJ8sMm$9&IbapuWuyPAJh|p%FX)Klz z(v3#QA`OAqeU8X&Qy#0GfF4oHRE}cDUR=y{fq~11*6t;#N)}w7RI(_r=6QiwQ4?35 zrojI9F%HKpmo029$h0f`wz6)!ycE00jEgOQl*SckgGqnNc01raxm_*ZP3FLoPnScrr$ zURzJ;zc<$#&=nl^+HNe~j@h+AjuV_mVOOO^AR(#Rcm6)_6Bc5NP)xF zUAoWqL_j%-2)|X5q{*#bQgB=EX0sz037bEG$-lx?T=6T5uP%1?zm^0ijJoscKb$^a zJld#OP7}d(BHFMuZFMd9d3P#sQNAAhy8hoE%oE2ys5bSjnodQEK7KMOz{2Xr<491y z#I!-!U8w9bcI6iU-qazE<}>NDB*77pA~XMwbk>{^i)uW0Qn}q?+o1ImflQAvSgPm| zRcro=UCRdnRYn!`q475r^d07h()dwhiw81cTJ`1qp8EGJ^a}sQ4*U90X*liiVLq~s zM&P6iKimffCaTyg!jHeyb{m=An}2d;4kk9`yJxL#CvAOAS9qiPd z=tyxeT_lQZIPzdHl952bhap=#wm`=om|K?Fwkeevgj|PM2(<&RU;$3p{?VkY2_ORd zfA@c#dZ+ETj42AlENXcMRWy5T%)=LJ4^lduC!oxz?mB@{T@lrNafUqgt192dBJio{ zSYz(5eM`!rSnSTX;*FdcUrmYRv6JNSz&E~W^b3+zViTzaMuM~O$(sOr>h#$uxOf^a z&g@hu_Ci>GI9bdab4BqR0X$+s{jVp2U9kT$Bi&AbOc2;gpD|g@4`Uge+47(~djJ|& z9ilxjX6fo7OB)^*53~fWY{+kMm8(2_O0GzW*=KBn1@+DV4$3XXWm|6^S( z`o`R`O1eYU$TlRBx1HSsE-d+{Rz{mhMMiANufJklpVt=CF>#+Oz1Q%?wMbcQ9WZH$ z@~`_VHVdvb>5WyH^+}mqw>7{1aGm5e`x6WCi$PRq9BkRtBQ(?>3$LZ|r_v^cSC*5x z;K;{M#xNodEM;>VB}1^Aff)L=z33EI%CDT*7Ml4L!>OWnvd>1#Z+0vmhQ+Mh1z{P2Li1eH6}*D+lnycpGW`EqQ*v7 z9!>pqOg=vrtR#1g z<&MK?dAvtgst#vcGR<#2Bbhl~Ie<{_eqN(xs@i%xvt6f8aXXDtOYHk8w9nxKc@%UsRI z>e`0vD-&6YA0zMSC>1oNgv^9U4*7&QT*7b!2>YqY;Eel#Zbij}tMfsR=C;}S*o!%} z*Xzs2t?h$Q=4x?f+EXQr8h8bI!#kT&>|4nat z^Vsv;_kG>p>wA4ar*`TKG$@++PcT;5sE{_3*n-z%YJ&;;S? z&cA5VAd`Ea`;{Q<_+pj1Q-#;wYFRhRS(;D zJ>P%nu;k43Qrl12rveh9v>kO_=l)d$eeql2Dxnh#T%GprqfrST3cu8L8 ziJ2U@8<*-4J@(r|f{kChwhKL8uNSLlfwRE={Nr3F%ESFx#lyb}ZKi%T)0fxmE;U1& z1F1iIf}@$T$(5zKk5JE6z?;RtA@e!wJWrkJU-E6HKfrL@Vx_r})}O4$W#twA;T8`= zXgBNuy&R+j9Je;yW zXDUH!V5#o}kGKlAKQ8FUIcz~4hb5dz$2mz|kFusrDb#aGiE{dQZZ*zYkqp5SlveSr z_j7IKvC_rm8SFVx%ZspIF&_v?KrtInmG(hq(>(h>9>9!wdCmO=@#ux#)zKp?ZSXyP z*p!<7ST^KO<_zu6)#a&3KK8=7(0i~ghtEz6b$=_1E1ueAZZpn(6_7g_}KrG)DFgKGfF(o?+s^F^|z2>NUsDrxI#tA4- z`R1~qnHmxTxfsKALAj&Bv4BQrd&qE!`(J;rMRslojj<>=gWiHV0qFIHP_FWthYMsa zrZZ9AomK-s z@2sp?i(Wf#Fc93)UvtFoyX3ELo_AUKQJ6?{&u=b?f8!mH zn9x}5L)j!^wjs1<^f!ram1ABxtuMu%=)P>)3IKeY9~_njb4 z*s2w$(jxedGNRU7f7m9)f}u5wAyhz>x&IM?)t^6kts78-joYD+L|b$sz<(XJoS)b^ z7F^vssWT`3v!vXet#21@x6PKD?fES%IxM^Q$eh}_tG5qa=(xcFU*Fhj{iQ%P_$w~T zbYakW?{?CxOuqf=-Qf_s!{zBwR|BuV%MT%y8AW+))p05?|8REo3T4PCVe44g?z}PF zkt0877zWFRea;(HBH$7wGFaDCQOcn?18YOze2bOC(y8bJ!zK@DnyQ#>>T@gdtD+&Q zZFBy@0=51xs2DMbZFH_V&hQ;9D{4?SZ9+33;<=PM4d` z?cn-uMJ=_wB)F2FLQ0A7xI*j(;y%GE-tLkqUefUuu6|OY#Ynd1Som=3+7HdCaNCDq zP#PPVva#c~kuMy*!FGj@YeIeu^YIl_H%_en2^blo{(BwBC7`b^>Dv0QMky~kJe#%1 zUS54NrEUZ0_QV%Fp=PI@`1w0MH}e7UwDZc$%ow&@OUYjd4nMBf#dBz1l{;z3Fo@W# z6z8ceN5H!NQ&al(vUIf5(RPsa9iAQaxOdG)P0%X$o!~4jIwCuIcP_8uguSi$GrbMq z{Uy5oemFv2D;Hk~Z2EC6%WvFzq8k#H8sbNPhPfc8frbCcphVPlHMI*e$1|cRGv7~U zhs~6Pjbh!>f78yb_Ltmsi%1G%>LzGNka)MpPr13U=W^dqe0p63Wyqz!cg=)>P9!#Z zm5K%vhxmj;!4g#kMJ2frnW+U!p3**d?bL9%$u`C>p%8K zYPBvc^W_k3W@-Fi5aQ8E(_E|GED$P72r~Rr*JIF%8%ocq!kxL^ht$W&fobZp}vS#X^xb?2@E} zBv0Z#MA{+DN9Y|*bzn9FB(58RU5EyANeb%U;5kKeY7^8hlc zZ>hw?t4Ha7EK0XmUct~cJITU>01yoLssmUaXM=5ML%yLLPiLyBEYi#3N4U*Qi!vp# zML@$K+@9#xkG6Kg?ZcII^JbP8W_r$8lw1S1u>G`pa`fl{Ue4P zx0nt3YKQ+!ZDU?6Z$Lzf7ysxdOb#8m{#qZrs<&W3kS3h9Lto~Ov6-1th#gu(YT0V! zpd@_V%Eiuk@30TQYmq@^SxN@xDfW;EaERH>34Xd2uq*w{YRMnPU0yXgB0qo z7rtNWJ?dimLpP^cp)(9|{Si^(R?XrcwX2fPHj`zxA*c!<$!HX4*^1RNy~YJl%SRvP z9?9P(z*DLxn+HSZD|~ovX+ONH!ZS=bpIC>L*bTHh20-IN<46E@))R-*>UKpk$`E@L`3Ys?gY{zcglCvHi&jT2*tYyBYpLNCrpd1C#A+!^1G`L zLDBKI(i7Bp^k0wiX_wrCU;o};HC@)d>*naC(ZxHfn?IUgwskcu$q(=E`TDHddwkdg z0XIyxs%>Bjx!6quzTe~_syWI9P{XW*uvUSQH&&bq5Jna@ z#inu!(&Ngc!9ZT#tvech4#3!N@wJ`F zlV<4-+eMzK4yLi5?f5NFSMCDH=XTLIh$_oV2y4ftqs=7%$|$0RV$iol&0H-Y6Meo; z((mI8879z+LYf<(g3W6ZQ5J6kCyw+lVYhez=I9Dcs~_yvxMN$ieD@GV58dahTzs+r zEwRa*1GHl$7Om;NqLAuIY#L%c#Y}1{^i2Oh@@Ol{<<(VyfM%3Lv61+BNMbnNJvY9( zC$Q9GS5AxWp|=h~O-$eo!r3LaXd9w7!?Ob`T%&34Xsv5Zb>1~m3P={~!z4Q(Ip{qoi|^=N(l8OW zodrUVw3N~ZmZc%);vQ-h-{9!$kwfJ zh&d=b`@)pqs6S|cPenM7ZM_8B6oNXZ1Bbk8xK)1h?MwrjNN^RG!v6a}8n|N$t-ql$ z%<`s11X<@sX5MapcMkjEGB@wU$aIa?aZ6+!o+nR)t&?qMp>i>WiViegj+D>$%GkYb}~1Ip$m51f`Yb z(Vsfw^m&YGE2EEN3CKLjUFCB;08xB&ec??Rrp@eA%+6N)$K_uK8!lDAd#q^3)wrBnHixV4p^i=o>a=SWh* zIC|HT6ovy*$MP{jsmUb~`E+PR=JaQqLzf^=NaKMQzSp4xIxNwJZuG9U?fGq&8r%j5 z{zDUd^4J`+2r4~n+}Fg4<4d-t$wIBZ>p_J+pf6!cz{hgi$Op6Qn0SAt<( zMM9&(%wx|BoeJuCCHJ*D?ZKQrWOX=3@o7y)4OgBC@6b(zV=dXIkJx{r+cz?YxtOVo z*&}j>8dcU&C-z>AV){+J3p6|SnNJuLZM@w;SDPmB$FHus{YZtJ6DouJK-aSKQ}fBn zS_ZjIxFC;1UPtW496#mt#Z}U*r@~?2$C1kAWaY0)$osm zJdikw54B3bUn7BRuCo9W^4BDKKM&%j{4>9ZATs2M3nd0!Es_Wk&wwZ;OJJR6_;efE z)rd<#U%MBr2#SRX>oEZrx~90|Ovy`?JGneqGCw&FK6#!dFcUfgjDQ_<8U=^HEjeyJ zTJfDfYHyw4%~vxf1elq;oUa}-v4b972Z`m#9G{S?`Kdg<0G+mZaG(F z&S~b1L#hzq3&?CQ^g8k7Xc8t57sABeZqZC?9&o{3n#^TqZ|!x1pZF7o7ZLO^s-G&J%&*$d$-j=Q zMe~E*r`^5m^#(sCj;!HiM`!mXHi*0E5(vHAG!Z)^pfQQB<*%unBqulJcMJHyBjK@J zCKEWe90oBln6L1{&{1`G$c0 z6&89X=vR>{3dZNq;yiLAR?;1NZD`!pcNi>U@ed}?{j$QnMg}E4oyyY(G3BI#n z5&J9q4R!o$;zyt8hQU9^A$O@C)Sc#d!h87DZFAt7GmXvmwo5R2$>e)#V8?h3y6fp* zP+6zsGEK`bj}0if6R#Azcdu~AGMTc64}jA)LUM9=H+KEytz%Q;6Sxy>Uba7AS?jrl z9z=%Jt?hXTlpI<)!nf*9GF8~N-Uymzmr{6m+?z6_*Q&`)CMg)b7R8)OknS(;3O^O| z7qsV_J~{C&X*G)B3MYIoy{KSd=LyQ*tXDthK+s+JRcq;eCWH|LemVvEL`QJ=IQ7%! zpGwv#7zRIZ>1;gB%8Of}xtZt2+N@|1pP~N=SWWK$XRgMyc1O8-oD&|)VBZ76y!G`o z+?&5g!FHL}5m~W*t>{hpR~Y;11|t{L#(G{zs=P$*0Wk$JBOhi)j^(bnyh8AIlArCb zbn*0mH5$sCoA@GL8XDo3|Jw00wXO4f*}>(iOZQc`hm>hQ33N8B7(tOZIs=t^wJcS7 zIxU_&UkyG9UVkd;kXJ!9=c#F*;E|fkrT*agymUx&=4>qQ5>4y+r-;+ z>stj)1E0PJbWUu7KJ{Gu(0$_En$XNG#V!Gng4y^YWv?o5fl}pvc6y>YHoxlVY(T#B z$TLna0~w9HF|twU!7kbiPNk~CuPL`tm9I@L*?f1N1ufI7t-7ig;6dd1<jb8c#NpQ2(i?N{94 z?(0JNx#YL8R(x6Wiz%Ls4rO?3$w-U)&YGVO_$QAXMysLr-@)O9HB$XVz}E7fw%l{p zK?SuX#Hkg3->9AjJW?J2!gx<5o;SyxGH$i!mHj5WhJI@_qB>&yp1RyQ=H3|OzDg>T zHZ*>0T%Zz_W%-|s#j$px1}g3fC^4`MSj){iI6qE5GyUM z0cQ!;qrZ_Mv|uOrV?IBXoI89tOvr6*BZmG_j0Dz^A5jgHj8GJppmZb`fGemdq)9iP ztEKD&T$3PY121IxF0mvpn@Z)TGnOHh0TuOMHx?NR?sww=T|>)fIJ%<2D1kb_Sx;#D z*=5U1E#%isUrRi*cCE|W(C@e5-Iw6>Hn|^qGd7%8!@2+agmXpB zV-NT#GNpl#5PTE)zL!&S;`iHq7j*0veQV^i;|#d#jz?%8w*)|I0zc?tLU+Q?VM3>! zK=E7S$Q9P}F!r9uUJ^x0BfCY1iRcsFjcwUgN>y(!SsDkFyj{Y7qa;?s@>-_cgqa$V zN`ue&)&tn>Gp)*5&O%2v?k*aPI%JRnq}~ygb#+BRpEiWbw&>bdi|(2GiA28g8@|FS zy7rW#0V%QZNN+N#tK*)cmtC3f4PibRls9(wOpVtqsd9 zuoJ27n9q+S2O}EUyw7v-_;d%beMAZGbPHW;WrlXt40-b{Rk)*OXbnyA_WcFq@PE4* za%bO#4>K2+y;#G|s5PG&0^alqdp&P0ruE+D+$h;5JxObmB2j7CrpsRCdtFvbSl3Sb zh|lYD2T!H8Yipe8DFMA9XFKy01E+9@+f(8TB}PKH5p08e`_^oKsjV<>;US>x;lR+a zu^Y8K;6J_o^+%-QRsX$Heg($ykwB{se0liaZHbNEf4tp_j2*`avZ>KVR29h`&YCBY zS?@}9qxMFne7X9z=z_R%DWSo&n#3S@m(D*4UjNkfee#pd)W{Hb1y3cxp>oZ~2hSY5 zoHOj zd|!*crW5RO2hT@lY5@&H&p6brwHTosV0@uZ&-SzP-UWcBiB%Po!0x2IGem!%Q!Rcr54##ey8?-llZ+dg*{n~i#9D2kM+e}3F&{BW1rC==CptPPZo6A)R zy(MV@K1{O*ew6^{?8QvBUz;rfCd1qfx_YtxxuA@2dIV^@zP>F^kXsGS)5?p8;&U!B zOl5nN47W1!bx0scglkLonF|r@Ne%^ZkEP~`i@>hGeGLtR)9T`{fc*Z!DwoLPru?u7 zI-}QNlb?J$y_LNIEIo)x8wbmry&!sS7}&YHhZ7C66Hg~B+05X^VP=7yK2cusCOjbs zuaSh9v)+#T5oj0kD942LWoB6L+esftL;jpf3zMIik;mUErc)9@dJcr;sd%>g@-l?r zOT&kT!oAX2OD;m+@Xl}S=UvxPg0ZBIk`oX~4u$(R zUK!2M6wuE_{K2G$B)BW`M}>4n`}lbD_5>Lk&yjD>qG>KKFmNy$Q5QKeG!t}_P|#O5 zG%N6#r8w(taVfC9Q;_xM!=k*{<%6y*_O(3f7k-_S^eT7+Zb0m4)=ZrN{DTpk%Y1$- zfQhP(zihpGbqmHovN#v!8zeMSrm;xalNe5LfL1F_`ki&($2@s7zgu7Sc!``CvshQn ztCm(rYQU65Iy+?LbdR&;_{D9j?m^ z&)<~Ja>=;vz0I4m{j#Sb=EGzbI5jjTD3B@~Bima5#Us|=Mu5+?&2`(jsZY%N;fL=t zo;>HKK6dwr4KDze1Ubjki>0+_|Jbw^-iw+2F34DT=wR(5>xPSSY&SZ8P z{{?+p{~r*>{Gz$=W=Lr%_Q#Q1aq?m;#51de zJ^!yeX5UnNUUmew?DEax9Et?ZSQm=`I1O^I##rHup8s|Q(T9;UL03_c=(7LSMtVNkb;-59S2 zDp&m##tMLn|EYL2H}J$BqK$53m*vHh?Ci}YGTS`5ZUVI55Iwz_6Ho`cxR`Z3nIrLIm;kd+V> zWc&7wsMZmB;mgZ2~|;9|OJ{drT;1w6WDeABgI@fJ&5AP=j*(#5w;DXk2gH zBmu^%7A7&t6a%S)B#u+HkR-R1_(B5`*+hL5R76dN+P?_$!X>|PW&T_r@ay>(_2bzKUxko@F@8#U_4>7NfRp{vYM@8Vgig0>?=Z(gKKyk zf^-*>P0f@uCh3_75XDNWB-slDEH84Y0;jd3QiQ0Po?=-Hux{DBIj*>#vX2#uY1P*n zA~wOm;e4DVs%^j!~2L8xnl}WzC2Jwa*VUn5hGAz~5|6dVoXRibBfnVFKVu zD-%|ds4BJ#u$N7)E3%`}J%?BRJ z#cb9Q*nUZ5P+mn6EhTWE{jJ*n_GmFI9Zxb6cyj-Oum~l}4)ixX2{!j+?Le#oMGIiN z_BIRcI9P%4j$g&x&@Ga5nzmbcjTJyDk9SJD$&gxdg3${ngp@4g^;egBVmOWUWyf*P zfGeI7O0vfNf9l8uV4{&IS9KLxOu}=UAXV}&Uc|ZXNeN|3EHE>?$zk19IW3+vLTl00O~}g{kNTYGfaoLHJ~5bilV4Wg9e)Zsg&@=ffDYTgY+NK${YtM<-{36WG4(V zOHmpk!M2T{yTvOW=A0lbkyBH-?TRnvvoRz+cxc?Rlk@+T+^UM!kM(F_)q1wU zb)^{5t{fm&(m)(5qhOGjEyGtN>Z6~%$nE&e=!tk{W|Rr$O6?$wbC_A!s!eG73g78_ zWqQ)YuDyFw=c}>n=}8~Goi5r%Vc$w>CKu*OHPw+b6SJ#TXG2{hy^Ylpyn&raKd;gA z{(OJK_vXgv(XX7ppwtI)HKWgnEP%@cUpp-I5%v;<3mjStI2WfOi3t)Jv#B{vE+Qwk zm0+tV2)NobnQ&b;(LoYIdjl*570jV)#8#ceXiD`(;}cn2y`A=cXUX-?_BMP9GBpze z>UheaysrUyk7SHUg}ldOQEH;H7KG59nv8g#P9xr(nN*}5c*VhZu~+=si*jbh>!{kNS8#3z+9|geC?=w z0;6s@O1*c9V*cG>d+dVvj_^^e-w7ce2(V4ymfAb)n~opoALm>6n9x%?Ud<3L_YeXv z6|A@>ROLqOe_j6NZN4)j%ky!m+(hSU!Qip-Ta6IG!DCHN`dz)OLhBgO)P_fH3r>_Q zF&SRpSTr#d)aZPaUs)YoQO~F-(#bfw_Uxsz=^=mIc+sYeJHL=0AH`mI{-Lv? z>ZX!)ZVA`&am^7AVT1~G*Bb#tMfWa6XH@#^oqt*IvHLaR)SW|<&6He2m&UHQ?fE7p zF&-JF+Xinw_BMX-@1qB8AHN(>tQh0YDpL`(*~F^pa-@n8()4nr<;2&b)fFkp)_=aB zayY*E$-H=6`$V#$QUu*lUJ8U>=@T+JlmiX;G|09#5DU0>Mq%&7dU&$KlFV1$GatrB z{4n-YG<;+W7_iM5%RONjjZU%NP@@4mwi^iAI5 zYe7EwjO*X!md5UidPGz90dspB7?H%Peb{271-OT%!@+vTm9V}+Z{F1D@AvtgnGqC8<#-xnjx{We zMtHsX{w=C*W_|@jeZJZ_>XZ=aQa-$Lt2A$0DPetL1kHD&ev@KXr|mVQoyX@KZnUCl z1U;(N2D-JhIhOW%2Z8YLA5eH0lW46qlg)c?-PJnldV{Z4AFDT@-X#5UY)oOMesUS`9f4p&rCx6EHwN;IXGkqSjsi&?T`<9yYx3pXb;c8>e2SMy5;2*km-R-HC$UuNUvE1M7FgTZD!1%p`{^H+JUt8!2dDJHe>rK< zZC2`{TQ@5}a!)`PY1Eqd2wVUga>H6r=!9_WlRFtOGE^UvPtQV9x&EzsFVwp-&Ak<0jK7THRBF-v{1lX>a0}N4CM-gog~0zjvJvp{zZUA` z{bhKGlfybdwVwPSdEBztdIP$(g?0{nE_+?jMDNDdsl_hAGS6le%Xi)Q>7hk#nPNp8 zG@1(`p!ff}rx?dqI$>X)ozo6d>K~k#eqFL4v=+G=b`H)jT*ntkh(Z zr5sD-Fx#ZUSY7ZW_YZobooXxb?GDJ-IKcNVYN`$%}lQ@LqGAxACn-Q&{gpC~{{FwP<9Ncx*Lz zXyKRRflA}i!{0)0jd-NxU26Udiu&NaN+z81z^u8Lst8#v`=&78_#$Ii8gyfKuRTWf z-YksNVt`DOgWegg17}l=BE~S^vXU$YM72*mCP#WBWZ3>p22s3$0v6H-*tV%q+(1ueM z{_fcPc>w_=Y3I26I1GO?KYIxIt?l;MKy|aV{l)na3^JiRWJzin^UuwT-ape|=J}zg z32w99$L1=HhwU1Ti7mfefmnYlRvQ9zoOXc=$v{)&8+FlFr`&a@BiD-++b{|st{e%@ z@cqBJDbrXthCl6ec2&t+7u8myva%w;^8UE1d?uf0;Xx|-eMq9Rp7l)N1s7oUwUlTV zLh@Z34^1rk-A{cVXs>_ssRO>_RiPWOJ3;^n@Kgiv4ItE{>lNiRNQM&9=n;mqQ8ywf zZrW*RDh5VM>ReElZU8o8F8BeE2=b#_=&TE56VRU%@7~mL)lCWrvQ>+sO?G zC*!$f>({>!-4F^!n)m8Rp&RpC#0KdbcW-Zb=@s#BvJZU+kODJF1jnYH6f!#&)=$fK zB6cA5VL72X5~p^u!vUc~`Q*la=R-|q)s*TNu}EK_g0Q@9$wN#dcBkiq%6(GT!9F+b?*Gl+2W_MdeJ=^EOxcN`Apc)d?~^W@4<`9T1S zLfCjP{AIan$s1SK)bs}z+9+%4r8&dm1HMdL z(dr(tR>pnOOPWul{)0_LZ~TBANf%R<{IjUT1FM@M_ib}+_&*kz`elR(?YsYgbq{x* zuwi2>Y)fJmmKsM)s)oZ^!KN?Am%Xeovw}n`5NmB`a3kP3U~aW4AECrN71ZFTR(C{g zH_@`u{9|oU5%Dk_#5JIv!+Idrv1(u;2`|rlMR!HZEONR~ov~8*iN0&vtG&BC+qoKm z!%+L{&4dVd=5K2CGVH<^SPo*2wb840beiPb95vjGeKi#Y`p6-HSmq6!Sg=zpFqLsB7Nlzmm~KSnqfT#_Y%bc__wEhl} z8?`1j3Yq!uN0Dh2aIg&(zkRuhu3k}F-o4?434SE&GW9zQ8JZKH`z*rpi4?ZE=~hAE zUGv0>E!7lujtg5(<8^81OU*6vKcS*6T;<| zEWJ7$zGC^ThxWe-?meILCZfMM?6JLV*u4V@PU|mz3E7%mUw`A}^C_0Gd+?&+Te15e z$-*c#;5%MY4nqzpDnf*%QWvGg0VJopi&lkXl{@aMUUM~r2s5P$%AfRYB!vq}PEzCV zCEz<^MVz7RCG0i?@H3dT;ve846w}%GnhaqZ$2Zw1LmKw>HU+|jdcK~C-|YD@+Dbdy zS6?V7c=2|2%dktrj*hh}SLkA6mHDVmc+)RKleeV>d@)moH<6pmdsyrK5tWLoMP}k! zN!TPfi={|v-qhx>XNbhwBDE6I)Bh2Lyx^)K5%1pN&j_Qj$Fhz_1QhBYXzT})F3c<{ zuDm5rd4Ggmd2#lvWhKV|7Ty;U=vngkT9FtZ=w)oYJiE1Oeayk_JBqyO2?3Vd-|`Zi zD+-9@(^wZq4qAF;SO`AP5H7TxR;g_bGk1(63Qy^W=dIw(PMsk*TM z^lJ;nIuK|09lb>YyWE)IlQ6ljnmw*8--h3DS#qoEWKY7eWKN%D;(r$Z7JLlfvEC8QfQW95d8HzpEc9T(j|GT#BTqw5kaFDJsW)1b+-DlrUr&X^Hn!nao7Qvj8FFY-{r9F_LRRaCwGF_6**P+hgu2-X1L1iu~Ca@HLV&*by|bB5I9UgG+x1sV$q6 z89>ZzMisF2g6RNSu0%C^7E%X-{XU2S89lV%QZt;O`drQe!CKLVTO?!+Z<4F#t!dX@|IG#y&1eRdUSXTS# zx!bGjOXbys>0?JGHtD$VnQOk}H#0>SNzp_3J(kO|>V{7G;m-3tug~I1Vr!(Dr)9-p zKlb9$6-udZ>6y!~8+$I4A>Ta8CXr1>QEPn;!R!%`7^^-;tig*z0uq|x$F9-(azteYz_0iWS)a> z&op#)+`s$ujhhc29luC(tNYEI>sh-fwUWJ++KD`X0M(6xCLjH0!XR;yq;kzN_B+(` zdF#vZQ-BEZtNePoKl@s6+(}X(oDGr!e=^4I$$$_QU7O zLCNkvc+1CEtK)N6L(G}TXMu11C!2FjiVzyjMIjS1LCmd&`Y5|l0d?ed3XG#A!!OEM zl5_IC$DHmYv(G1*C!qvi0xSB)#4AbSur{`QFEwDu1tv`M^kwW5m^)se4*m#R$M{yZlY5C8Kt{b1LH0d`r+Td?wciPcFJ zB}JM?viG~OE%(e78y=o%)oW#^w$S`K+nDvVRA(J`Shz!{R-Oxhn~Uiu#8asj{` z`^7vOQG#L2gi=!-c`FHAe6H->x=MH2raMimenUqf`X%iJ zzx?~ceP6x*sf&ssV8P~{hz#m`%3v>RB>cy3+hB=_C|#(mR1!6{$YqQZ=i#-Pg}Z%7pJ$G_P}m4`4z5De*XmG8_HejpqqAUsA?| zPL=i``Ele6qP=A7`3yzZ2!zdqZ0fR^r&#vePJw;@2*{L#?&7U>{wc5H=0M8JGc$jd zSEbEBjRFjeF}|IQX>69|G_BoPC}U7NxvwT}W)6wlIAaKSW|`4wQpbFENNC0cbZ>6_L+J76=AlnH$MwRJJ0YJp71)9^^d9Vk zXCSs^=~E**R$ra9Ues`V%rNuBcC zICo)$vL?PPD{+9JZp+f-R_GC&k#KzpM%PTfFV>H9hbS?J_ zLeWR1yCk3nrwsHq=`|rHD_Fh{{XN=q1m99+;?zpnFEq`c%&};L>^NF#>^=mAaK{~; z>xA~=XE*H!CU$?kER2-@1E}iW)N7$>4w@oN78D`+;t~W?0%A7v*39@`ZaG5MBsNoe zJxWAn%L{?_^>*n$9IB$kv$cVl1@%U<^)_96H`L?`P=wS1v>KUIcw1@xhi2zN8&h{{uwshI7#d9fuEAS zlpE58o~;G8Az4Bv?i^kX>LK#rn%!$Mo`h_YM6|HBnxgf(Ih#h|+lZNw{PFdsQfG0g z-Py)7O)jR-UeAmS9J9vU@n|X>GHM`=J$bcs$%~)Jtzb^{RO`Dx%8CsPCQUJo!?Q2e zJ*r)(D{WYE8v82boeqJKq$tdMxnzMc5Cl|@_!I8LHdhS;TQas2EZa`h%e7b_MvM+{ zD{MVmiWa0hUBn8cVsR?qr%9!l`T~(ve?hgpHheZ@OK6k%E2*uda%Z7vBU$Z8 z{|La0Plc&aZ^M49aESsfky9&dYcSesRC-D1^Z>mv@X}6-S+g*vJ<)@+aNj|N1+;5) zovVSHrw13Y6EPh#VN53ZB(&T`1ngw)t)E4nJ#yjVulb?(1sT4!Vz;lsL%7=`T9yu; zM(HURyu+&|m=wxlGl{{gTxINP^HI@{!a^R}VkK96?YPqza&xbOHNV3P*oA^QbB`<$ zki{911ua#x*Sr)19$&x+=>zT?ATv10v*SOcqRM2|IGBACiVwx+PSW9!m7P z3=oPV+D33WFQpend$IQ@I+CHb&m$|zq=g-^SPm_FS$2Aec3P6oY2I~77r0}qyEeFzi;R9E+}Ry5e1 zZRx8h#;JK^2H4oT8k08}MZGHJ29qfn`~|A81_gCiz9>oB5ce09^^7}))C4jVK_~3Z z`9E-HorR3HIYrDJ)NxL&RPz=JP83`_-zePu}wc7ZSz z^v)|1ycj4nQ=Sv%3?@qz-6*2#eNtoLA7GlnMR-+iEo{3wOWw8Odo;JLJY`S46bXED(+ zct@THl4cnf8eDBw^Oijt6jU9+Tn!c%R-f|EyX?B>@@zrD$1Sr}gpv66_b;YT@G4vE zo$O@?(u6Gg`FJ~L|9HoQn0Ie4WY0&v>--C)#wQYPf4-0!md&v{-JhEm9IzJ% zqcFFwMnV1FiD881*xQN$x59?L45V1#moMlpc6Hl3Ip&o_ ziE4q*d*i1VWN6J~WW3#)E+mrSM9w#?J@nrr#_W%aF{2|)Pp_4j5$5=n%Yqj=4>u_3 z*K;e*aPh!^499(ovbD{hBkidJ=0FJHNr&dx*AGFB50-f>V90V2*Jfi>5%$bB#m)RT z8;Mg~XOYi(cxFyHUeax^JsOR~f3&MN=% z^a&oderFs_SHDcUip2gDsO)~|Hffv3DhUs-HzI%PwCF^T3W}WIMno>*E4@)8daZv= zFfrb_byVTFd_H1e4L6Z$$SaAuc;1_Aqx8_C3mQdp%uW!(wKi&XqEkol*J*6!-{lt; z{d4P;^?o1TNOx=EjuzqKs&SNz)bGAuX6!L;;%KS*7&|%tE9mZFt9Y(%B$teSjKjlU z2$q(nVisJIY(_~B37WoyY$>&n_-;c!5e){&GU`;k#Qpo90Ldux=X3!N6~6?SFs7>B zX^bvQ!J)q3I|;l@l`h^4P_7a}O5JSTQDuhyIB6krqODqbxEaBp{%gg8ulg z%Va1c_?U}a`IqC7v2i{%6Ww=%B1m0frli&djz_G8T|orN zLun^La3!^6i!}m7Ro-57J$C1rLUxECOh#v{$>&Ya2^e1b;X;Gy& zYmCc?;i5R4frW zGBQhLPsdR2+bxk@61qc!3ul=xfc!+jXQSq*D8IU3F=|6Dd_lj!a`<78DFWYXEarr^ z;hqAThR$cUZn*i&;bp1wO6T463F(>PL)z)kESENV(>8@XEczb0iJ2}n=#pF%PHA8z zFHmN<;qjMOM06(MLUOl29A}If9%SBegIro;ys07ef0+pIzUcSUBB+U`=1(1eZ`?q2 z*v0!Wi14gPua(*%gW>IL?oU_ET=VRlbJy64t)MCPvL+j;%%uZW30@lox`s8kUfOvM3GZ$Hk|kp+bDl%l@cRRXy((G%z|g3Em_f3#B$0o+fzb0?TJuMtK?8( z<+O6xoR><&un;>{j$;e+$qvu&{{H^-vVFGQpU-_?*Y&>M*ZaK`A8~A;xT%k_r0N-s z)F(lCxH4mM)-nCX??E6?ZYrW@M|%C_K&NB}y~(CdVwaA;ei;>@c@<&1F6p1x@&5fi z)c97kz48NXc+qD+d|T7)AsBkk+1uU{j?etddG5}e8YY?ECB^kMbt8>&jm^#5ko(9I zW%<2*eCZ#Mx|KbU^nr=SF&l?3;R9Istzl2;ZDUw&%8X6X`nN;o_?^+>KjHU~JJ|}o z5!vPUeI-y0mS^C#a6F;wWzdHQAU9UDp=vw;2C4)J+E&~jR^ktdvFS@fbcYsaOKrLq z|8`l0mk-p~MOK!%iFa4t%(~dGIU-=VV``S$n^(IWIZb0(G+7!x#8%+E;w{c-g~Tua zesBAOzONvHsbbK*EHQTT3UAL@Tv-;7Ld!C6ongE7RSfknDkdo03|e$`%~XxoqSQN< zyc$H{HF9}^@!+C)IPsq1V6I1RRb`Z&1&4vE&Hsk0!MqXYl4v`=;%fN8^i-zIoUVHj z_lZyGsElQFk+&0h7UE-->~nI2@-qjF|I7Gm&!JvS2W7UbkC{qrHdLhnQgVlp! z1vZWLmHfIrRnbU1w6uWqhDxLd3E-u=QCQ7j`gWuyTx(i!=sl!G=|K-U-qx|4J*ASUwHHz+~6>6hh|uq&(L%1*DT4-Lk$MBLQzFWk?uRu7c#d}>o6Oy zL6OCaY*H5KFc}I8z9x!jFVd+MlM-ZypFuAX`@vSR5<0Yw|0$Xra!nODI-pr|h%48s zremYhm*^u0aP5CRFiJV_dT-c^u%}2J@!^sCtIIzHuD!oeX>A62O;*9c3Per=X@nJaM8itZ)w~zbF&2;du0CfcFo->axb}>yx8(bu(tJ<2iARfOx- zUo=G&C8$hgl-U~f=hMq~X5d<EusG+4;HAudhMw3!|@EYcD0M%`C*3x!1m(vPoW z);@8y_ZJlSmEBYZLf;1?*fIinV&#m$#bl%~H*jA)_3P47{XILT&p9HGD9io*jarJ! zavH2MuB~bGi_!Ou^BnW?4P3siHlugRDX{!-NSJxUhbcX#e#rK4d!vS$R|VMnc*x;m z_nMGu{Y?`J6HVghCFPM<9H38!g$_U#^0zNL8&z=WGRw+S;+$!}m}k_7S#!1jc1-+C z55w}c83{|bP>lg#nqBpXM^bg?6|d&zW~~ae!k5u~TNe9-ot2}vHTqTA*%0j8c$+y| zKw(GYvm*tvgu92tX@Vt*%pshtd_2+xSEPzR0TtmKQ6LSMlJU2h2!F8;-e`Hl-2*?C zxsj%87ESLI#ghFp(A?{r6RNh3@;y5L$g~CiSh2s-q88^foD3>v!+Ks*bg;jFC;blg z_%S+%^jHIC^W0QJP+{OsLYA-}ujuG6j6TUU;;H@Wb*>Quu+qbJi_AaYkGbiPR8)jp zCaPfb|86epmB;>UaK_*KV!tYD;Je>HCnp>s!@?NEP+5ED!|r#g@;lH4 zqwOkcDvI{YXb$Tymz}^u-Nd^`OpSPSM$tFeA|+v>s9_OW zu+CaCaFSV}J2!_xM5Co=VlyMjV`yiW4c-Qa3&BPimYzSS6Awo4*#q^qm;}B?Fm*9KTdNaM@2LB^n85{(`%$ z03dzJNuY>fEUZYZsr)#t-{$o$lGtI#D)@x#Cy9`pL7w2udFm6D3#<6kZe#f{4#^IGXxl`GxOM)UhA*t+i zS}e)+o$$Q#Z<$sk8$Kr;^YxZ|MQlKcZ)`{#hC$# z46gI%MJLqE)rmKsK$bKEwlws7TGBwP`tr%eQLkCTd8>ikFy^z1iai!Jcry(Cc1}+c z387lMMLtOf(Z*w(Ep{@Mmo3UVuQ?-?M}Wvd^aW z@i5c)AIoE!*^z~FgUt-yQg_R%e*Z(v_cJ5SlH~`Vh&xtCLZQAV7A z4GU3Zak_e75n~|J6Q^?$5l3|K=b<3-wkAthCkpV=daS$(C)%60Io?FR6p+-uHP=^< zZyK1*ByOK_&nwC<9`g)lmX&;=w4vZoFyDrO*#^zGG)TyNq6*Xq=F=_s@J&$G39?Z4 z`upcW;|Qodrfga>VoTN4hI;?_Ovd)!Mk|}GPKfZXtUVgLyR1z$pIzwwd-;?&th#ZL z?mWFX;MsGdx5GcIynmucqqrhuuZf9tX1F%HGGMAsiX#lzV`tr@28upnU1%}yyEsU; z1q!af(OpFOz9&nM@G;v?Iu<4K%s0-vrKrBFmD^kpAEy)#Pv?q8zg-%h>L2^&-M&}s zq=#1(SxTP%KOCslB-R2Zj@%NOfjln+SjcP2bJQh|Y4S2ufY74LSECoX7FUMW)KdHj zsfpFwXjbCrRJ@BI{@9}0-#$QGVUOr`_RpE<*c&WDcT9QEl-~<^qcocESrb*iJevSA zu8&^S2RlWPa6N_IbkVlAVt;qj^XN^`UA=rTK-RGIga0AV?@SK9OF=Z-6F5%91{LF)Cm_iMYA$Kc%k{w&W%l2!s#6 z#w~OfZ4om%h47AXO))ZC>WcTtW%%ld=BUM&l)23o?Qek|ZsOor2`#lUn6gQ9Qt$9< z|H>$$Ngbld$c?Sm%R z?i`Uf`YRH3uLuRvt7hPOGIc*uV;6B9UPnMlRv;i{uuf#{#RJkKW@wRj7fMtSrDlgm zS3s(K>w6x$WyuIMPa5LuAk{`i__L{A@rB=r>%cvr{-^YY3X;=Qg%YI0%cqdWHK1g% zGN1f_w$|&b%nd;GyYbe2GE*_L{Z^1oCSDu5!$)tMlr+HZDR$4;hh|5;3Pf&dq~*J3 zbXOj4-kcB<6LRA}r6QyyxYvIx#_6?anK>zqW6%^RHNyo1VU8@Zo?^p~vWpO$m(wh^ zpW)dg6E+b##`k7*`tBO4YVxE&YjCOF5P*CxG`t(IvVg%h4CCV3b8b03`-XddaW1{5 zto|agbqDH(9yZ;bqizf(iXKl6(M3KMS^!Evv-Y1s{3UBGNQVzg-p!rCtOd{5Ju5Ft z=i{Zd>OO&$v9v8d4whwUrSC4(9(v^MLorx#HGh#mTRGh9<#n@xm;70jnf=XDsAfyo z0AUS%!uJZ6*y@fe;51EVwL3$TB2bW0ITY*^^` z8v4Xzq{xoE{^}G`HFYaB-+Ooe%KYh+-IcO=kZ?oBxwDqiYlJ~~4R-c|IMoFz0dsZC zHrTK|OR+Im3%FmQHdv%bQApk58A-O-v=f`Cd;7gW4m;cbr)XaxkAAPnv@wg1NnQfd zFBuDG#cC-xVqGLD)SKALLJ(D;9a_Twl=zq=Or6OC0Y_i)DNJ?(Y?kZ9VG{39rQklz zR>4ATkHT!1U4URbbJkgGbY2wtpfm`hgD|VaF(pq~C_iWs^i$V1T@$gqL}gvfZICNk9|u1n zR*=A~NTFM6o>PZ~T002Tck6uIdyEH0`d7v!440xd;f`lwvSOo-X$J|~Nfqiy;}xyd z-cacTkNyu~FGS~6PIRJH&xGOPI?*E2j^!NGmN`04hveQ@=YnZxdj%xo144N6bDun_ zz4IF{S}0*_(Egl<7?Jz@*NOAcR;e|3a?jHCK<$iJ1A3>$sHtR3PC{A`TOAj%NfSPU zNHZ7Eli}rL^I+>I2|B5#a@!?g-nbdJnmVyelFA!9a;dsTL>aaAt~@usa$>Pmk*rvy z=<-#AETq6qP@W{9)?s0|2+1W9*YrL!drN}*Ao$GWT%?|OjvD^3!srvJMS+&7x@**ttH8oC%*);OQ-)1Z!*|b?phsq+}`(W zTI{&F1XV;zL5Xg`JK184@GTd0N3jL+s04^Txg8Ff4pG6}8s8k&5nGs>E{m3)2}Z55 zqNEKO9td&hY`Dx8BD7G9o{cbAAQGO{zLN38q{w6l8-<%cYi4Wf3cZy#TzPJ%sBOdoSom5WE09@a&Q>F&*Tw-0xyu$5{GTAy- z-skNee^i^q_m~CmTlWp`2Vq_iTg@mkKd}SPq(VR*MoCJbL@^nRS@q+O2!bonDdTI& ze3e$F7T{^q@6KaoTG(iQDtY}BM;WoZ#OKDiMZKsrs|;Cel@}r(@UnhB9`DR0L4OiS z3PZ&>XA9xL7|u!J4Ut8rU7Y%WR4^~utj-6foN&cJJ#bA%wG+4DUY3fF!Z+i0isD*9 zgMvLfAM+aL%|DQ`+=kM|Ur@OpWrkH{R(J)|^w?h0j1#Pv;SOWRd@3C3{AZV(eYRJ| z-=;M?m|Y2fU@PrAn{1VpklVbgeCO@K+25D)`o`TSdtlJcWSLe3kH{bm*YHLN!$k+! zGF9nORO|eRo`9Yvi>ShH2R@0|yN0&FIL(ZKJVfqdbJjO`Y9!14q}kITZxQo!UlhzR zaC~c=v>dy4DIbTbjjFV9udb7j?-NCyq!-*R$OfB=2RaEB;3w zKAvK;{1J#yDHy8L9V_B62ApPwNFzemIh?aZR}Q+AA)I9R>Jkwn8Tp6Ivkt@yGs#em zSR;dBIkj)m;LkZTlEFpBq5Sfxp@}njbH*YY*;Wq;k18i4 z&1Kqy2%^+cwpZklfg9eIKvV$ZE4!RR405U?hBG(#mboB4BNbT1BuM&BcEi(=}^Ld7a6^X8GLr16a+CMaLt{4p2>AJOa7U5u@nCIf*| zY!q=5F0dA*KD{t9IYJ%vlm8{pp$E?2~&c1?%4E)p2AIe-~79a%|SkoiVj+!4f`xJb{qO&>A*Lu^tK_~ORUIsp&edBVJN zyf}XEq57YE{>93t+fI~vKe(E-qyNLJSf&bLfKy|dQRj?;;f_e|LUw%k7T*tVh9?`M zL9jwr5-l|@Bms$q_JQemTm{FtM@~wB3J~^6!xBJyBSo0x8DA9R<>O6)JwgR3_JBHnAuQLTL%=HW!=dx%Nf#?+~tZnr$WX|`V>D) zU$Y{2Pj>B1xGkltO4OSFF`dc+-qZqWX_%AiV^hO-%!tt{G|MCxbtx_$MxR*X=XdXT zm=dUw%u+2$b!>O^?EN40A%| z>XRsE8N4sVJ@$gp-pz$*EOY8O>@=BS0F<%cwHP$q3gTxra(rfAFQ$4e3t7#J`wsk| zG3!`E`$kLR%x133mfl4$z?tATN(1G5velFCt7A9+1DpCFWK&-PxpyQ7fC$19D%wh1 z;=cDQ5#!{M@sCo6nHM(o3SxFu3O(mjY2oX!I@vM1N!#!KepN8nCrp&@;7{V}UxXcs zexqC3P~D3(KV>8Cd>c)+sDIa9oo&k~1%B2k3#dDOs4etB7#i}9Mbps%m-U!HmsT35BssZo%We*!+ao6Ac zZiqjE+w5pBjT5=Vk3PHn>^~)9+QO_ojGfYRgB9=7^g1v8_F&^oRpZTgx+)#|U@1iX z7`oW#^9#M;I<#1~k+bxI$WHEd%zWP zrmee<>FpMWbGBDAJ#H4h?Q*`niQRiMT0eokBhqeUzpIMvvhCCor7l>p^^IC{&8ETH z9L~FshU0+_-k7MO49Lr4_(BD{X;)dvQghB&UCq1Ca~}-$$4I7HW)$&Ljozi<_6w~O z8Qh1Yg`QK}i#TLg9COBfn|Yb3u^^)$N+?{ko9~Kfnf`mkx|EzlMm0J$zrI$wOdD|= zAH{~!$Ojv|w2W*gbS5{46}^Z6ddTw}J{H<7DdStHfGqA19ShgQIrt7CcJqOkUN^>| zj=33^-=0_bGnucZ;>ki9&ah(%mBqpKQJtm5FQ#*BwsbmX%dSv;)JQMy^SO_b9J@PDEnp!cit zsmUN2QbTpse6>hkLZuM&MD&gj3olV<9rr!n9ubX9i06{kkG*u_S1LOVMC!$%h3H94 zC%Ky3O4n8}G8OHHrmQ-71a~iabtHOtJWjPRhATcl4g?zREi%_nsT13d1z>OX;WEFp zJF&3p?)BAocMgX5y_c>^K2){3xE+;v-!XkLHt8 z$huZUU0ia`p~1$#qde$2UHaJ??{)i-!Ij$!>pcR^el?EKyK$dU zA~(vYTN_+-)*=a{Lm+vwNTOG(w0&x97r~CR^*R&>p&o7p;7;w6@=X%1MTwL7Dzj%n z)r#}w@S@Y3>_mQ@(G;%Sh-Z;LcNHoYqukz-U09)!@u7i>g%;mA|6oLJ79)B<_!6dA zg*OAf45r=p-!h%=mEIBG}oQbWffZbp6X2L|{9=Y}?^p^baDMH*)A>Yy+FoL-IdYvWR9+g18u4Iowt{GE*7qEL37D-) zi2wX6yGjR9Hf#uKEiM9ZKu^8QEu{IssNi%G&dj~Gf%?v~yq~7mIi5QL!DLp8hhj zptk2Nl^*Qivok7sU4!@07jkSxpyfhxCq{G$KH}E}Uw;$1i}0CKW7G2KD-DFbR!b`s zw2LA%Q0XGw_qBbSda7Q0yoQfb9mXGo{w~_ue{cMcfkTsQIgykq41ct zg~+C*2LA`7CGz7L{CtV6;>h*@=V=zNWUel7Q%-$DGv^4Z?l*!De6TRw1!xM1d@Hn8JctVE@52f>p7_yZ5 zA73+L17um>6z^7k=J`1u->}30vg+ss1~J6t@V@Jm2{Y5`8MbU?G7{6AEt5O^mdJ0q zPsSEC2@tX5mbh32!lPpd3%L`w-Exv;F%80?_#X7s-69FI?faALKS?}x#*2sDZGSrPLaw~HZYmOrW2pJA>SCdLUGsrE-(bdAGJl#g~kY@I?<@$oo=Vwkx zi}lkM*JR$ZTbg0zeHc94+O7Q4w_P8l|DRIRVS^K0-vX-Y^HwE>$BgzaOfg-5qwXwE zm|j@3yZf8XZpI~rC_wbVR$F_9ohN%KbEz~B#*R(b);FLU7$2_YMV1V^6-om&SU-Ty zc?A?9IeHp21H_v)!0SlDi|<<)DBY$rFv{|IKU+saUXhhMNs=iq>Ox9L;yze03_2>) zGjYgxn{YRF0e5XAH5z%ONomJ!RR%32hw@$ZV!Dez<^QpgXawYSbt}5?V!kCNY zp95~&F@0|xX1D@tAo?jX3Nj=QO$?tr>1n)J(-7=ZS=G2KYxVEezr3VOVY5JQ!LIA( z(eSZk*Mb1be@f-@;7-&Ha>oBRzOIq(U^Gzxmrh+t{Fx;Jeg}7>u=)^f{&cmXZ}0`H z_FdnV{>2bPPiXGSPii?c;Q3_z!EYn2QLi{7haJai4?1=bHxoV*Md#Ne5waV#&{{D` zaslE<4P?i}q!xF1IZf@DINuwL-#Nng)RxqEH|PE+wEE}_28MU49Dyu40jTBGPr)DI zqBY|4-TNZ*A5iA2{4_J$ev;YVj_l0&DthuUeaF~;voC0J^M|J1TM44H=Y(_b!xuaq zK6V$k(Yf!~K>FpAVgp%v0;+Ky+6M;HKaoY}D0j)4fd9KC&`Yy|?~DbZZ*y@k!uxrr z*rvnFH~m_9kWu@%#?buhoL`FZk3XHA(_Q;*`7u?zlPG`y-o%W|NY2V%${cv)RQP73 z9>m}=W$G1O7}0r)L=241eTu3hCR=f|@rOhf!YCdLCes;u-Y4?}gmZPsp7}SFZrTcm zK{6<~#n3|ROED?D_VQAoLO0Xkl-cNl8g-Fg3@k3I=O4&mVPea6dTnkCY%BgiYUdF6 zrWE9kDU>$NAc(dGZ7?0Ze`?5g&3o|$^8Go4J;M9tn2+~}C*&J{I4W*x?n;WD&ZhgN zmGi<7=}y3shU);lo1u2`eqqeO-di+vuL@Vz;}t_>)+Y zJ@`7`!wE>qVAnt-N$LQY)uALJfe*A$rAqN}gabeq?t3j%&8!xDo5B+3;0+~6O=SmI zwal%Gk1VLdw_0qNC?IadIl0pN`vq;9h>F&n@3`3&;FTP)=C z`q=j(@NA60C86T0B`vB#y;qrKyH zl{f0?H9w2b9r6e}Egl3GDe$lt*q;nJufrU1dp(Ej5JrTi zCuyRz6`S#!o8`4plr(#^Y)N~guUNb#=V-{e!` zDYFy9A0QlGoxBqK0#NCj2X5BFHp`3$n^d{;_oCsO5Kg=sS8MP7K%7JDr0k@+vx%jeN;j(NK-_5>C0rBZs3yIG|t{?tq-0;?F`Z zB{&w(=8m;X_yRxoev`%1!x3D{cKjJJBeCg^*CTkl0h#laQrWS0^g$plepKeY3Ar}= znmDwiWQwZo^Ad04Vlp_J=W5|knOMlqDXq{uS7udw?^by9kgSiiH_-EB`>~o>lh;`d zPSb061Mk8u-`4;s;EoAppf9JqCLHO-ysrGRn_IBjl4T%`P@oN6;-!D#VN|FBD>A{P zkvH-nPTULl7EioxE+2lc7^1{HO^v`==cALc6_nIDtG8Dx<0X@kf#%0j>gMx8wtu=b z55$Zp!!vv*fS~g*G6eKngyyScbdh5C%3S&y`~?wsy1cB^Y|fHT3|P}4r${y8*}*Sa zS6+?y1m-rR4C^^KNaIqy9~9@A93w_&Hxhkfi!=gz9QL>cnR#@ZUVH93b=!N{d&zMy zpZ#=ko}H;;;4t8lVL#z`5N-H-GK{2XKAIJvw1|A~7zs(jci6wjTSZK zUHVkH{9|MVh}_@)_LE*4T`#S;T|X2wG#b3@l%(i?45BIyn(P2^zwHH-@QwUCd+9H` z9TC5ymZbTn%J66X3`@X$DLc!ByZ*(UD65R0F9Yn= zR<3Z_j_2f;vH!s0qO}O!dJQfbz|{ZIGJ+)r_Y&U)~%Bug(*@}rHewg2XYAodWIFneQn=!v@ zQCUCi%fSO=qA&U>pSEf+gBThxf&(jd#P^mxg<*1K?m&Tp*mjaaU()z|6Ug2AFSZK4 zMlJ~`m)H}EsuS|}KH<@@;jQZ@3g8)`&7yeZJ*jx9X&+}u93`SZDzzD?<<>RatkkNPC(0{R;lp&nxvc+`tT>$k*^4aG37s!tETwQWj?uxUTLm)O zX`gq@oHG5`@1$pxHS*UFM)%^QUFhA4e9Vwrq{2d7+7>)naU=O5wqT+=xr#WE zr!NN}$t?%=*!f3SvM&3uKtieCxsSd8%L%FjeV^!Wu`cg;Q=^q$4Kd;UagrJA&I&{^ zh^K9&&9ZRFO^o7F(?T-m+fZ5r1BlJbO);NH^gn53VStNp6Vb~5wy(~0c0A?zhwaCY z_qj<*B)+#q2Ykp)vLXHtfVrp0Li?I51tBoW6Y!V&|B9`>bZ#zDIka{cb_uo(6kICe z+zuZi*iZ_Ckt5sLF9l<&Ej0DM=e_Jlrf?;8c*~dUbFX?&I{c06JDN=_g#PZuhGffO3G$6NTqIv<4A9Vq?|(~d;ZNz zz5B?&zyIWDHfL-|eQ;lTeVv`jx8EWD{4zbW;nGYGmwB!0jVD*?Wb430by=zv@oO=k znX1qw446|C4BuVQ0;tWpS@fHnO$sJw8$E=nH%(Vo=*_SV_a;b+#YxR#iXize0uCxi zTm`?gBscLI1P%!@;N%YQMO7uihK1!Lld*>?OAf~`mZji#OSjO4;Q3$U2|WDeVa%_U{kZlMD2|hIWQ@j%0U`i9>-FA?AK;2{W-4mBdl? z=BqrYBdN-Eh>m?*sS{SVL!*q7Jgg3IZ_mlTz88;r3K9AR?(|x6r>}l39jw@!32j!? zTVV0EHQCC4ZlKuo`in>BPW}2RjI*@u4cQl1v2%NNUX6>R?(I|33Tknz@k5y_UInTY z!_K9)+cWMe_9}XPjpXlSfl^Ci>osc_w3l>Gc2a;&p}N!7y%i<5GgH(=h6@bC&1`Ib zD|zD*dNXbat{TW$pWhNoNR9}~^7?9Ibm?)4B9(ZRo=DJ!fw8E(3@W8*u;})*H zt^K5kllb)A;Om{@b#-BeFZM=a)JDf4BIZq&$ABUGWXk-Jq1Smo0{STe>bOn_0%a|J zRB`J3k81|zJ=!HZdraz6vx52_6})~u>-?0vRQ{rWsArP=CptRdT6AUA;Oo~+fu7_F zB;Z+O0H~+c8XqUM3;a(>TVY0qOy*@XKYhx|ps*_JzPajobT7d~lN5W}!KyIYNvpRg$0w#z?L#ZTfs|&iatDFo z`Xpf;^gB#*VZD(a`SVV444zYSU1%Z|x zSM=FuJmbR>2>a_qOXDR(sS)0)&q{8?lN5{YVm4z=J}0n<;C9}a*Tx@b%gzeiv9Kxk z29x4w^V%WQ^=8apA}!f=Ej00t;Xa5_P8bC+u`gy6X^}!!ZgSvbAGkqIpviSNwlhv( zM>Bzvq`U&^0ok4=(GSjp$WN2zGVidmLzU&Y#*>;XjFm#KU-Of+CZUtAVu5$z0riKA zPz~=i?VwmCG0PWp8}~LH;SS@^RzlnUWCvD3n7^q_@fdf}Rc)8=UbOV!Fy7ME)bgrZ zMIkr}iGBlquV^*&P!!P?67ei}h^|f80ZR}HujM6hMAjjhz8>ZTfU`+bJU|eA29t z2hZS$PSZ#AIyhR^sTQf6+6iHpc!mc+Y!&<&mbn%40kL)+`4tq%>x)e{sS#y7~{ z5D+*E$y0d+a*v{&yp^VgOXX?ZU!to+S*;w6E26OpA%L2B$!UlPKW?AY2N&12k_1GK z0y<-vN&J5J-w7b(-tz{y#o0Vx`Rrj7o` z8=gGwzIFCA)1>zpx+YLGTzH6uep|!(Q0R@Wt*)p_$~?IsA(R>9`qU6%cqO+_b4E}Dv*k6a)RF$RqkneMKq^}6|HM{7{lYJ2hX{1kO3sVV9Wvitc zYPw&#(b0_U8N05gU-%N_UPcn-MODr- zsM_O#R{4vj8z=2_KqZ>oti+A)U8iz$sT=sOg>ckSehFPH>FExbQ8}>EO-o>vT>>%Y zoSb~$1uMC~D*G=0*#~}5DaJYRer{er9lzpNrRE^`{15(=Je_%6UdmIcB7RSi`EhSK zAL(R>IhEc!j5?Z54-G55gqsQ4RfTKpb_I#GqTzh%b z+00dXDwj5?r>Q^8Xm$u#n%sBj-Un^&Wo4F9w2^+%SI~N8-gvQIF!n1!+jR2QU#3$T zmcJk7O@?}ZS5jKn`r*s|18H!I>na1ew|Irb$sGDN(wzM})7|~o(EN+on(yD`6F-I3 zlk9F`^{>I=pWvqrD?W*a9bp~wNxt7#30!t4kCJ&a83))VjTKLkJ-fSzw_Rg|OG0*r zCsoN%pYcg;uw2*)Hox>c_ETBG=bFivGpy6`>51e}|3G80G-% zB*5s@$~fXT)!Pe5lrH7*)eE_zU7lt{)~AY{a^o@5KFH}IuO}jb*@1pwoC$i=ZPzLY zwvMDy+Zo^7Ki}%;`%#I6zZCu%v|_^uQ2`usvi@>_bf36YI-v+K_ja`3c9LPFpAqZv zyystA$3}%ud~CpfN`oZtZS$!e*7W*3mwEcqb1-&3apmgJwtGb{`!jMl-O-AyX~Gr| zJfi>-1MvG=jF2UY=2lM3J}Ad|AD2HTXg~~p^u64R;_LJfUMdVa171XKrRV@6>r6>$ zW!R?1xlpT0@Al<~7rT%w^XA0Gyzo!2js>XxLe;9s)Xn(WK>Kf%DB&DrCYtAxNVIjb zKf!)Fj=v^PSfp>j-yEuow`o!A78kuWc~A<$+K~3WO@GAs3Cd+^d<)prbq=6>in3?z z%aTj-yhnGt$TYicreNUoO6EHy(@Y2qObw~E=QgK}xCIr2N?)lfr3>s9eat$v3^zmS zpm(rB>Exs>kL=5?^2SV7Ls?y00twroW6y)^3lo><$)QLS7e4Oh#DJBVHuE#42FUl1 zE=wLZLNU^FkUfiwy$=}hoD_I1x&xJDCPj?kj+_Nsx^rHM@P(iTr3*OshUm4a_$W5g zxc(uZZrEN08GZs8ClYo{)X#Ql6lp!U-it7+=oYAH>ZQ8@?#TL?!E@=JF7NDq?VM`} z+81Ks(CPY77=1P{&NfC6pKY_DKhd@;Xk*FT@7YKGY_sM^@BPP$8|(T}Cbe|PL`Ak; zdO@Dwbod43o>ikWECExGo^_Nu6T0bdC%UjB0vAZkFN7bJ=fOKH@!k+ISnBDE+tU@I z%wwlftP3?Q5`4Fdvv5@tExIbnQL1L6g~(~?bqdf$x1w`j)^lKps7kcR59mY8cUeHY z!2PR~p8sD>V9|;lSvU;=<)&kzGT}}UsTB!_+~T+vBLZj966HR^jz?UL`^fC{ita+M zopkrrV*_rdJKLF6^?Ki;^YG$Vq6g}V1+<5HPhkpHPerUHG1Y>>GODAtTjf^3{Z+9%sU#&l6$2y16oqBNBp0a>tSLfS^xcDGZ^6V6mWuUK7G;F?Z&MmZ2EsNHe?}5EQr*q1DWOwaoym>`U^W4%~8JN9BiH?y+e=)e5 z;4m!W4^cmH>nPZ4oA7~B<;vzQhv%EMM+}Dp* zrEISWSF+Y-7QEN-h=d6CO$La!Yq!Y@K_`|aae%_a^O7)mrb0z#3&Ev!qfDtgq{0)D z+ELoL;&$4`e&P+{ND^`NG(lDDO5qdlZwbs+4lh_1W zy_-CzuaCg)N5d*-R!S5J(XWs2n$>xQxk-59Yg|JcYFkgq6b27`fIy~B;;7ZaA1KyU ziwU{K!MQ~{`mgm8c0e{5h$tX!#wD~F8V}0geC8%xMVp~XP4(bn2)2FPv^OfPBpQ$rwOBBEQbp#=HLFOHLdGZ5e`r{N;a2q0BTu zc25vzaXYEA!{xHoA}!$^0cJQET(u@3B~m&MlZ8R8Tw(%I-3U4?rt;?!#S0y9O}rf! zt%_qG7n|e@0H*-X1M__wqY44r0i9r88*-$B(V^H?`|i#1B#QeSq{G&Z7=Gvj9z2m) zr_vFBgqO=u3y=gq#IF*;J9Gz=Ng&k=xF|ke$fqa6)#~DeM^5!M9gF6vnYHP1(we;a z&FC||K+zZ5cJ-r=ch{luKWL=(=g$&b7EWE58F(>Q`TSB}^^aT8(bZ{`xbcN0-ToO)Xn9$_o@^t!jQYvvRSYhC%nan^NOzB=AwWCKU{M^|dJ~JUUiCi_6 zmsI>Zkhn>q0$UAz3Gq8x`@Trp9^nP0?W`;CDo+VHrMy$vkbkTCP$_7ntvx4yT-oHv zgKfnHx4~k%xv+z{Pn0X$E@;ak)&(%giAek8PW$=Rw_+#B zK9N=nEENNzY0ivHw7VFB1DZ=}r*Frj-a!=c@$L*#X{*?SPs(_WcMvJ&;=;_+yl4*$ z>A^1oqu&JeG;8F#6(TPkEt}s}KX5MfdtnKX>5z7j%>u2XE1Vo-u3P)rHf&9K=!BqRwN6&xx>~DZ`_S(WRN} z7>l@?7!OKXwyMXy-Cb^JFEWEdG23!S`dw<-$6r_aO+Nmy^LXr*h@#k)B1T#iZ~>!O zz4zhhSmkP?#5b4x4e7rQ1^P?ncD?Br@%LWc!=}m# zjjEcvV8)~7q=Dl)L0YW9dgjcjgRfTNHUb*ZPQZ6fo*(;tG1;uoWlH$cYQAb(?o+qu zPuJNF64+Ackv$pu8}Br#4JIi4|5N$|Pj<^&#%v0e8ay^umgu2*+@I@D8rYiBT=(x^ zZ*)t;NpB>AG5Leo)G&36F0E*3$TecYFCmE>8ah$k=2F40-iTl)(jRlUAddK zWNW)4YQmPg`e3;2;>6&mG=mO*fdSgDh@qeH8(E0EN}U^+c!~(O)AcYH7WR`z)F z!FOFw+KyeDBfk1aFLrZD=_%pi(XodLYw1xHrYW#P--F$Dj64Pv^5Svmk z?YlugjeZ}XdH>`E3dyI7V%575`pu|~T6dBT4{Rn}m}33=F$M@=40x0r8&9_`O(j(Q znjT7*IR8R49sNG@$)9YVL;j=^HyjM;iau12`tNImEnUj6)AHW^Cnxk*eGPc_WmSDa zLcwi@-3ML_Qz=qe$Dq0A#Bm;V%@!~Xy4Y5b_UpK1(Jhs;dGEHrL9EeUdveF!lS%>P zTxDC-E^VcyZdS$8Ag9}V{x`8m%j5PkLGN~S(7Z>DPpx}D)Bmr4q z1>%kicplT_u|@yK(Y41jz5jn5m5QiTE@7QRsf2`Fx1DZ;SfwapbyRL4xsA={l3Q*? zrChco7R4$?xoqz1a+v$FVK+5%+ZJZ*a(IrG@Qw6yrL3uGv?v9p$ASYn5_W z85e#|aBj=ICBt!fAtmxrq6-*#acRd%Z9cPfX)dp{FyDV!r-31TR%h~xU&RzoA1{5g z@#aarC2Yn)?wXQ?wa1n-H>ds_GRXH zB9hz4ZaJ9yZ8}_A`Oe?k;1esx1Vf#f38kpMy~7?wJ`1g}FaoYGq~YFVd_w^r9R!YR zlZLHn+wi7u7N{ucZFtmuBM`^o8sgZ$UQ6ykIZ<2>C2m)1qAB6)GOyZ>`q|j_w)74p z5_f6?YbUkJ;*q*axlXSZyO`@EDxNOYP)|;#?rqM8WI(Iz6MkM({Hk3An7}f;JZ!G| z@^baY;kd$FEH1F>;lj#XU~GcoZ#yV&AkLPmH@!j$d3fVQfr_1h;sr~@XMCWX&Qg!0 z*vh~OI1HXY_|g1J;{D?Zsr~nk-8*h6=v9sKhZCNW5>3heJmff>*qB(zAFpdOi(h>x zJZi03%ITMugVBA5=JH<1ZGW|%X8#El)+qE?rNH>l_O~qjPieoKSL&`1;o{e14Jh%C;NXi856HLGMQz`&#t*qN)^qdQO1 z5sIt1?*HijHSA5hF=&keoScoYLy)^;{(1w}Z3%0i9#Wi!tW7-@&mDcYcrc$AwU?V; zw~!csceB5@Hoq)`y=+JRu($c>?jo+|B{mQjDkkXeD1&D6haaeL)j3sc8}O=?V8M~c zCCQsr#L$NxwgBt_zXp9~8s86&l&I07DnfOgGaGVEx(z_1(UWl}Y0(V*z%ygZp>uw) zb7Rqo9(0FE%PT`fhr`AnN314KeM3YXjd;f^rq9xY@F~~+e5~K_`9CE9XH2`_T!MV- zZo{NGSJUQK+QwETiH-}yeo)rhfM^?NEfrCwe&3Rv7{tpD4fwLE{3B*JnK5&3^JC84 zJhI4bZodLL^HL41{BpG}o!LVIHH$oIS#J~ncHx9+oEkv6*94Pqw_Xf=g(wDu(DvaO ziDFulA0#S1k?ohD85$O6z3}Fv3&C`O)%O0`+^dZS8HT`UKPthjR3l-F3iD;Suw#B@ zlGI{qP%=4LYk+Nbz#`>jo&IRdZgmPod+gl)6@JLy`s)UG{r9PUK(5oj#@(z{YP$71 z4BuR3Y3^$~;WYRd?0Pro(EtjopoO+cXjHw?cV~S)OkWgq>)4xm7 z2x6%HR92*VS@pFaGfT`6Et}%X5`I-^cJ2wm`IP>lvAMAMy0bEx<%D%>>|o_br;lHa zI(G58f!m`~{~h4wpeu^TUMtGLbM@0wq z_FDsk*O8%L>Q0k36-1lHxNsGFy?c&sm;nAblB1^3%K?2KiMg=DbRZCFtgTYcrw!ZA z4Sj|s8|iDnS-k)KGUPRYxjm5j_anqQ+_y2d?NZ14jn{H#Mubx>qvJSZU>Sd0r=@89 z$Bevq5CkWEn~IweESKt?+1eYMa&C@-Nvz|9k>rp`&eo^0KnbJmcL%2C_@{(d-i_qx06AYztq5Y z0;7BSbBW=S0YndlY@fG?56epfh6-Iv&T~be z5w=S|F9hWRI2z%}63~(d38MZ$vn2n)HJ#;I7mEIUr;yt*Ss|;x!ekTCzsVj1nisij z*5qFA>Bf9R9K?60txD|FrPX5?ew(Dm<bWR^5$j;G=gkwlNvGTxtr(6^wFVNqY^7B!rvWvx4x zSE+`O3FT!xiqZr}MdhDgkENXE=lrPxCTpDs4W`9p=iD4fTvA`x1N5D{O!Gk9zeFhii?jRFwCqrRFIgDOaqqT_rHDpFrDE$ ziBms%choYBI9o6)}El6 zDYa43pW8_XhOEdt{Lbo;Y{r)Ppq?p{kD9^x9Q_j}bNt13sG|c``P&tP&>bi+TW$^; zbcC`6N$^b#ei~Ol1c|s&VSrcBi(P=9kQ~DFUPN8pa1XyzO#ZxIct0=bvsvLsgrnawzp6wg4jigc z-)zZGv75Ey;%-XN$CGTo#ZpcVJF2(YDU*=cLsy?Ks}St$czR3y4l(lh%0qMc7X@Dz zmRIzX^;PN&FeU&yg~<~9ZZTe>g@qBz7XMSKyg%X`aWa__p&-RC#9U=w+NyMH5gM=0gVc@4+VBJ=@H^|=r**WVvwwXiUh9h29&A@ zw*cJg=zed=aY>{wgvq_lo2N+sLQ~f>x(R9Iog}Cvf(-^zST6%F=!c5KiXngHv5aiT zNRIgyguq#7(`BwD@xEHvY3U;|6Nl#Ckzl0Jh$Y~t+78(x`Ny&n({UbRhPpURvIT%( zPcG=ktdw1pzAVVZe8hK#VLI1u!S^~Q+GMx7vB+r(9Z6+aHDa!ltV$$jU^IyCPQyK* z-E<^eEqHxTssH{);ZO4inEJp4W8h)RAlXay38@_PXDM`!Sr4G+I1y09F)tVGT{?rK zfK(t3vOV&bt*1q*oMxGY^qs4D7IP(nV7@<5wqYR z{vgWlB4iqv49Zl5|4=y0jMlTPB1ssK^sG=3nRm%OFG?yo6xzyX#~6(0ukSs_5x-ZW zztsUVkAZ}jvN3vy z3hu#NAw_&vj(5bomTi#)3rjfAtd%d(&<&EnYdAj%Us&Hk-hS>;j~P^dA8mjGcoff+Itsj18lSdA$- zZB)F3_`k%5&O%{EH)edvy9tQ-K<$@6U&|6ioBAt7We{VE5IxtPkkQ$PJWaC_Xay+_ zj2vhjC2mETybU@5G5T2I9%i1&6uO)5K%JCCN^eOVeB`fU%y%Fv#PzN6LLi2&7i}(y ztO$8lC`lFj3Y|Oix5GO`z8%d5_3Sbgj&};lF$AYt`AO2mO4M;we$AAe?wvXyJe&M* zpbNp=&nFAr1@sDW44`ti4ux+}{3$aAl*~ABlN=Ao8*qAv?_HRWDF|Oc4|f=phq(zr zU@|R$ub7f)31Ph&+h?DF)UEsA>?vF&!t=@y+K>-1P3=biMQo+5fHXt4zVQ#q+nN2W z+1Gva9o@&DUKxs{M>jiJl^0#P1U` zkN_RHYNEw5A&rA>yJ^z_^a>|Q80FXJ$XjGPIm~e?yXk=p$%Us)*`@YOfh5VIeO;ci zQ&#Y!Y!%_cB zfZ{`d!uYm0R+yz{s~AUD7GIL2vdQCtVvfX67$rT;0cAs3#M|}JlJ|SM3 zi50MsTGJR4opdNB0JAafSpi0C{d+JMwP;#I1)t5YU)IB{OB{KPiLfSDV%Y85mE%|W z3AfN*WtBh1gA-8a?+3geuKnvny;J!7vw`k+<3E!ZBb{tCliR<7p=MatbV79X+#xFTztMBoLruo`@SxNZI+D3 zfMiC@`l0K-&XGGMeetAyng3w-QafJZfmHwE64E!b^6X6> zNG~;C6HVP81JyN4(!ZJ}u@T*UuiOMGLd6ggiB4A^E!ehGjR`u?lYGI$dr!&_e4AfD z8jUhSyqls0aTOu7d4!$6;Z8Ze%%tV0-==7QAivZZn;1?&FT{hC(cKVL5vU6=eKcky zIiBU@!h3^R0)Q>vY!#t~$nVyQcr&+(-3UvykDl7pNLx7^d7}E=40g|_VHEVuY&|E; zYtE(6^J&~<3+SW5G#Pk$D4e~QfuP+q?xjdz`aeE~9~R1*S*687Tr#XlTrB^Tx*=T@ zIM4dQ4F;Szn<{o8qaIx7j^=@zkhq>+;70+X?acx0{x-(jft|9)y1G=)U}|ObVfYoh zrZ#(btETYX0`l#OCYa^qzbl=bef1Hl)+TQq>@S7230nLcNxW>??ynLHAum-U=k63Tzv=|B=ScFAHn;9Re8)gWJOWk3}lt0>=%279`G%Th0S!fKGP5-F`ha z^mBaNZ6wnd>b#c_hld>`l=sqPCet{v*0r|v8y~<#{y?_4^6PuaAD=0*+ePgMd04C= zkf%78N6vHbo=n(!zz(d(;_8cjs6(m#CTutzA198O2kH<2ylVSxDKt+5`uq4KuX^6H zKi)8Jbk!pjchq9zcAlXzw)Gyy%s#fy0ohiVJB$or6I&Vl2`Nd7ELI>Z(7(K=Nn6R- zx8EBX!OW(0N1V-m0|5HEkGvr^21^J@EB7(%PvKj*ppRElrnxz&dm~#rRt=LWv|&N^ zFHdEz6ko7K7O$L)L!Trh#duK{DED8d;>M4BSue%q**{WNQp?F%F~E+lpiD8f0eh*l zG0{zKJ>MItgt(spD-$oE!CrLChY_!kb!&ZEr9wIxj)dkqTHp~Bfb&Wx`d$?**CO3U zta81+U1FPCEqDz1n#=`Sutx)l#aFLYnEaJ7k@Idht)HYXc`DLidz6K;2$>iSS+M9J zvrBloDhRWa1j22f^~ukSMq2;2qKL?8E0f4B-SiinIiyuO(|e>rvo6U0&(F?duYjoo z?aLc@ry!?!hHvG8ci$@fmZScHdcGiv&*b%;=5+fXB#kVfwPdCucu%&wfkNOs(&~+8 zN?@JX&AKF9q$w6(3A{o?>BK4*PH3%}cfdt7)5y;I|P ziA**-*U@6MHo>WgUI-72nfm%JLJ|zN1)lQGB8g0eoa$(H9HDJ18s|=|Bmb$u(BU3> z8mS;4fdj6BxD{APf!I?6m=Q0v*~6jaJtf-m7b{SX4m=yDs_<7=@;vfrYj3=^&c>)~ z3$-$DJ&o}aZ^#3gujE>u!;%FB+)^sa{jqGAf_P>gLok(JyH@t3Cv4K@QRva?!8>9_}BjJGO($C7QTJS?alvMG9st>-uDOiv6@Z(j~=L3NNGi3m427H}w$Yx*t zf+zOuEtdD{(uU<9Rb!ix-l6=rKK!aiE7*c5{ymRHqdX1Bc?SFkN#P`{!({uq-s9OH z8Ve@)6#!_o5luhZupi;jWh^w3%%mh{*y?Z4dDl@ZyAI$?UFIQ)FnA6~wPTL<*Qwnr zM#{+p(VI)H3EAhJpm+4oAwfAfwND1P!$vtPMkz>hS4f46Un1O`PgtiE`Z1qI_YfoE z$OR#t(P4Mq+?)iYA>u5 zRdYzhF!qnN7LAU$FU%3Jjg$q`z&x}XDv=Je69%UHI&yK6I%$j{keYH>Uit3a1Vl}i z;4hPycm2I=%Ts8+RBCs)ci-ux;4-EWZSwd(rGtIddI1N}W~k%xlg}gDjX+(t)t*-+ zHBM0WiMD-0{HfOf zUiD&_0#5=8B-vkrf_?);Y{1g*zdX3$YRvy}%Xw?N!3HgvK$BW0?c8d7ML9iSV7w9(P*5 zSRQIrH1>}m-0k3`yI0L3{o3S-x~hC}(eP_S#nC3p$9u7h!iY55`$ZIDcVN=}`xSvxufa3-ZECZqTT5f#rNl zsP0%9K9u%dFV%I(5vHCRSL>P|_NuDleytMq$K`VKu+$D#$Q8U`Xu9vc?fR3S``j4L zs;NC5o=>elFW7rl6|`~&k7(vrdZZa2dd>ZQfI@k8j7ui`_LJD7()YK0Gx1>RnLH>JUedsrWmnLz1L!#{ln{Em!ThMe@K(c-xv@)DpRCbF7?*2~xKNYywiLJywH| z4Y5vAPr%yhJKGBKj_fRG7pWGuV%$Qx;wiubUo4}7+&=cw!N~o_6 zqc`W4F=vNl)U^MU0w|`e{2YU9sV{o;+p!H_K)Nzdirr%5u)ZxTuu}B?_WvSQ--T1e zalMLT(3$hXab>XjJp`}ZcE$uJ&u8Cc;j$%qHVdIrZwn>dx+(U^#=uz;_W2&7=$V+& zf!U?liaJj_;09HZ1a|u$5i+x5UX+C4GpwkYB+Zi3!zkyP{_X>H`GK6b8tTE#MA>WK z;1fRIXXy`~t;eKIj$EY{(c|Ct(nHoReH>biXk0xnJC$Z&G&7#tu;g!Xu1_Y z;Qi>4%Z0v9JF-hFO%Cj4#GYTj5xZhIVDK_>fl;N7*?CUG)~;QUob#B0jY_5jFn6Cr zk?=uBww3NtOS`Y5e&Qom``Cf2`d#}Uz8T8dpCdaV2 zYH`$C53N;p&0y5o+OW`TD}H8U_ztM>3VEDHff5Na%_4D|ybQ93aqN^`N`4^*8fa z@b(wRqU+7(IkK$)F0&rdz5T=zrD<%;=R`&PyBV)l?Pi_eMyP9yne*i694pIdr&*C6e& zVGk{)cZdE)~IqHYun--;N`6rJT+9u;(%Bq>3(_6;Z_3N1=ht@e($#m->f9 ziw$T)&?*v#4u!hNPcR?KZ9CEhKnfsoATG;2Acpn!3gASFmZ&F!i7kGyowj+8s{y6Y z!LRqbxj`KuTZelZsKVrtTioHKC|mc`6fJ!_US84;OJ)w1TRI#?s-uO0_3MV4T}-SB z6HSpOzFx8KezoM5*}ERGi7g2;){Sfvd}CY|+$liyH7q-e0BgK4deFx^2YO2*bIKgR z8}435sQ8GOvYl~aqP!>uw*|F95`2?G*oIP;SITZCa@~>bUg#sZD>Q5JPMOL3G1Q*u zM*EFG(O%OVg!5HNYMhYZ@(F3;?lndKA_@a*xeCn8 z5|rGJ_8w)>74e{g!W29|5?N0WGx}CLli-s_LjIwe?YqFokZg zd}br{0ooALVT9T){S)EKVrDcv6)xh1g^^T-8ua$$z8)V) zIdFlsFr6!nJ0|#zgQqt=X}aV)$?y$9k3&BMQl0@((R(TFBxT@^F$+-~mcNn#yY0pS zAPQK6qm_i-?Z(wmk>J`vs;EFANTC(+z!5Bh!y4UveMJd~s4&+S_FYuUOR@NUqS3WD;IsdA0CdT39X&*s9@A%6)mJpzeI zC2hflE5)U|v)wVSLQ1zU*r`j5F5)Pft)yTto-=LXb*OoIC`a`z!!y*f?!o@X)=8*1 zJ#2$5{iWUw^(3`uojGQKGs|}S^No*iNn*Wev_aXrsG!p5X)Tg*rl}=w%#9ovy-+hU z6kitd1P>b@a_Kqy?RcC-&!D#DE$m#aCY^Q%$n0Y*QDA5&UO%e0;90w2-Udji8Y>ZO z_hrH3>Nd3->fm?)`2gq74LwQ9Fj@x^R2VA#D zxfm5KnKjblLTj}q)(s9zU8AH9s2F3s?1*`$;Ybbw?#TsHK%NZdkPO^kc;}?8^uQY> z&^f~n-b5VNP^A)4v~ZC?_eJd!Ij59Avjc(*V!vV1u-k@qr?-w03EF4qSAO;on^P8U zvTOGy!g_S&bo0kmGxW2Qliz#yC`<2T?gHCy8GZ50c5nwP*G-r*K@yd_vl|lukR{tEc#1-X{vY2Fla00CZv#A#o z0%9mViWD=)M*>s;fn1t#{&BU z$$R1L!0@M*nWUM#ing<8SCO+HA@pv<5Xja_vB$V_!gKW#7Cf3t`5gB6CY09OH^7iQ zHTPBic-k=_U(zPZt0>sKXeXE}UeD8c`T_A4etnNGf6x}=Jx5x{RZahH3;8|vpVDPQ z9Uw-VS`Ceuy9f>8=8sdk{wM!aDhS{j;HwoqEtFHp_30I^_41kH3eG0|jXQ+UxngN) zJ^=MN22h|u=G#L2?IhMBhfZDSGwX<5h<)AbX2rK}e9(8kI2gWg_e_q{UPGkOUtW$! zcJ+0Nh(ZEqZ|XR#qUJG&so4-cOaz}pV?|mpr06Tn8b7yw|d)q z5%AZ03w`c!T|_zU60u#~3rTV(`UeeGUtBQXb#FJU1bJa}2n4@@8IDe1F1p zqDS!C`!BgaDKBpz1M};msO+BeufHI!v;K$&y_o=0pyz2CN6QIj*>w!!YV5Gc;$kFR zl^N|7Hg3k3&G$!eS=x8*p6!&FtZD2^SN(HdN%u?3oRYeZl)8ZAF1 z=y0ZBw@F?B2oh-!h!^Hv95e+e4SQi=_WXQA<)`#6pFuAUW&S&+hJxwJdmy@84#)?# zx6F?IH1MYl%`*JQhEKYDef@6rW3Y)oeTqK68nSNcRU|W^ku?1LQHd?`*UJK6jOTO% zhvx$z`w#ku%xMI$hMfbxa65b@)NU#p_d|oi+z_KdKfx5+k%fIgUx(?oVR5oZCB@X& zJt8BFtNOxmk4>*8yV|PEOdo+l3QvoDL;&qJE*0-CWEFMRx^+P}0{ja#i%eXdeRlLtLxQ3w}CI)d_jw~BGl^5-xxdv5&rA5kdZb2E@Cmp&usOa=)|+83w+XF12F>i zS$mtzR&d=oV^%lqwS8ko4WFJe9-^C@_4z~`?SeZ1C8v=!NPuanlc`DqOzeLPRl3N^ zGr}4F9l)M_*Aq6dy#2`Lf?LFo#N4dSzFC1SEuYJNHMg~W`b=uWauzx9Of|@*HD*J} z<%vBL0r5*Vn}@#h z*rf(QG|oyKxF{yEX(3R=WCiB88cR)4P}l=41E8i>=td!JkR0YfBD&01PI@7+ptG z5spQjFOu$%XmZ4D1eyLwtEo*k3P=|f=Scjxpe!K2d8?3mC7lM7SD-L2gXo}MjZ6wR zBe{X8Juazm6`@F)|IhXBfXmK|SZOJ60a-gXe5G8Oap6IJPzWA8;`!KHH-3{3IZo^4 zWe|+DsqghIeOjLtHeU(6^GiM(m+)r%ZDZl8w^f0MPhC&5O|y&qEq&|aXssl8UzNh7 z1(!4|Goq~saut9Qzo)k6mq4@~QZj;f`9HP%m7uD0uqWWLc>+4G`W z;@klM>)A4pNX1*Y2z)=xmKQyY7HpKHuNfc(s=kORhT8{GKv1<&`q(#t-}}L3`3dQz zydNRPZh3R(A1;ES7e>PB5H&y53yVyiti9sKJ9~H^@*~y7dU>oHM>iag_GziF_j0d& zQkqb?+7vr47@Eu#sn36T{l~w~DoHL!>}ED6;LhK;Z}P{5c6kDqA(M)ySLt_xkvVzn zBnlq%9qU3tjm9-u&geSM>hqArLQOh*AHRi9eEaTsU%Z%USp<|4C zJ!vlkJxonCD2WUi91AU}IZ7gXf0E{3VSmEY{N6KOsf2 z%^qcMLYiwAs-C_MJ`rc1URyKk9V@o?e}TW&y)+4*wQ1UQ7NF6B|8S()*jPHNSs1z) zrm2n6wmOCR#)S)f-?<6 z+xi*{Ma~>vUMq&3)|%q$TavFb<&F=v(%N;s#xQ|LGW|Z9MYF=19R8ivG!c38`%sYZj%sZRdLJ zl`Km0o=bmxX}6aAJR4FF*X%jYN4no73hG*{@eu`2-Z5t1%@+*cKx(jQ~6xej@RVzUHX?C$~@gi2ws4zXSlcQ$^i#IGkdSjUa`g`la7E$yEO@jRs{*^NOp(}?+@E(^QJb7~awr#ap5c63i z+3HTI&v|y|Sh+{e;VadyWj+>{XHUI;VpQr^_kcRmoENu!b66B*zf^qdKSu*o?i10Z!IeppPxK{yIbc@V;1R4_RC0gr6 zA+NJIyzn)`q0?Uc;mzD$0Exv2KcTg{T0lEncR#qlMdXoRNV(v(yoa^25x_}~!*Zai z9w=(2LWNi5c(LL}po+29gXvoZ=ky+#P_H>1taS0X9BHtE6V~|QzsJNjh96!O#MXwE z9q&8#J_Wx`l4zC7+itKLvJgJm=+iQ3gg$i*(_sk>5~!iJ0KOvw6hvMDWCsg4gm!a@ zF>JwH3Zl!rg*pBf??>*EOi&@Bh;|K4L=?w7b#dO)mA0I(9z2HGVlCfyZ6wUv)R3Zk z4shVrAiOS*M*mp60qn?u1j&K?UP3x(w-qjP{WY#0)@7=L35ysfi)-5?H-&IdNf#$^ z-`PR5Di8y!tJr|Rbv8~Nm*6TX96(G$=~<_AgVYcl*gnM5=*AI{V%J-JkFl)_%isIF z#nq%^s3TP9F4VTIRFxikTf?Om!7PG$U3^c{B7BWoI}F*VqA&BTNQgT)AH~oCD#wE5 z4@x|)-u3*L?a~>$dU;oZ^S4QJLZO9%78{vvqFOV$mUMmCR1ES|vMomFVCHVv)ND4}heSM1mx{Ga(FM zDuBL&Ai(T~Q<0sKn2Ltk#HMV=i|oF;{y%2Xn#qo3?ul>3CPMf^vhc+Jti0sjwo~6Z zMO~N;vTZa+&stPGc}xc9xQEPfXxR!|L=~?cr|f6~n6V`#mqe6&ptBv$icY|!nYTuX zG@W7ka|^E#meac1=IHZxU&rCkpek{@$1svx@#5_`ElI%^1Gb zx+0&ferxFBrJT?G0KfY&IbI9<02rKdlS29xK5^8BEkZ5#Zk4Y|a-n6;_v#q}TC4iho?2{c3ksm6KN;KKf zUNSKB>NuVjLH&i)4r-RdL=sgA<6g$K1Er)zDI~)Q{*EB*pF&V}tDlhOHb9iBQZ;7H z;{(@$by5|{J_P-JNv4Y33}qh~R56NWBqY{9&YSQo;%aq-~CjxL_HbhBYh~CLm^waaRG-R5};sQw&CqGMZXaI6#2DGUG$oP>)A(*|* z`hbDzb#FGXM%9VTA2{l8 z0caDuPg24TC98*EbTO=_5E~(Q>htZ|({JerEydQ!a&({nK9N@$C*p%o-)^*tBr8B@ z;*INqz5X6mb@lpO!ljU=d#1#Z>h-6#Ia~F={ZY#**3#m9q{R$Xsq{XX_VC!_$91xl?rnVwzNaY)5(%R z6>8l#;EZIu2glnG@Ayr&kyfkqz2*!|4&~ln^x?xbxavqwdpT z(PO>6KBy+|BFhs>=V}*zKK+q)#waP$@lR7hHVR%NndQP-hplk)C9v%hf5wHZTB1%v z3-Wu}=?h~K?V3^pNacEb=a7g*mPXF_tt**n~_|v*-lUK{hZH+T5*T11isy&*c;I*n>|3i1 z_sB=t0Z9Syn(x`efo%PswX$E`WfrQYZo-fq{rB!W(bP9u9#bSRP><}Opp+tKKNB=< zy}uu$Dk?1Y(kdz*Fwyd``UOJmlW*hCOxze-(}xL^OrkVop854)Y{!zWlFNRyp^gd@ zh+D>gs!C<#@UR(thO&&C^K^3?Q}~TnVjkp;I2qJb9i%=)aNV?iauketK8} zks}KMjQ=*DwXxl}T;EA)pOZt}M(+N=853{L3n<-x3l#*-TsKfz--|_KUkB>n-)w*T z$AAjK+?OD5rS1J`kHSl{pcG=v@i|SZBzByi-^~m{T z>4uz@&wFDZmzH7^E!(G8__(`VV$k9`8Z$Z0aErhfHvS5pEs0&dvKs1NvwmQ(iA0+{ zrv#2y(RYgKoxsL(@!^|nm-?L&0`yyuHxe*r3(7iQI=C}VwtPuI@>tPhrZH`CRSpFW z*AoHSXlcHzHqmO0Ir4Rm-|X*WdbHqUgI{9={_?nnj-Xwea`!PsxVo%jtwr`sl{~V) zkMURH>1{*i#84fO(*eKqb|psfi*E?#HQvl8S_;-KJZ%945_4e<3fvc0YF*ZNX!v9; zzP7)$F?RK6jSIILgjW*H(UdnA>P&0mx&Q&}5^Z_+N2U8tKQBE*?)*Zzi0!J2qJ;Np zE34>da96lEJXkvF5d5n-*2~9anZ9oG8~l7;dJTQ?w;*CJW(Ep*t`9Z%L6L0RphAkI zf!NeFE>TIzRv9p0^kcdRzu!#;_zX2{ha`IYv}M5FC-r{3?s0wbS@2$4Q&#$c0?y)Z z&ZYPCar#;7$&r5_F-{+S_vQ?tX(cXVq^D)Fk%^mVmGAQRn!jG6mO31#m*{M#qfAT1 zrq+ezu#~z}PU?7lx!FIo<)8cQJK~qMRcr~?MV#~shd@`M0nxhYph;P%*MJOc^v#0E z9iA?^FjDTMS2Me}QR{HwFW>I=hgAxwNG}y6OcD)*i?EL-!c~-jc}{J|27dm)mHV!Y z19gj$zKqreh99ARAhO8KwckZ%y0Yx+yR;VPUFX_(^>BOd$$e-+IpumX#psc<(F=&~ zBWtSeqqrT|eU9rTVSlfz^u}`?6GOk7$-+4Cn>E`-Z*I0jjt}MEIKE|wx(Wl^IYt&| zbom3^NQODFrAcU{<@UDj*~W{K&JKUj{LJEovcA`A5x>@0Icv9F8-GniUG#eJ)9Ka2 z>?cp|K4B3(AC!z%MiUa@Xa6ZbpwmG-G%H`c7S6qmi31v*#_0SNQlcf*AJdOn|4*s( zQov1$ijGRtl>YjC41^3nC?xdAwcsQB2UgsnmxS21IP$#iohGqO(`e42-{*_X2vw6) z!NobEziS=%zc}0dOo!Z_0W~6F)(IUqX70s9csV<p zjbzl}^`J)hfclF?dv7uVYbv(sdLy+SONjn$#B~g`brd}o^daD#SIAwhUQgm?N0zq?(@g*R20$jzH;76=R$`Fw}-}lM>vfy#lQ3Y_ZZ=h@KdfCvF-d3 zMkReMrjhbNW@|KSV5}!>KA|xZg7JS2DnmFg1PBS-{2ZB|)J*0s+4@>$CISKTi|o3H zkY_Hl6a`Qq#*(;~bN0|2a-bjwq;jPe<;;cw*w1e3J#B+4xJbx>b;2^X9%n410qMq^Bk5|7iiUAcj2^v(T-I!+hMw2E`tr zJNIu#XdQpd=gtcbGYzAU^4na7tU|POpmjw}J)T?#ri$z&Y!B+3u)j%~X@vmrn5?4A z4sI$3v!fLEw|XUoG+@Vn_$X*#L-HF$=K5X{G}BT7*oL|_acJke_f4|S!kv*;;$eL8 z+R9+X2dlBMX1d%a5!$*87Ym|I1;1S*=Je zVP{YgcE=zJ-~%_AjL9t7nafgm23g(F{7tX|G)xYKjq^@74IP!4u+6vC5<2xghTVmW zkINB(?R0DALeczpp|lsil^m!s4k+jXyCZh=aLAd^?dWYNQ=vv4$}{Xuh)-=(7AgQ> z=v(V${4Zxu$-wcLd~W-Cf<+`jrff zX%xf4;fqwqQjllfIVedx2%a9Ci#WhMA8yIZj>Z`V!c z2rxH3dMb@p`k0|lrp_@^i{VQO%ohUKZ`VX=nR9n49wdAs$-pY;+ofu^ss(>SbP|y+_Z1B_2E%0 z{agiZe)#OYe+P8R(@#cll&BA49{-kqhh6+n$z1$ea{%qgY3w?n=cU_sjaFf*={u4u z4aGpqzIIy)SFq2-mzv$jk#oMANx;Pb zg)6iNa&iXNy&a{ee%u8dg|(ml)hs!A%*j@onjcw6*&judMP0|YKaH5*j>!9859HzG z84HnW_qGHi59gn-6CRiwDmB-stE&PfMz_Jq)YCtYUHsX1FB?DlL5nOAt~w8j$9L-O zstD3C%y|SPSVT@@EwFDNU;p*m7loKkta2<(&HZ@L$$n)fp}>NHP2X`!Z#wtW7emKG zI#m609UWh7mGO?^*Bc6;>yih7wADAqoY4s5DtoU98_~+Y}w-l0*Jb zRzR+Q`jpUWnkc9u|3dvK-L215`Uis3#soXcVGBBUS8L&5+lGE<5aHq9dMO+9)Jl1F#?>|OUH>Uf z|0Jp8QlP?G1vr`l;)7H=cqtYQ$&V5fx4XMX>cRtm5=>@mY5fcESd62GiQMapY2D{t z&z{ldp1bh}uf+{fM^*|Tsg4_H`PcJ32Ue-qFC_$)uZtLt80_J#eA>i%j2uwGEhjAv zm-C>wa4=ZpeenM%I`?>{{y&Z@YF#OEzpSEELdxB2l_JDKk?Sf-2)W;ONs{E2`!}&w zF3WYvE!Vj(_04i!h>a;Rmkr~v%lCJF|Lu=GwzIR(`F!4&*ZcXZYIgRRpKygw+D|yv zj*o8_rXoZEp2}ws1ihYx5rNMgO(VJqb#J9;%ws3PW$W@20VR@hrTXg=uM;v2n!v$?hY$%@vN(?^U|pX3GIR4GmzO-pH!7c)ENE)= zY^@ykJFZYvw0#@pPE0qY+wL&E*uM3G3#QH+werJcuNy@NgyV=krjK=| zsV_cLu(<7?m4vY!)jVUqvND8!Ou>6nC0Eu>Md$NQ8O!Q%2gHBc($|)kKxNo z;;i{Jud8@ID^4cLuwwRoj;`*$%L?w{ovAIDS<+N;bB({&tZr+cGBZg_v*0-h`%@%g zWVx2Zcb0ef*|-QXiZ@~#C#C;nm%HBf4ZqMk=B&A*c@%&0K!$nho)0JfPdf8H*$>y} z>mj=S6H(R6Rg>I(Uf7kqW4wI9h0+-lQ(9REeE}D(Zi@XkWZVOo=o*bUWL!{@F@Y?z zIBQ#cP2;$#czm$VP1)OkF+F6)yDN?^w4m=l41z<#o?PI<@|l%d*Q%VY^0_0;<+@t| z1-XQ(R*A#R8q06l-tr2TV-wtf4a~Z(#F7Qi_yCjX-4!5!S>KDI@+Jk3 zV+Kh}W;|m?H895hxqH^Tdlbfnp#_(gQs1WPWa7gD^Qn@_kE8I{2iM-39d0>;w?&EwBV?B?Nz4Yhb2-~kKK+~T^6QyK^z^Gh< zXD93z!}SFK{&Bw|2}tJ01z~qwyz==yuwW3Igo4D*%^fdA8A#0>GHA!7MCEPV)4a$z_&2;l=d zP+kkh-*_u_`8b4 z{8`OOKd#F>eX=&hJ|CS7;hNNGu zs_t||eWmgL9h|WJ&efj)i@*gJjsCx=QX%~09JjfN@JPm-l2_;l%90b$i1Ghr7&{>} zH?eK)m{a|MiE*?X!5#`lNMe=e)MCNHD?Q$lHB@lt&|azsgUk4#hj=E>oI!L^DU>#8 zYGcP}lt5%%clciA^3Cx2B{+?iO~=r~FdqL10*!+$y`7b7Bet{sb&)9GrYoqAI~hn^ zvf&vv@#%_fidBzsvtBh&w^@c1jl)VkvxuvuFaB9TlT%RQ55C-y(Uu&yJf>RprBGX= zL2loRe_eOn#QVLrK`|CWL2Vz~Gc!#EaNW#cM^m=j&R)%Tp5M*f&kF{9P#+OrLO!7z=Q26QZZ}0_YJ1dnwh4^YJyEEK3}^$ z80KfKY=_pE4isN-Sw~GcjfQ^&l0ZU^@tI#fO)%Of&SvY_pCZIu+=- z>!Uv!&bfM`+2XvLd8Qf6{%ke!$de;E@;5%F%Ljiuo@2h_#aW${!lSS!CaGx_RUyQ2bMREibaI^3KeeCA`W<*N%0 zH5p!pt%W;f{Wds_$bdsq$hDNE&*6BqJI{D$Bn2T7`ZhRnzMJ8lu7e8?LKM|QX(?t0`1n0^_b@iV-uh~NfqaU+a z$ze*#f}mdGO%m8Ms|5+d2JH}`w075p3G&m>10kul5TiNK1;61VbAOom2P7^>&ZX1Z zOtA@huC1Eo>)>GDH!43Z@td0mSa_m=yl!_RRc}nFleMeL$PhCI|i>q6GF9EwTcy7Lo&9`E8eZLggB;KmI-w<~1j`@}I~kS6JJ+wIYn< zr{U%=l1Xb4U}j^5ye@>!&C@u4Pkm92{7(cSY-`JUHXB4GfseV7@-}0g``yHUPJnK^ z`#^w@APXhzELGJWUu2|$*O|*Dodd+dR?|k-moH$kzqvSQ87slp>YFoN#kB7I9{phX zr_x&7x;n}1hZ09%@eg{Bx=;qXr2eE(s5O;w-d2L2PK}S;*z7mO1l?>pSyhlT71Zq5 zI+YVXIu&7bGOz#q34d&Z`^wOQVF7%zt!eY)%J|#3626FX5bw5-`ja9^ZoovU;x9tY zQZ<$_2WF&b@@fpc;ra@=Mbgtz2e89$3k(>0+M%2rsL1~l1G5uip$Z>}Tp{<;NnG3RadH^FGeYE2qX!S7J>3{l_+2w80Zucob9myFMC6ZTjvX zx>WwRIqEMxZWgiY(;iXPcl#+uOx0EXE;OX#ME=?+vPSqorNLr${LBNO4s#ofW)CtaQQtc*eU%^BDref*H;<3Q?;(Ad ze%@A-cNr2=ZMh#V{?^hJSh1}0zIF;HbH$MN!-AxT&x+gMb|}#p1Wljj;Ro4& z;=l=;v8%+tc=>_#Nr=U#2KQgCr5g#jP*8xRl~wq^pdC*n!5VL;u0k5&TMV=^)Ber z*}RYqIdsF=ntStVjBg*xvCOjkWaT_ckdudu9&u}!-{;man%Zv5DwbFNRU^DNPYT=!So2N+`!WL zA&;fbiDGW~wTSDnP2e_$P!Jvx{y>PYBa%=%g{pwV47Cd?WnI=Wy`l`r-b``cb~or%7SuTvDiX14pao z-DFR6gA7NFKtMF0dJ#!@LyoZVE&n7a+IsG{IdupC%RXXe0 zd~3mb_--_YqX|O76$8aVZ7XQ@w%)3J)iq7^BBwvQD6-wRsfU_ye^BpWz}MatyMc*s zImV$ndW9};@=t%Jq#LfS8FpH(8KJDlzsj`rZwsNx37Qw`efsLzkc~ z)h#27e063e+{UQ`dvv(=Hqp2~7jz>-K8>MDc=(3vr~CeJoiu%8v*A1noo3C*rhwkL zM&2X`rn13j0I>HCd|#!Vtjb4Wxj; zc1bft693ryzbKy?z=s4Rbh|9&kIo3LlIth=XE@8vJW!wEP7+FkMkuv`;3xWml~i1Z z4%w4BuFeoodxyr+&_*-bi7FWj*OqWUS zVVQpQf>1>5fZx%D3$b5JqeHm87F#{$Uz}2(aK%)NHn(xMElx}){^9*hka;m-0Ln&H zd7)?l77DLq1W z_58bI!(le!CcG!K;9sCTwx;GwQN2!FuW59QP}y)!wK)18v{_Z5o@}4yy~);?lD1UJ9$PYxTHn=hJ`i zV!yCb6O?1A9_J7eN?={}{W$Q3k=E0y!Zj-*i}T&1{Eu$kuO12Ym)vyy5f)TIScACdi-EUl_O9oYRtJOVThoOu;O?~EpU zPOSE86dn+yY8j(OfKs$^D%Lo6ErzQ5Ya${~j@~cE$`AGhJ`lqO77lR49KB#_glk1%4dGm31 zbO>M_cH?n}1|FFNHwuR(i*2P?>6Ctv=e)e*8ebsz`Piu6NXA*lW`e) zj9l>N%^|4kbDJ-sSo8BNdfv!K!`P6g;MNl|^i%&=OlrP_LrVEIsi5B5hhv9;A$7u| z?d>&Gu5+W+cx%L}ADQbpTdm`p^~S}#z+&L<@U0;-zLnRS_7dS`zMp|g@+WLHON`g@ z$s4YGcdG5hl~@}6Wb0awv42i$iQD|<$F30nut3wgZYhe=$6sbJ{{ugeVr3lb1JydI zw~HJtJ-t(gOONttyc2GHpkzS1$1QdZ3l=u&q5`5lnS`WXanp{apKIgIxFq;lj&cDd z4t(xGyrW4kU8;^pmY7%Ia?5=6JtPO#Qxw1nBP~wWxL5M4gXbqQ!L!a7pWNz3}mvNq4o(0IT_Elc=#N$_#KFb4JPJd4Uyn5Z;+>IPfyL*rnLn zJlj~u9$j_I+i>V*exZtwUkNc+eQ_q|@O}^^MVd)?7WzoV$3l#62k;polwLxjw^%wTkw_#fZD;0h z_WKQ2;_k1+%i39}C%)x0r0JnWqezk8W+J*x%^ZfcjOdU3AHl3kB4e&Q@BP&j9{c{` zw&_O=>6`+eeP{jNBJQumzC72qr|AYLQA%gd7{a$idzf(M>XQ5ZLm5GHfz7^77?(k7 z8~u9ftp~vlUvzHSe^|X})8Oms6cjnOk}+NM;3w7OlhHYmcpUzvznS zfu>oAaX>i_Vdk2;dWm=?)$>cA(a|vJ`&dyR9?T@-i@^l%9QHil0{+?1xLT!%Gm$^j zDo=iZiY>dGCibf=nT-zN$TxG6ftGbK@sOdnO8byJU9?312-_Vjdch5VLf~n*|XZL9Rio+;8WCbJMh$Q~eku{{$nQ>iJ`5P%IKPhGWFSBK3ch!P;YY*v4#{T>c%; zl3<H;Rl&kGl7YFu2ioy!FqyIwJM15bY)0g-7JDq^BnD_y^Cx9Sg0jxoC!5{X&A zK}b3pyayTIIj5sQ6!GoeYkzmhf9a2cm}@@~swN)*7wlScyc*vd$fPQ*k8o&JhDzLD zA@fT-9lkhBLd^COlc0n)mzxvzg8UoYQR5KkQ+10y&CeyAcsW4RO~W z>PHzN!=y(A*-K*@b)+*a^#Rwy1+JEn%9-qk#hGM{Podj33EcM!bT)e%wbEKdGY;#+&BU+C3LAzRHdL7W+)R^q%*S@HUWMRY$TjNigOAbDLCH!`z(%MlX}EBW^Ze4{#SUe0 z}Mm@4}x-GjHIY&q1bEY&qKHoG2+bq5Q3#5K3;3P24YRd#?Tj zyC*WRE1DOXw-i-Ty;_Le<4rJRMw<}*2_k^RO_|4yA_CnIWvnd;qXA`2?Vkz$eI zt}%ocba5`}Fvz-!J}Z>SSy1%%?tx2Yc5j!|lmV{YZIX5Qtfl+KzB-XH-Fc3LG9QC+ zgN@3mHx={$iP*YQ)aGA(ZC~BPD-!KG&5(vEca>73du{adB5XGXLd-`qn_^t%F_q9# zde#6jz7(}XNg5`jmG+6`n62=#*tnL}XfFNE`SP2LtofU98@2E3Y{l6%$%KDVVkaDU zc39dL^&5_7A|jXn3M)ltM>3xq&BYC6Qm4kFb8b=I%_lQ`Iemt6@~okG)E>U%M?7*o zVWZ>rprynF!rQ6a?zOywQ2N%^uZc^?fhm@C>D{6oGHEDj{A1*@y_h>W@aTe_ zsE?A@;h*{JDCUs*4<_j?U~uy?y#aco4B>Upgzyjl@ny``xM|w;P*P!=9zT>#Xa`!L zWdP5Hwg{7+AxK(GV?1XxTeB-Ead3qxdZ&48Dc`EupxRdjY_PYMZF#69^dy!{{Z-Jf z4!bLx7+7m<@>BcGpC$KXdV}bTR5@&){Czd0IcC5mIi<8*Bgg^yYwa^_gy){>C@Hv; zoVBQPsh(Hm8K0?zuQXRpqwLAo+gO_jmloFso~CzrK}Jw&B|OFX#8A~&`XzVWOID4J z_IvI=G1pIz%V+;Qn(Upg5{(|3A&)UT@v5BN?`A3?LP;nDc;k3jtC9qW_OzX0%am4X ze%j;T-x@T1(8)5bTf%Q&h5jej4U`Rdw#oFTOK7!{dqIv4$TzGD`j5#sN6>goA*Sn-j$aKB#AP)959h0DnTCMs<^A(2ax0$%n{ zIHMBAol?+F(MX4QS^2NM|S>NvB4o`x3ysY22Ws@(g zAM5I(BAHJO2++$2jM( zhQ=JxxBZv+pY`Y)IL==6z#c@J$e~MAu#`%C_odzVFY1pm_lxuZbS!~kEeVMYpdRJ7 z83Cy~lt1F8wM-$fR8x^2Q^ftWSWz`#-y-<)q{xX7_P5B->B+MU(LOFC@?Rye0UEE= z@n{S8F?e)x%Y!~4i_vop9QF-L<9CnRa{M-RCx)_{$4huz%c1YauzsQRsKU3Jq>9RMqCoAMQIFT! zS%a~EFUUS<`0|OaiJSE=NqX-R_4BpkDBYzZ*ZRlPiOl;;F0R%w4PjLYhj8A=Lm_F2 zbQRaQr*J*~HAlvsP@2WJ;h=oH&X=+ko>AlrPy-=r#{8k4mdo_+ZrO=64eN~T7snk4 za^K`%P;a+jM2fGeeg?0wdY#4G8IqqLt}1U>YOz?umP^ZRcS^QOo}&;Xuc>iCRm56G z(2qN57o+!|f18m>d9YW*0G*%};{uMO$73+Y{C|wU>bIR~uB+QPBwUEO={UIUiy!B% z&I$5c%oEaHqExE!mY}Bm>^3OB4E#+sZN|^0%knR*Bu?LPnrWaswz$ss1+qXHVo231 zBx>3;h$7Eb7i6{V$Tkr|-MmuC>;kkoL@C39f2)xS$)u5nR07AW89SFLPpV=LfXgb3 z15NgB;GgJ<{l%fED*L_9@qYFpBa+`iJ{xy7V$8*w=f29FnzX+CY!Kz#?d(GR*`fZP z2ITopEst}(m7(o)$x=n(8Gd+3eh7y&b2(XM_8|J)`ccpM`C{4j@0jdM0DYe8-`6$I<9Qsht)iqRRU4|F2&gDe}EnLd6J3 zp)kSb2+mDZ5YmGhJ$Jsfdh6Mv%!UmaXIc(@&A@8ZXE}%U`S%&5y3rlyO+DO~j79?NOC86s254L5=aKNEYN1M=YN?c$S=etbGUcX)z@JSpSUTi? zcKK~<{a~9a-;6^|4c)Wie_zVgo%|e)B+-H3nT7HD&i-0jp{2j;K3~b3e4l=+Hy`?G zrTy7a(i1h-FhHL%IvN}el?4&K`9bNfsvCjqT`9FRl!TsyBKizR*Dg_)Bo%2*nbK`@ zHK|j4pLvs=n^-3Z>1aFi@qp}7w9|`FlW%@6_EQwcf59Rihfrm$GFz19^Igp6hS&rmG_+TR^rQ zcsn%h`tZh?_l!H9KRl}qP1iEhhci(Zus?!oagt|V-}sqgM69(^}l=LxnE@nv0l ziLeV6R1WYuf~laPnlXiC5+i2w<7Tz~W( z@kv|rH~v98d(|2W^h@OhIjrqVF)l3< zo{o00dAK}2=!$YG|MYV!QGbSOL(ld*p>ZgCZ(BPcAde?0^^%+8E*E*zk z+i1T3yeOIoSmo!fU;T%&=EVdxIpR7?6fkQ%;MBiO=|SxR6BQH#1qBrC^ER&zx1Hu2 z)-~3UYY0{x$aAhhoic8N2kRm-e`V)oil@o4fSQOfT*DV|$BG+|Wc%&E`IC1@T>&IE7Ugn8!ukRHs@scl$ z%zH1PHtU?g0Q=#;&?77lnSn*n4%pw-df;qwxk%H9w&voysNayU4T&WW`K;F$;qEP$ zEsMIuF1Np29fKj+IgDI)8?^($YN!Li%PU~XMe>`<)4zDdoEcg#@Q8 z#RQKdGqT;xkDWBR`w<=2@LH`UXyD zyc1i)PJ`gEPFTOnRh8H` zte+cmL!=O^fcq55X77C2qqA##^$*GbpQZPLsEB1L)qzTYloQP4pBPm-g%@(=P27xJzr!!0x2xKeP}dc$|x5ik)F0_5#rH zb5fa4K_c$O$idmB6-w(p5A66LD9RGn~)lcFe6ENrDwVPj6xy)SECH zSMnJ=l{%F3JMyPqy0BN5+>G1Gsa#s+=Z*v^``($X1cfXPNr96CbxC#Wf4AxH8~$b9 z29RLCK}sArc(&k)+qZ zWL&;B#Y}~V&kE1th|C|P*QeB?QeuT;>Ud*sd2lm&g892WBHMa4P^ffTeIwBC{)B1x zGHG^7@o@RZXQu{!C(_!GZt*gDr4WNb=Jr>!^Z$Plr))1lS2chagvYpl&MM?c&QxUQq0g-S2fZpi))<>~d21Lmo zaiw#H5LKhD+j$CDmSQpKQ769HV})nZy0k#TICykPeJgfTSZ*t|UmgVmi4k&hn{eg3 znHz8;*tS$ACVe{XutX4ev&^^@L(qEGMmPmH4EugOFpK+AJ)=F%A^;iUammr^U;5!W zl3$|MP)9{mR`6INz7fvP53+upc5If*TS|@OQdYTCD|q$u03lV8gj`ba(QH6!(e7i` zkt5!VI__ug8o1t7$9-B7t~WHqko3paT!7r^^GXFC6UjSq(~30C(A)l`OnfBu(Ji3G zlAY4FQTKo{3Stf1Yg@wV9m|9kk3 zrMX(XZr=VECoJE7etGpkMu+bG!aVsS38`LZm2odh#ZqF9*;oA@h})@D?dWLN=?4-ow&Hzo+x5GvyR=6#!cJjbFCh$Ow-+ulbg@GZgwA^R#sIWQVcxT)StA-^&$EO zo_t-pUrt){?Qrq%^6?b-&x?NwkkQa-$C~Z)#k`g(O_W? zjOHcFt6syqwhs@s5<2-~&a0R79a}(=PAxhN_Eci6{}Lqw?fask6P4%=>Q47Ky=d<@ z4=J7$x3GPd7DN}D`-wJC)U5pH)Lx$SM|HLJhKnXxRa4vOdo7EjZd(tasIBDga5DQ` z0Td%iwpMM{Dq+69S6qpgIxtFKTW!djG(p%jDMuoXdlOoRhG5>MT#<uaiu@T>c zLV_-wy>V5t^g)DI^1<#5jae_!3AvKRsYH#jx*ohNY_aP9ZWu z!@#(pX~Y5F=+rkwIA5tU-&{wmV(oD5jhBO`W{3P&MltuSaqHoMwH`i>hiy~S9t9lN zI9BdALy$2)gzG%GqjQ$oYaBW2sZ^h31S3#2lAVgplSfW@3=EEDy>Fx4Zai5~usvZ$ zE#Lb!z%I#mzETgns6W&MHkt6ms*bwEM~mw5R2bvH0nv<~uvfd!?;gr)J^ZU^^+LX6 zQNYpsbbRaDUlC;;4?Acw|7L%`n*c%@lO-(n!lSzT21#4dz%`8g+Tac{wJTJ*q_@% zC1$>7hRzV_x7F;^pR=)FIv~fYGn1Kr+K)1xUU(Apk7=qc!q!*p!@ zG^78Z$*n_&^sI|PjFNK)Mw~h+j#X9fm z2A-gosWX~Ke|_pv-LLVY?wbGa?BiLavE^*fbjE!(kBsqo{md=jhP zwScv0l?2{#;TU;O0_edT+0YJ7`U+3*K`#t8>k5Ekhfwh1?0@kZ!)R%aVt1PyK%AKn zK)Vg1A3xR9-*2jDUzfMMI(WLO@_oUB!0%*`64-?pkD2yfDrJ{cwy55dYiKETOBSFIf{k3+7x~oM-QosdIYa)L*(1~#1?8p=x4~(D?TWCzO1M&= z|9hV{xt8J3))1?q%Cl9w{Cw#U#3S-^R1JwQJ4l~Kw4+{vj;1T)4IAT}A0;aw?qdR{ zY3(rx5zsoIl#4u!=vjFI-Ce68p%9r~SLHF)Vt> z!0F{U&YU^$p9p7S2HL$c1|IDM9s)Z`m%7D80yb?@>K*P5U{B>$RT9w$FY!aD%0CSd zD}42H6>QUsoqe%qt3TI|R%yOKun%tTD{uO`C^{>q$P75z(wSPi@9{4V(5> zmVAQ+g0o4KN1y7t!^SB=)p{Us3fVyb+*A~ST|6_iB;Y@@A9;{PKFCLM8qC;E30^>Y z5o<3df(X#3v#C#8qL>LWIC1)X(x0h?!mF({!}wlnlbFx9zY;2-2g1J5r6LiwEhFQq zRnFf&JoGqv?l?t?6@74h+xA_Q*iq~snqA%y&-UxtLpe_M3zofps|~`vn(P${VlTMH zUqh=QU5G@X1nvJmupQ7H=nHSb*(12dVAVQ8D$?7zp>^aw!E$sx*~G z7v%oDFfMC{Y@!WIw8In>}@QyGOReCA{rc5HXf~iJn_w* zljJ+vVEppa@0u}>@le*g-wazD@Jas;IMj%kPI2`{|Dw7Zh3- zMP~H&uj(2%#OoDl}gNOT^>EN6rN?(hd;q{u8+v zbJ0Wz^puUc*CxTHEG96mQR81%HlB`v@b#&#PTYYOXeA)p`4Mdovap7op$Of(h7zho=%>{ z4XgYM7Q#|>(rF(^#IGNJ2)~z~8uk}>@0-OBWWMv*tQU+l!QB<$f#Of$I(k*5y(6KbsNgS zn&aMbh4aU)=AnV*P8HGXqocXG`{hoIE5l()NOs)m`^SfVr78pc@bX#AAY3Zm*bz80y6EkkFts1J${a%v~{v$on z?DRBzsy|C+(m!UoYLrDByd#Mh&&e6Jd017c&wIGO6ngJG;$pJV+;39}RdqS;w&znF zoBby*?pY8&ZHhp+O5aLmh2NF6zqW6$m4L^>uIa!nP9tdU(gsQ! z`?z0;wx^4VNt;gDZ4(pj(NP+OYEcj9q*vbeRvGV$8jR~N84mY{u6m?yBqOW zvjVr;#RnCVbMbAH0_mpE;MUDW8T_a_H*+=cei@lnb9T?{a?ydVp2Oy{Xh%2z=c#tSdZ zd%|Us=bugLbA!kHzUE%7%D@B;dCw+%gO?fm`{)HCBZ$o6ZI@Q24y{F19%{T0UGe*z zbjv?{)6y=rH<2ciG35`PzwA(X_$vSF`(hxL09?^~vIpW%wEMXrlP`WQlIjt$BVQ%2 z3H5y4UBRhwsKQ?Hwdbl=bB^zRyh+ z1$N#0@kkcxS)X^2K|&y2zUp?=<|otH=P|FCzDcMxR$3em3md(8cBGLM<7%_*v5gTN*>R(^V-j!i=#?d;aT5}^lE?lD z*8rd>AZ=zP-q9s~l90yVm#0-(9_i;YRJHzQ$*e27F}4EcK24}Me+3XaZ6b&MZn`X| zoR};rsB-z`qmbm{@#1}Mf5Kvh!Ue*3#766#OIze=&@IArU*vsceGn-4Ic~#Kj9jx5 zRHFBCRBr;syvsdo-Uen^y_wzL%z?}-5tBeyGL$<9o>@|0eR)84(!c_=h~b~CMQTwf zhx|4Tj0vfAu6uNO2i2{IbjS3*(f{cj?CJ-NEco@r(^n^Q)@*`%Dx7iK>^}Bzr1Jf0{W2A-Y~3rhM(<1zyTiXDD0-tTuDFp16qz%2n|a|s z&wZG`*>=Khm!PB#QmTwqK*!vToeyE7(|dO)CoF`##4VaP3J7l>Ku!4Wf+D$Ow!#0k zrbk5N*G0jNsrjeZEokBg|!XSgbuN`zCPu;y$^OUSDtfvqHOPprjInOsSVHc|25oH}{jn-405p<8`c{ zM%!yo5Fd<=-oY5JIXA`m%8YvyM%*QSD@Wa)Z`gD+zH))$uU{VH=dHVu=OrsuZWgHZ zE=a+}0#^2;b1f=E<2VB>@IXJJ_(uYT`!qRV69e?LQ=}>hssD*6h1n=}kZT>Qc~;V{bXJ@L0~x+&J5}cJh?P!9MDe`j^>-(}t%W z&XB$^zZ>jXGc2Dh!r7N42h}Qx7wmvf8V6<4t1!i@6Uq`ErpR?JGj(=-CwX@pc$YhT zU=C&2c90p4^8=|f)x@QPRgqmYX$kNP0MliV>Fk=%{4Q0W6=e z>8&L~mt;qkMzhtK@9r8Vrdo|;4VItUXihc#08xHL_{XVa;k4i0lYfVA{wefvK^A=I z(P-4&3iLU_d#S4C4M8k#ht6??ZR^FZ$|6iSu+2;dM;M29;f$fRP`+#o^pim^K@q(l zkOduRWS5MH51};@FWL?QQX6?gXdS_u8HspM^q*UKq9|dkQNSk_9viuqx8kYdJczb*h_2ML6G#>*-y}oEoLH;yw^<*xS zJZ6iG!z&8j*W%>Bczi}bom~ie1Pax$-@yR6u-tmuL_!Q8yEXVB5BoJ3D^LEe%c{Rr z7kJ3mv#Uuo%_M-QIr+wSC8F7dSead)-Sq2Ig>O=3u@61th)T9E%i^}Pq2&pWH#YrR zR`xztG}MpBTrPC(<(-wB*>%wm{BuS|>))}L`Io_LsTF`u9|3z&ge1Zz^(8VJ41>>n z1U#cSiWK@GxFitKg_j2)L7X=f*|yz4(0l&%j7`erO-GK(yO2O43xDDVTy8uPqz1EX z6G-!?XIx{Dhw6lJciN=E5C0cSg&z|XO>Be>V-E=jr=;R>epCfvXBRUq5w>^wVVf9q z+`H31SeKeW>=_SC)l+`t0SqeMjhSvlOv`xJe`+3D=9}$K+cjJn0FNOSw@w7o(k*Nz zmzSG#xjsG=rQLV6qwbzrU2QB4aR{n)`4i|^JMdwAH7Utb9enUT_PU86??ppt5Dsju zWA}^uJ0r~((~PxRd%26|k?Z^v930GYEl9wbhD6*$2~li6R21Qhk!4~wmqagLA`Y^$r^F z96-2;HJIoBy=-!H=<>z6{v=xdXK;Wv2+w?V0qHua@u8>!&u`6{*9Cdym6Z>IJ=vdI z&X(C{d48Ac?>}#Q-irMv8PCc)kJ{(cQ&Q{m7+1|oM0jh*B@^Qq{-^uYOXjNx$x@WJ zatr5zL!80&`Mj$f`{m**5kU`}A^A@X`h;CYn%XT#^4MpC2ZZaU2!sI*ySTl2do}4JSkPRIVyZXT8haxW zq*uY|g%aQ$C!mWlDfqswvSZ-?4x;;|W>nq~r`&u7fq#9+*M;)zz_>6)*6BN~X`R6~ zMF7E>W13Rj)9gpI9jHy3%e~Sx8Y(bbFgdMo?N$BEzSC6VSEB_JM$pr}bC8#8)Pad! zdL6VoXHJ!Y?Cj{@qbiiq?x~2SJ-z8ECl7UbQ73PJxmQ8(zn58Gwy?ydxw@;}Ef`4E zGCW|C{eh{o{$b`cDA0|vEilghZ8KK&&}JZ6eRuwS@rQkeeFgY8Y$pacrB)_~u%}JV zWxW06t4o1wa1*yPliZPgZ6?ifOyPt4zOq6!CP_h)9XEXn+)v|?>>sKBz<02TLv5K< z2!7J|K-`)E|40qkvF_F$Lu^0<u}t{ng%Oe>V~Z zm4%ff>BE{fb-z3R*)=ToWuP&9&n57v%)~M3p_1!w$m8F7=ymGdpj8B-u3@o{r;F_) zh=Kfms3i6Wxmva>#nm!!XC!Zin7qCMnZ))BPYs4qU*yrB@h>sIovKr;#CU$>=qJGo ziUDuOTnDeVoViN_(!Vv}J_i1errH8)nj*quqE-K%YZO6Cc(>^JrZ`T`_v>lONxrD5+SVV_DIf^g@|C3eEJeh}#4Q$GD^ z)i@H;`BtSUKSfnDc}`B*WYur2c_x7`qYBzI+RWh6;CpDg+9}4HsaP>!ZsR6!TPVSf zc&h8~?FLkH95(vdDVr(S-C)VcOsP$CXeo?z$hS!=yG*||Xog1;{p+IKPY60Rs@VP-f@6v$$?N1@r1pw5Vpa_HmH?Bfttivp3cK)T=#ds8-g_S$?b;Z zT5TC1t!E1U1MS4l>?Me==%#qs>^EoECoqxw&^Pc4Q@zZdCcXTR3+KLtuug{#^!;7A zk29I~U#@reHPYOxdCo8t->}`{;(y6f)01Dl6p%7u#+?@)MxRuD2spDRFD}FOPv1Zq zDdD`J-ORNzd$cNGdpixd1@^(SB!eYlVP|ZOJ%yK4)x|ie`5aUhP8`8#rGH%MR+az( zd1$k@*a4JSivfFR!(|ZnXcFNLOt7CKAt16+fUmm%I=Mas$vbr};)!P~H zL+DfL2zCWlF=$9dluf)Yj+;F^JU2v!*%O}O_CZ9a)4Co98w0YPubCtp)xV}uhD&MR z=r3?=7m2Z<_GpVvzh}S+-hF@qmMf36DT5v`T_5kBo=hr-Msy1_I^h~aVTz|0TP~<4g<)L!JKX~>4{g$@yB~foNEExhN%U8`Zzm`%jxmU6Xqu?oMmb?n4tesX^0x{tf7kk0 zwfs8WA6oih&D_qRHS3MfO6ZE8Zym0j!&e3D5r1Cu4dlMfB z(7kO(GA^*2r#q-24CFp<_%r3Bo3NPJ*j^+9wMYV^kT*1#5rZ%uJhbtCgm1vRy+GK5 zctlEoR&Op2fUI~00Gk_)T54p{UkVSma2yjrqvW}kKYA@>tfA2hgV@QnFN@Mv+DUhWS&{Ll zCr|erJ&n&iIs3DiYF1$xe!ZXvS%Cyf9Mt1FTbC1MU+f~tx8AjTq)M(@pVBfeNZc_E z`$E-hsN<8_>2Z%?J1LXCDM|WDpZ1zQ?7p-@hfK{|{*~_Ll0N&vTTalmmHRdcZduR} zPROP3G%5h(`%xcZE)AjxlTmUj=DL*RNqtbt5QH2PXeL{x6I5mCN5VjIc!EHim?LoO@!omCoq7Pyoyq~gk3*p z(|Ae9va5JW`qj|rV!M02*|x$fD`j_aj;pNWNe=C34x^$+gX2?XG8<4dk;7WNL6v7} zeR~b(IfJ)D;{jCsXhC9nQMi4~w!RmoPsAqB^$#CO`b38>bH|Lue;R1*`FMMKrj4Hx z?}XuD!`HIf;avT`FMgq))hO^aZnH^kexNp=MN)W2@yT}5)V!b*Ff)m%)?7RTEDPVh zg+|MW=5N3UQONgw98E#w!XO6cJ-88uTaki>O;kJv}S@(l@w*i(@h$)@>bEJ3DmsqNDJFI(lkJZcX3s^?P z%Z2aVW!0Q+H@_^CRGn@fy&bU;JBhe|2cL*iuY{Ft-SBsJk(R-S?_|QS6M*DDnMXBv zWyr%7vw~ORKym+kZt^H5(3}Sb#!oPZDpnM)>gtBLt(H}I%mrO~w+Yy~DKO*1jBG){ zvo-zs8C2SyE;xv}|LL1?uyRH1@$qYLFh&Ua&D;t7Bbzd2F-JLD4$wb!rafEiJlpv< zY`qh8b_ToVdvkm-+IIFWW2t7oL~!l1@Nm=U=FMG4(+(=7T88VKDT{9%etN|qJv`vZ ztDNe*@`}>mWjrF9jHQg9Fh~`k#y))G!9)*@y+UJx;FJGQyxb9wZ1!wuRXl~h+Ms0o?6CaO*J9+~>FcW7UCvC^SKX zF_F=Fa(21s@YsCyTf85~l%fckYhz-$Ti*Rad4N%P2U8(ltROK%Q4y;vzS9m{r}Xru zv}WwLX)PSlvYf$eu2f+vh;`50r#zr4yq__Y{u3iV|Ko6{=kdeZt6M|ddfJ-^AG<6< zAKPSN)lbNT<%M=2X!b{73wJ1@nYoPmAYR+}`whYQpLUG^Mqawbm4XtS=a^g;VD3(YYg-48;C@y*i=kbpidnH7XdoRpWmTE)oD{vvLJ zN?_&rMWA`@5y=|TeQ#5lSvhBLsCn4V{v}pt9EuQ~2QFm3gap6-VGTB!l4GKULB}y@$KyxYx3N??`GNC=HaBl)Dq(#_LR{SUXJ8$09j2Drmf_mB zr8VEwB;RMMX0+^4?)Si!*^|*xGxhUBp;9-J@hlQRBOlZut0Pn3M?hC4bz>N#_oav* zLhdF;pDdKy=BEIWL!XQN0DLFhU`Dd>Tjr%3z_k0Inh#)t)7i}^K~KU{E`OvMz6b<} z2Nrd)(G?q`lL|OUd)N|~_lkMQ;}KU*x}DVPMM442Nm3Q~!TZ2jd3|HX!W3)`Mb>u~ zzA^1oz-m}|$~2twQvxpKw5^x-&*n_T7i1P)nu~f$+B9MsPDnQm-&6BDpgFZe@#lQ; zEZo=ihu0nYRUKvjFyQdo*|9#wy~I-D(rwDVOX|CqrjFo5b0@oE8g0E9A|6^cy`b}V zSc_iXit%s0yxJp$0{76q!A*gBX(zPXNix!24E!jRp+;WkLvc8`EIb9V3yCK{iQz?Vubp^(DSq3BVZ-th0BomN z7+pA*qxp!aTX{SuA+;PERZNWzo~wwYaa$kHML9_ei~Pp?dR-!#=>`{8ru+S7SNbFR zY52RAeGlJH?We4*Ol>N?jVVxdN8285JWLDk8E(Y}znYrmKYMt#;O{Bzxf8~yV)FXx z3`T|@25Jg3!yb`aCkLEiYBg`B^<~trM^$!H^r)40qY9mNdH{i+JYyghE*x0lOE*5n zAV~v(yU6Vm17QUR;7bU{KVffwdq2VsdGe&!lSZFLsSoZ))@0q`zgks$Z}0y8vVC;$ z8a<9ayexI?+QACG7*;IaFaZZE!QNAs1OLcp?rb)!v_Ft$r}_vU%Z&!qG(hS+~&|R?U3Fqusxb5ABMb-50(` z_SuakpUKcoAD%qS4LbXkL+XyqPEVi+OC?qO)_X4;4$gY+IWHr#iMi?Vl3a;V5^w6B z(&RkW>S?oiczA%g!SbaS_t?_A@PV|LHa&(W@U#8p=(DqB-}`?ohS=;XkI@DX38=A5 zZPtU!U~iS=K7NX>>d2GGo^PsR>1-q<|J)s8?lvNJRIJJINEk=gpBq?lO^ujq?LIeR zW@?e*{cXr>G35Ji-P4b$;XG5SL0)klxEH)uc;ufJJb+u~_g?Dpv_QI3_lkoJBFLI ztZPUN$x7jI5nr*RQN3k5Ek~2=O#DMa%Ugxox%sGy|Uf?#^z54ag$yD(CdDr3f ztewE@$D)%17lw@^b?wn!eoM5u`ct_j5BrT2cCC82a<-2SW3R1auMaO}$A~WSS%It+ z+@qNm8=76-e=?o5G4DDDk*7vKdm4Z0%**JUNpn6OB?=vBk`=!FgsC2Fk2u^Z{?jMt z8zuUd!?Ruf<1C+b?T>U7W?`sqpvZTe{BtROAOyE=g01o>4r^JzHnScRw$h8W-mEwCpA3DRMnXw^u}j^L z6fz!NyPTdrib@>iTAoSTl%BOBu~{+xY5!hO?_W6|+0U6TZgvP6|4gCYTqm>1Y~_hL zIQ;a|#HUM6*v;*q+Lb>@&)0ZZ0UW}~aj)EqYDB+!SL&AW6E{~}B}xOkhpt7_ zRrmR9F&w_`8Z6lZZm;iWyh(0nRWWYgcQmc9na_W*F>mSWEtzix_tIExUSd_b4w%We zlq%ZDYC&)cg)by5%X&-17*|LekRBwlbrM_Y$+A4yO;~zu?r=s!wgZb<>9jcm4bZnEGg9CdwRW<<(o)yeB%1I( zEyU+1r=!>V`Kqa=&`(mZg1&oa!klOPUZV7g;EaKAR{2H5y-;*92R;@X1i0S})(){l zG#Z6yxiP5#YLtXIR0&XWH*7Q*ufO(`1UH2DeCmTaC33Y$84PT3>W1N%qu~{sqI7_? zS3)Ogk%r&}q8nVBh=KJ`7oVTV0YY@1S^erF(P)^o^9NOP zvF2eoLTk3*&;jnZX$!xe z6U;kU+EQ)KEVnwbot%i-GT5$42<~J5WySL6JpiWT`TAZ{8vjdal*fo2he;aHZqfU& z;C01dy+aIS+To#?ea^DuMQ7kC;*39KvmpPqN&I^Me^(15t zqIg_3OTkHWjNri=Wy7f$DV-Ns@+6~61Y6&@*2h0S6LKBn*ab?B4U|(OOEEGrMnMx-3w|~Xvk_V3a(qP&5bN~;frQNF0+PLas$@uTp(M~KEg2} z3!Vo(h#0_{&E*tIcg(=^;Wf&*|9ea|Hj z7vHS2$}7QS+Iy~{U5ATmvxApkNbJcis5>6D z(T%ii`|fT2Bk$eSd!c!63o~VHlMf*8Oo>;UoL{_^ej-=Ble}p%2iML*A=YA*M1e+% zXd9Z~|2NcKLcUIJaW{fyr_S=Lia11DMwZAOnh)cz0`{E=;pTeITVSwc>u_`Fvt%^8 zjwyNNASb>)LUa_7#Np6557uy3OJRl620KHTg18mbj3=V~OQH3TGjLX{zMTgj886k6 zybPD9odSd`Lnjdk2}mTO9Zh#Yr@Clv?f+1)%Hsw@3Ju;+4?9D z2~;>BCHzNItx+hy+c9B_A1V*=e{GUfGaa71C%501Qb0?G6#BH4E197vlcRZ z1K*c?fSiC=R^)HFgbjDUh4S5yad90o;;7O4JwPiyiB;>|5s)azZf7MFxGoJZXRJvukbS1@3v0%N(>h?2o_SI)&$H}}kD_o+Tl@u~VwQ4PbYXAU<`?EbZ{ z+Sj}=;zy&p*NN$E=D)*Cn)faHWvyLk43@h0G;<{Ts~V1NJ^lW1+{;zHZ-Im9l&}I^ zmpRh}+Pf@BC;~3eXE=0an80bJsb*hDJf26{lQ+)jk^**~R#LX%l8$LMX zo;cj~d$a>tMNTE21x5TKI4n?yjJ*%9`gv(}uf)eNde9UMjjraOZ8zAPv+ZhP3r5Le z(YRIUla}(z&0ntp-)^pgTT^K%3$n>$BKq&R{XJ*BKAH5phEzGde5U=l>JvD_IHR^~ za+)-M^5=+nB#ot;6z;1-A8V&+Fn7TzY=Ho+`x@7M-8O%1A-x#hL~ONg_bbX3R!Yh- zv`yBXcd-AjbURP;bFii(Oy{QOV zaasQUeYBr+kU%>J-39yw|H)8l%&Te}d6;;ViWvK{vQX_3w$mG+#f4XpU3F3$Tokf3 zc8lEWM3>|(6ir#VI^8NE^WrX2Clc^MaRy+T-Wvrf!z&ea4Gu3>B;(ERV+=^9a}Iiv zT;g8>l)N}X@^1j739Ma$vZKR6xxee7xG7W3HV6HY6$&EQ=j6Ux0}ZClE1MLKt4`2u zbTD@i(ScG5hO?TL#TTKwg%g~Ma|I5NKTN7y4_|sQBZ=+v1j!F9DTs>`++0GX8L1r? zUUJO7zcv=z-gNu!f$rU3#(x$Hk#I$DCF)DkVeUh-EZ>*j!Wv^aOWv5X0{Ya6aA>l~ z36gz3vVX2%KWLmxw6DN=tv{7IGy%-?sS0PuA@gO6uGPDAi+R&yXJcE9ng%N!dO~jQ zR*b30@aP>)UBp?fMEO=Ssn5@}U!MJ3w!La5kEV0$v}?}(r%2YP!QlOwxi|rKcTPr) z4PLBC7VZM^@bZ(T4pWCHoa)Y7;%ne_79SIfLNp;ohoE0x5FM;&Qsd2=>k&_Z&aJFX z2WqGt2AiPsuO=(9+Ymb57_;~yLhShI{+jOQVDzzl)Xk(3DsU0`5Tnk*qP|q~$qR=- z4*`-*ADfxNFHC{f!ZMJ$XvFszQ|%PSIT!)-2c~qly2J$wguxusU{H+WlM}Pya72e} zkz+7=1@`HYZKGZRK4o)AFRN0xw_qJftPg3FwUwkwZrMXbiyWXS;jLkEa38e$KbiN- zNnBa6BcGdM%~e?>Z6td1I~*kFo&@MgmZm1{1*$-~)P8{8q+b?Pn1NFa#EBNIVN^<3+}^K7lYUAVZ*eK-wZ%=~qlOIc+uO_&98AP$VFyQfraP zt6Zd;z(XOZc3n~O0I6taEx>5ZYk2H_QR+=dw}`Plk;P??dPR;Z`!T=onc)>QC~b2@Y3~W1uI5&z?hu5 zcZ+B%yi0MWz!kFYQI4<(XdP^A_zAHuLj2ca`;n&0IZi3CaDId$=rQAP-TRqZ&H{wW zE1zLuCM{?-?6s?KoHb1T!WB-#?FrTcG;snDiPHUI3+O%&G2aqObc2qG9Ek^pVZ_NB zNJt0G*#iVRzb<(du@$E_QB$A+E+;;JH%vTG#=&?9U+-Ib@>5>^;fOX$4Sr+e$kGEa zSHo?zm0}>qLH>oa>ol=SAKyc)HaT+%po)#wdxY8GJl|tc^9l%OWP;xr31xhWQhON@ zSupQBXyKY+Kbbz|!plDWv&kNpa+flT606dLTi{<_z`%WJSvyLdq%G~jD2Z|4Nklwn zBS+dT-6<)ui{R8I0fR21f5e;>ASiG=o94VuWragoIc}|vsReW<+L`l3h)uIkCT3x7 z_8!ssCZT$ecS!W^RXjs@bHtS%854fyA^WnCwtGhKtuv2~L=c;jKDu<6e#GVGxZb^! z%7E5{aBCEs47Z3dUleN^#-}ej@yak?rV(G*g1W`TDg2m-&8?;394kbr4;l+5)oE|h^uyE6`ZT31-*GyT`HRf2h0$jOgPLMEg-AhNAJ@7%xFkioz>M@gR#WG|8wN&${hep28&Wl)N;DH-C%U zVgIZ+ATX^4FH6vYttM02FHkqy8HWH|3|Lb}C%O*U*$XD{?NUT_wmSnT$S%P+M_v{@ z|BW9D^Btotl~^`saM5#_Plio{JI$&+=~~@SKA&FOS6P2IufD!GGsA0o`6YQP ziuVrr#<&BAd>A!C6`)e6O2i{@tBa)y8yi6D4)ZvJ{ZQBo0*j}VU)ss%suJe;HS0BM zzJ&E|w!?uelx(moU*!656|bdzhrFdvK(Fo#)6^^{w}k&Ea|N!NVk^+A4X;1Ts-dx6 zS(}K*A!`6ut!*d$A?+g_0G2y?cK#AR0N_AGZvfq%3bcAr{f5MfpypAavO1hUfj=r8 zX?pjru5NI8z?321?TTe?haDnfm5CuBo^76p+TIib()q=dS3i)z@d*#Dmd2mMRTz|4GV+EX+?ax=H&a)Gn^lvQy#oaMkXME zV|a=Rgnl2%{BwYO)jSTP+;m8UC9$dcO^(R-F~@{2!1J^usT!#(ijo{o*)hvmOOsl| z4lnDIj486-e1UPu+4oYdH$Zlr*pP?5h_}GOjuW^lKla_rKB(>04SsI58Lqfx+eQcQX5ZJMt3Lio~^bF`0hQV)tGQ%Ig1(qJPIcPR8TqzsBaOTfIk$!nW z2D?WrQbfK^`=q9lTr#g}(p*yZTtcbJL~P{&Um6PV0aF^nGbfe zZ8Q_bS6{zn<73YZQt=kJ?ajka|jLB1uVDM))r`m>=c?F{o#!J|0rHq z=J3CMj#p@)FR54}?IR-KFA5_g<#4N!i;ln~`C-9cXo+M-w1d2WD0z7gT{zBf3y}KA zr>_Mi2iaa6()9utSxFYYf7*FWku_^`HB-XvcXC|k+cWrNvC6WL29V(>Ky1MtC`p8x zqi!R^kRD`$YNwCSv^Hd=mk^z6pd^?ViqxuM>AKJfXWKZY+_)KFIs-I3KFV0s8FzUz zVCQHZzf@CQF)>NVG8vDSk6wAYBdf=_V!e!R6rvV_Vblb$6|-9NmwP-`*lgR#J5#qwIcTg*Ii^zKGRvhzoOy|lRs){C74-=k} zG%}6m$Xlh`*&x*k%&%*Ky&peM>ct7xT9#FbX0YWl5Z1;iq9qP?k`UYB!WXeq4eTJL){d;Rjw6QhdaTb#UMwuHVBxBuh~O9GPhls&5$&46fXU|2 zKs^ffkORbAl00DXo^iF|zKjzl41)MmaLCLdSQ{^uC%5cmD#L}qf#_NYU7c@NosUXv z$0t%%_t^!O5mRU`Hx#sBxav(KL_gB{wy^2;(<}EpPfB@rwTmu(i~TRdT?$pSxWMyNA@2v z0jQlbT5!-rD!10dpp==X5IFeR!lclOawU$sVsaKvh(E^~M7p`m-XCVqV=N8JBhL#oHmM3<)T*N;usdTIORFeEWgn$6U8Em!gnxmzw;nC62+& z#+IRvWgbtqb=wGbFY2DW(05?&=P26A$#1^8{&%kEsI(k&ZwJPz|)Lt?d@I=j7>}&$t*I z>7YZ9bAh`rci53NJF${6&qWYzBndT$%|VeQ<6+uplVjMcAf#rvA` zWep6UX6jgB^xcI;aYG$WeK$H`(;n1;O_#$uPw@;;XI8c8(>UpNBN&$dTLNhkHqiu^k$wPSt9h>Q~Yi@k!>(_T(Jrc4TFhGiqrnSbTVv{-m$U%<(8Q}OX97@muR+50L23L{T;U(2s$ zMch9B^!w(nrN-idk}=~u94~C+m~G8U;4mx(Y6(JJW65>_*)CAw3*t3E$P&=wG1bVn zzSQ-;M5z`br31O0d<~1j=_CJXgdCrf1~94%FRH7cyVl98z5#bCcH&ZPgc0$74q2i@ z%;X==w=iYNEk3ZKvBFsR4PIko-E;+s?Ta4#Y_YnbFhfE?1tJo-)z0summi|k7|V(9 z$PeGRFrxQmR-RAU#8wTy4Y{&A+j5qZ#5h64S$FRnJQaCScRCJ>L5Xj|?T`iW4#XE> zf>AXv?a@O)+D6v&m%M6H7nyd7&Ih5i!jT-$pKRC?)+5$H>vNL{Xdn_20XkbhnqL|B zKSqvfxy6HeV3pRPTd`%iCF(mfyTsc`pYc>!_Zz87=B2?hilSm-(4bfsbmhS!VPh~% zx;`atq0VV8SNp0*D0Cpjl!V`uRx~2F+R7Aq9s;=lP9kAC9!I*$BUed+2&e3 z#e}H&)?=sBhQ4~ASPzdQc)TC=LxS*5FH5*|aR#718g&FpzwXLuvOfC*vX;sli)_UQ zB?a&Tn4%`egWPrmg{9)KhtzvpI!J=0c?-Wq`w4XmOa(|0E12(7;7UJZ)E%^cG;ovg z4ocz!gR6g7pzABco1=c>7EL^dLT+gE68^+cQP<6(U8qHj= z@PxrW2Se~E5eFe(2N|eKM?qxLokhbAosI_u%3w!4KW6WeYY6K6lZ6vM+a7~Q%60Qz z4y`@96ryD^KrGc8nVjogv@OED2)mG)>(hvN>GdtzH|)m99H+5*#aiGc)~FLILyn+r z8&W=e(RJ!Lb5glkk}sD++65ta+$6gIAh4CGAt`CHe*sZ=qiyiY%B_Ezj0h^&4C>b< zHQCdO3rm^{s=~|X0BpmS`;}cGCH9osuK&N0o@EGMBH!xHvUKm@k--JlAlwRVh3%jb zEfmckFk@=%k;4(O6_5dScl>W;1S3z^rW_j)NAe{~R*moKV#lQ_^JAyIXAAyle%DZ) zHvf|2qlxriRk4#jvHw zeZey2n?WQSUPh^|pyZ9#;#RiRlo?*Zzl8ix)HK(;&FP{~*A+}-+tyU8eqqMOrQ1VA zuHpkwHGECMYZl*XbUZ~V&! zMw7@W^(%JMTd3|H`F~Wcy0zq*WgXa$Y*FMXR)WYP7cmNcG?Cr#EAyX>B!z(=!@ENH zub@Ewmd9Pi-H~8Q`{W@ox@9WKVHY9&&c8(ayD>b8)klQFY}80eITS1(f^i@CGp#=K z9rlXRW{059#nU+#ZoB+dnv8CSO>rwm*k?+VyyPcZO?}6Vb;Eo^B1?VG*4Db^qG}Sw znu&Y_8#UJ1iP2&zi`6C1?Yuxkeq_h;n27JuM!iqgkis`7@z4pgLd z_H8GyJD7){5TKIVE;WTVg9tWv8}R^GCs@8;&fGe|iK62~XNXKAaD^72LrFQ|<){GPMo!K#V}NJRrwCrraRP1enPAT9Vlk^^&gYts zQCcSL%h#{baMJ

Rg=eIhq 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

lwc@>De?Ztr%Yz*%DE*j#pRrv%%H*(R` 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 A8vp