From 624004f7a58a43ef8128a7053728e2599156bbe9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 10:00:10 +0000 Subject: [PATCH 1/2] ci(importer): guard @repo/crawlers dist entry points used by Rate-Services Follow-up to #105 / DOS/Rate-Services#15, which made Rate-Services consume the engine through the vendor/rate submodule. - Add `test:dist`, a smoke test that imports @repo/crawlers (root, ./shopee/arena-spec-syncer, ./shopee/flagship-seed-data) and @repo/shared-data by package name from the built dist, checks the .d.ts files, and asserts that the test scripts that execute on import stay unexported. Run it in CI after the crawler tests. - AGENTS.md / CLAUDE.md: document that Rate-Services consumes the engine via the submodule and must not copy engine files, and replace the stale `npm run build` Rate-Services commands with the pnpm ones. - ROADMAP: record the change as 5.19. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015v1boe4iagzc2mQVMuU39b --- .github/workflows/ci.yml | 3 + AGENTS.md | 12 ++- CLAUDE.md | 1 + Docs/Resources/ROADMAP.md | 4 + apps/importer/package.json | 3 +- .../shopee/__tests__/dist-exports.smoke.mjs | 80 +++++++++++++++++++ 6 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b7f1a4..7b21acf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,9 @@ jobs: - name: Run @repo/crawlers Open-Core Intelligence Tests run: pnpm --filter @repo/crawlers test + - name: Verify @repo/crawlers dist entry points (consumed by DOS/Rate-Services) + run: pnpm --filter @repo/crawlers test:dist + - name: Build & Typecheck @repo/extension (MV3) run: pnpm --filter @repo/extension build diff --git a/AGENTS.md b/AGENTS.md index 578171e..3df1688 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,6 +115,8 @@ Hệ thống Rate được tách thành **2 repositories riêng biệt** (và d > ⚠️ **CẤM TUYỆT ĐỐI:** Không bao giờ commit code giải Captcha, xoay Proxy, sniff API nội bộ của sàn, hoặc khoá bí mật (`SUPABASE_SERVICE_ROLE_KEY`, `AI_API_KEY`, `MPROXY_*`) vào `DOS/Rate`. +> 🔗 **Một nguồn sự thật cho Engine:** `DOS/Rate-Services` dùng `@repo/crawlers` (`apps/importer`) và `@repo/shared-data` (`packages/shared-data`) qua git submodule `vendor/rate` trỏ về `DOS/Rate` (pnpm workspace, import theo tên package, ví dụ `@repo/crawlers` hoặc `@repo/crawlers/shopee/arena-spec-syncer`). **KHÔNG** copy file engine sang Rate-Services. Cần dùng thêm module nào thì khai báo subpath tường minh trong `exports` của `apps/importer/package.json` (không dùng wildcard `./shopee/*` vì các script test chạy ngay khi import) và cập nhật `test:dist`. Mọi thay đổi engine phải merge vào `DOS/Rate` trước, sau đó bump submodule + chạy `pnpm install` để cập nhật `pnpm-lock.yaml` bên Rate-Services. + --- ## 3. Kiến Trúc Kỹ Thuật Cốt Lõi & 4-Layer Backend (Zero-Strapi 2026) @@ -151,7 +153,11 @@ pnpm --filter @repo/crawlers exec tsx src/shopee/test-review-analyzer.ts pnpm --filter @repo/web typecheck pnpm --filter @repo/web build -# Tại D:\Projects\Rate-Services -npm run build -npm run test:enricher +pnpm --filter @repo/crawlers test:dist + +# Tại D:\Projects\Rate-Services (pnpm workspace + submodule vendor/rate) +git submodule update --init +pnpm install +pnpm build +pnpm test:enricher ``` diff --git a/CLAUDE.md b/CLAUDE.md index 346c4a5..7922316 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,6 +57,7 @@ Hệ thống **Rate Platform (`rate.vn`)** đã loại bỏ hoàn toàn Strapi C 2. **🔒 `DOS/Rate-Services` (Private Enterprise - `D:\Projects\Rate-Services`):** - Chứa toàn bộ phần nhạy cảm: Playwright Stealth Scrapers (`shopee-scraper.ts`, `shopee-search-scraper.ts`, `sniff-search-api.ts`), Giải Captcha (`shopee-captcha-solver.ts`), Xoay Proxy (`proxy-manager.ts`), và Queue Workers (`queue-worker.ts`). - **TUYỆT ĐỐI KHÔNG** đưa code bypass anti-bot/captcha/proxy vào repo Public `DOS/Rate`. + - Engine Open-Core (`@repo/crawlers`, `@repo/shared-data`) được dùng qua git submodule `vendor/rate` (pnpm workspace). **KHÔNG** copy file engine sang Rate-Services; sửa engine tại `DOS/Rate` rồi bump submodule. --- diff --git a/Docs/Resources/ROADMAP.md b/Docs/Resources/ROADMAP.md index 912dc11..8ec12d1 100644 --- a/Docs/Resources/ROADMAP.md +++ b/Docs/Resources/ROADMAP.md @@ -250,3 +250,7 @@ Hệ thống **Rate Platform (`rate.vn`)** vận hành trên kiến trúc **4 T - [x] **5.18. Bộ Kiểm Toán Độ Lệch Giá Đa Sàn (`PriceSpreadAuditor`) & Bảng Xếp Hạng Chênh Lệch Giá 10 Sàn (`priceSpreadLeaders` - Issue #13 bên `DOS/Rate-Services` & Issue #92 bên `DOS/Rate`):** - Xây dựng `PriceSpreadAuditor` (`src/scrapers/price-spread-auditor.ts`) tích hợp vào Step 6 của `src/index.ts`, tự động tính toán độ lệch giá tuyệt đối (`spreadVnd = maxPrice - minPrice`), tỷ lệ chênh lệch (`spreadPct`), sàn rẻ nhất và sàn đắt nhất cho toàn bộ `36/36` Master Products trên 10 sàn, đồng bộ vào `key_specs.price_spread_audit` trên `rate.master_products`. - Mở rộng `getCrossPlatformDealRadar` (`apps/web/src/server/rate/deals.ts`) và giao diện Chrome Side Panel (`apps/extension/src/sidepanel/sidepanel.ts`) hiển thị bảng **📊 Chênh Lệch Giá Đa Sàn Lớn Nhất (`priceSpreadLeaders`)**. +- [x] **5.19. Một Nguồn Sự Thật Cho Engine Open-Core: Rate-Services Dùng `@repo/crawlers` & `@repo/shared-data` Qua Git Submodule `vendor/rate` (Issue #103 & PR #105 bên `DOS/Rate`, Issue #14 & PR #15 bên `DOS/Rate-Services`):** + - Kiểm toán độ lệch giữa 2 bản copy engine: sau khi chuẩn hóa format bằng prettier, bản copy trong `DOS/Rate-Services` không còn dòng logic nào mà `DOS/Rate` thiếu (`DOS/Rate` là superset), nên xóa bản copy không làm mất logic. + - Chuẩn hóa entry point build cho `@repo/crawlers` (`rootDir: ./src`, `declaration: true`, `exports` tường minh cho `.`, `./shopee/arena-spec-syncer`, `./shopee/flagship-seed-data`; các script test chạy ngay khi import được giữ private) và thêm smoke test `pnpm --filter @repo/crawlers test:dist` vào CI để chặn hồi quy. + - Sửa lỗi build `@repo/crawlers` làm CI `dev` đỏ từ 5.10: 8 seed đa ngành hàng chưa có `expert_review_summary` / `expert_criteria_scores` nên 2 field này chuyển thành optional trong `FlagshipDeviceSeed` (web đã có fallback từ thông số). diff --git a/apps/importer/package.json b/apps/importer/package.json index 8986590..668b909 100644 --- a/apps/importer/package.json +++ b/apps/importer/package.json @@ -28,7 +28,8 @@ "dev": "tsx watch src/index.ts", "lint": "eslint . --ext .ts", "format": "prettier --write \"src/**/*.ts\"", - "test": "tsx src/shopee/__tests__/open-core-intelligence.test.ts && tsx src/shopee/test-review-analyzer.ts" + "test": "tsx src/shopee/__tests__/open-core-intelligence.test.ts && tsx src/shopee/test-review-analyzer.ts", + "test:dist": "node src/shopee/__tests__/dist-exports.smoke.mjs" }, "keywords": [ "product-normalizer", diff --git a/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs b/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs new file mode 100644 index 0000000..0a19064 --- /dev/null +++ b/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs @@ -0,0 +1,80 @@ +/** + * Smoke test for the built @repo/crawlers & @repo/shared-data entry points. + * Imports by package name (Node self-reference via "exports"), the same way + * DOS/Rate-Services consumes the engine from its vendor/rate checkout. + * Run after `pnpm --filter @repo/shared-data build && pnpm --filter @repo/crawlers build`. + */ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import { createRequire } from 'node:module'; + +const crawlers = await import('@repo/crawlers'); +for (const name of [ + 'ProductNormalizer', + 'detectCategorySlug', + 'EntityMatcher', + 'AiSpecEnricher', + 'sanitizeSpecifications', + 'AiReviewAnalyzer', + 'computeReviewWeight', + 'ShopeeIngestionPipeline', +]) { + assert.equal( + typeof crawlers[name], + 'function', + `@repo/crawlers is missing export ${name}` + ); +} + +const { ArenaSpecSyncer } = + await import('@repo/crawlers/shopee/arena-spec-syncer'); +assert.equal( + typeof ArenaSpecSyncer, + 'function', + 'ArenaSpecSyncer subpath export is broken' +); + +const { FLAGSHIP_DEVICES_TO_SEED } = + await import('@repo/crawlers/shopee/flagship-seed-data'); +assert.ok( + FLAGSHIP_DEVICES_TO_SEED.length > 0, + 'flagship-seed-data subpath export is empty' +); + +const { ProductSpecificationsSchema } = await import('@repo/shared-data'); +assert.equal( + typeof ProductSpecificationsSchema?.safeParse, + 'function', + '@repo/shared-data is broken' +); + +// Scripts that execute on import (test-ai-enricher, test-review-analyzer, +// run-ingestion-test) must stay private. Resolve only, never import them. +for (const script of [ + 'test-ai-enricher', + 'test-review-analyzer', + 'run-ingestion-test', +]) { + assert.throws( + () => import.meta.resolve(`@repo/crawlers/shopee/${script}`), + { code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' }, + `@repo/crawlers/shopee/${script} must not be exported` + ); +} + +// Type declarations must ship with the entry points. +const require = createRequire(import.meta.url); +const pkgDir = require + .resolve('@repo/crawlers/package.json') + .replace(/package\.json$/, ''); +for (const dts of [ + 'dist/index.d.ts', + 'dist/shopee/types.d.ts', + 'dist/shopee/arena-spec-syncer.d.ts', +]) { + assert.ok(fs.existsSync(pkgDir + dts), `Missing type declarations: ${dts}`); +} + +console.log( + '✅ @repo/crawlers & @repo/shared-data dist entry points resolve by package name.' +); From f4396fce5e0780d5d4018d9e7db96ea0b57ff4d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 10:04:36 +0000 Subject: [PATCH 2/2] test(importer): also check flagship-seed-data declarations in test:dist Cover the .d.ts behind the ./shopee/flagship-seed-data export, and credit #105 / #107 separately in ROADMAP 5.19 (review follow-up). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015v1boe4iagzc2mQVMuU39b --- Docs/Resources/ROADMAP.md | 4 ++-- apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Docs/Resources/ROADMAP.md b/Docs/Resources/ROADMAP.md index 8ec12d1..d7ea18d 100644 --- a/Docs/Resources/ROADMAP.md +++ b/Docs/Resources/ROADMAP.md @@ -250,7 +250,7 @@ Hệ thống **Rate Platform (`rate.vn`)** vận hành trên kiến trúc **4 T - [x] **5.18. Bộ Kiểm Toán Độ Lệch Giá Đa Sàn (`PriceSpreadAuditor`) & Bảng Xếp Hạng Chênh Lệch Giá 10 Sàn (`priceSpreadLeaders` - Issue #13 bên `DOS/Rate-Services` & Issue #92 bên `DOS/Rate`):** - Xây dựng `PriceSpreadAuditor` (`src/scrapers/price-spread-auditor.ts`) tích hợp vào Step 6 của `src/index.ts`, tự động tính toán độ lệch giá tuyệt đối (`spreadVnd = maxPrice - minPrice`), tỷ lệ chênh lệch (`spreadPct`), sàn rẻ nhất và sàn đắt nhất cho toàn bộ `36/36` Master Products trên 10 sàn, đồng bộ vào `key_specs.price_spread_audit` trên `rate.master_products`. - Mở rộng `getCrossPlatformDealRadar` (`apps/web/src/server/rate/deals.ts`) và giao diện Chrome Side Panel (`apps/extension/src/sidepanel/sidepanel.ts`) hiển thị bảng **📊 Chênh Lệch Giá Đa Sàn Lớn Nhất (`priceSpreadLeaders`)**. -- [x] **5.19. Một Nguồn Sự Thật Cho Engine Open-Core: Rate-Services Dùng `@repo/crawlers` & `@repo/shared-data` Qua Git Submodule `vendor/rate` (Issue #103 & PR #105 bên `DOS/Rate`, Issue #14 & PR #15 bên `DOS/Rate-Services`):** +- [x] **5.19. Một Nguồn Sự Thật Cho Engine Open-Core: Rate-Services Dùng `@repo/crawlers` & `@repo/shared-data` Qua Git Submodule `vendor/rate` (Issue #103, PR #105 & PR #107 bên `DOS/Rate`, Issue #14 & PR #15 bên `DOS/Rate-Services`):** - Kiểm toán độ lệch giữa 2 bản copy engine: sau khi chuẩn hóa format bằng prettier, bản copy trong `DOS/Rate-Services` không còn dòng logic nào mà `DOS/Rate` thiếu (`DOS/Rate` là superset), nên xóa bản copy không làm mất logic. - - Chuẩn hóa entry point build cho `@repo/crawlers` (`rootDir: ./src`, `declaration: true`, `exports` tường minh cho `.`, `./shopee/arena-spec-syncer`, `./shopee/flagship-seed-data`; các script test chạy ngay khi import được giữ private) và thêm smoke test `pnpm --filter @repo/crawlers test:dist` vào CI để chặn hồi quy. + - Chuẩn hóa entry point build cho `@repo/crawlers` (`rootDir: ./src`, `declaration: true`, `exports` tường minh cho `.`, `./shopee/arena-spec-syncer`, `./shopee/flagship-seed-data`; các script test chạy ngay khi import được giữ private, PR #105) và thêm smoke test `pnpm --filter @repo/crawlers test:dist` vào CI để chặn hồi quy (PR #107). - Sửa lỗi build `@repo/crawlers` làm CI `dev` đỏ từ 5.10: 8 seed đa ngành hàng chưa có `expert_review_summary` / `expert_criteria_scores` nên 2 field này chuyển thành optional trong `FlagshipDeviceSeed` (web đã có fallback từ thông số). diff --git a/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs b/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs index 0a19064..28b67ea 100644 --- a/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs +++ b/apps/importer/src/shopee/__tests__/dist-exports.smoke.mjs @@ -71,6 +71,7 @@ for (const dts of [ 'dist/index.d.ts', 'dist/shopee/types.d.ts', 'dist/shopee/arena-spec-syncer.d.ts', + 'dist/shopee/flagship-seed-data.d.ts', ]) { assert.ok(fs.existsSync(pkgDir + dts), `Missing type declarations: ${dts}`); }