Skip to content

Changelog

Thay đổi của các package @tasco/* bản 2.x, gộp từ CHANGELOG.md của từng package. Các package phát hành cùng một số phiên bản (lockstep), nên mỗi phiên bản dưới đây áp cho cả bộ; thay đổi chạm nhiều package chỉ ghi một lần, kèm tên các package đó. Nâng cấp từ major trước: Nâng cấp phiên bản.

Sắp phát hành

Đã merge vào main nhưng chưa có trong bản phát hành nào (changeset chưa version). Tài liệu trên site có thể đã mô tả các thay đổi này trước khi package lên registry.

Tính năng mới

  • uiCForm expose API của form qua template ref: validate, validateFields, resetFields, clearValidate, scrollToField.

    • Trước đây CForm không defineExpose nên formRef.value.validate()undefined và gọi sẽ ném TypeError. Trang chỉ gửi form được bằng @finish + nút html-type="submit" — cách đó vẫn chạy, nhưng không làm được validate một phần (bước wizard), resetFields() sau khi lưu hay clearValidate() khi đổi giữa chế độ thêm/sửa.
    • Thêm kiểu public CFormInstance (kèm CFormNamePath, CFormValidateOptions, CFormScrollOptions) để app khai ref<CFormInstance>() mà không phải import từ ant-design-vue — ESLint chặn import đó, kể cả import type.
    • Chỉ mở đúng 5 hàm trên; hàm khác của antd Form (vd getFieldsValue) vẫn không expose để hợp đồng của @tasco/ui không dính vào cây type của antd. Gọi API khi form chưa mount (hoặc đã bị gỡ) báo lỗi [@tasco/ui] … thay vì TypeError.
    • Tương thích ngược: không đổi prop, slot hay sự kiện nào.
  • ui — Bổ sung JSDoc cho mọi prop/event và defineSlots cho component có slot; build sinh thêm dist/component-meta.json.

    • Mỗi prop, event của 17 component C* đều có mô tả tiếng Việt; component có slot khai báo defineSlots để slot vào được bảng API. CFormCTable forward slot động (tránh TS7022 của vue-tsc) nên chưa khai defineSlots — slot #title, #toolbar, #filterField của CTable mô tả tay trong trang docs.
    • Thêm subpath @tasco/ui/component-meta.json: props/events/slots kèm kiểu và mô tả, sinh bằng vue-component-meta lúc build. Docs (bảng API) và @tasco/mcp (get_component) dùng chung file này thay vì mỗi nơi tự parse component (RFC 0003).
    • Chỉ thêm kiểu và dữ liệu, không đổi hành vi runtime.
  • mcp@tasco/mcp đọc chung dữ liệu với site docs: component lấy từ @tasco/ui/component-meta.json, docs tách theo mục và trả link đúng phiên bản /v2/ (RFC 0003).

    • get_component có đủ sự kiện kèm kiểu payload, slot (kèm props của slot) và link trang docs — trước đây parser regex chỉ đọc được props và tên sự kiện. Hỏi primitive antdv (CSelect, CInput…) nhận tên gốc antdv, cách import từ @tasco/ui và link trang nhóm primitive, thay vì "Không tìm thấy".
    • list_components liệt kê thêm primitive antdv re-export qua @tasco/ui.
    • search_docs trả từng mục ##/### kèm link https://web.docs.vtii.vn/v2/…#anchor và đoạn đầu nội dung, thay vì cả trang với đoạn trích 180 ký tự. Gõ không dấu vẫn khớp chữ có dấu; từ khoá ngắn (IAM, ui) khớp nguyên từ.
    • Tool mới read_doc: đọc đủ một mục (kèm mục con) hoặc cả trang theo link, route (/guide/auth/guard) hoặc đường dẫn file.
    • Nội dung docs của MCP có code demo, bảng API và token thật thay cho thẻ <demo>, <ApiTable>, <TokenTable>.
    • Server gửi instructions lúc kết nối: tool nào dùng cho việc gì, app không import ant-design-vue.
    • Kiểu export ComponentMeta có thêm events, slots, docsUrl; DocEntry có thêm url, level; Knowledge có thêm primitives. Trường cũ (emits, props[].optional) giữ nguyên.
  • theme · ui@tasco/theme export luminanceOfcontrastRatioOf; meta của @tasco/ui ghi giá trị mặc định dễ đọc.

    • Hai hàm tính tương phản WCAG trước đây chỉ dùng nội bộ (test khoá cặp token) nay là API công khai — bảng token trong docs tính tỉ lệ tương phản bằng chính hàm này thay vì chép lại công thức.
    • component-meta.json: giá trị mặc định đổi từ literal JSON ("Xoá") sang dạng đọc được ('Xoá'); prop boolean không khai mặc định ghi rõ false đúng như Vue xử lý.
  • ai · composables · theme · utils@tasco/theme export danh sách cặp chữ/nền đã khoá tương phản; bổ sung TSDoc cho mọi export của theme, composables, utils, ai.

    • Thêm contrastPairs (cặp chữ/nền component đang dùng, kèm nơi dùng và ngoại lệ đã biết), pairContrastOf(pair, mode) (tương phản thấp nhất của cặp, nền gradient xét mọi điểm màu) và AA_TEXT_CONTRAST (4.5). Test khoá tương phản và bảng "Màu & tương phản" của docs cùng đọc danh sách này thay vì mỗi nơi giữ một bản.
    • Mọi export, thuộc tính interface và giá trị trả về của composable use* có TSDoc tiếng Việt — trang Tra cứu API sinh từ đây bằng TypeDoc, build docs báo lỗi khi export mới thiếu mô tả.
    • Chỉ thêm export và chú thích, không đổi hành vi runtime.

Sửa lỗi và cải tiến

  • cli — App scaffold ghim @tasco/mcp theo major (npx -y @tasco/mcp@2) và CLAUDE.md của app hướng dẫn AI tra framework qua MCP tasco.

    • Trước đây .mcp.json gọi @tasco/mcp không kèm version nên AI có thể đọc props và link docs của bản khác với bản app đang cài. App đã scaffold từ bản cũ: đổi args của server tasco thành ["-y", "@tasco/mcp@2"].
    • CLAUDE.md thêm mục "Tra cứu framework": get_component, search_docs/read_doc, list_tokens.
  • cli — README của @tasco/cli bỏ lệnh pnpm create @tasco/app (không có package @tasco/create-app nên lệnh không chạy), hướng dẫn npx @tasco/cli <tên> hoặc pnpm dlx @tasco/cli <tên> và liệt kê đủ file dự án sinh ra.

  • uiCInputPercent nhận dấu phẩy thập phân khi gõ và hiển thị theo quy ước Việt Nam (12,5).

    • Trước đây ô chỉ giữ chữ số và dấu chấm: người dùng gõ 12,5 theo thói quen thành 125, rời ô thì bị kẹp về max (mặc định 100) mà không có báo lỗi nào.
    • Nay dấu phẩy và dấu chấm đều là dấu thập phân (gõ nhiều dấu thì giữ dấu đầu tiên); vẫn cắt theo precision khi gõ và kẹp [min, max] khi rời ô như cũ. Ô hiển thị dấu phẩy, khớp CInputCurrency.
    • Tương thích ngược: v-model:value vẫn là chuỗi dấu chấm ('12.5'), code gọi API không phải đổi. Chỉ phần hiển thị trong ô đổi từ 12.5 sang 12,5 — test e2e đang kiểm giá trị hiển thị của ô thì cập nhật theo.
    • Khi ô phải sửa lại chuỗi vừa gõ (đổi . thành ,, bỏ ký tự lạ), con trỏ giữ đúng chỗ thay vì nhảy về cuối ô — chèn . vào giữa 125 rồi gõ tiếp vẫn ra đúng số.
    • Value truyền từ ngoài dùng dấu phẩy ('12,5') được chuẩn hoá về '12.5' khi rời ô, thay vì mất phần lẻ.
    • Dán chuỗi có nhiều dấu thập phân (vd 1.2.345) giờ cũng bị cắt theo precision; trước đây lọt thành 1.2345.
  • uiCInputPercent gõ được số âm khi min âm; sửa hướng dẫn đặt dấu % trong docs.

    • Trước đây ô bỏ dấu - khi gõ, nên min âm (vd -100 cho tỉ lệ tăng trưởng) chỉ có tác dụng với value truyền từ ngoài. Nay min < 0 thì giữ một dấu - ở đầu số (gõ -12,5'-12.5'). min từ 0 trở lên (mặc định) vẫn bỏ dấu - như cũ.
    • Ô có min âm dùng inputmode="text" vì bàn phím số (decimal) của iOS không có phím -; ô khác giữ inputmode="decimal". Truyền inputmode vẫn ghi đè được.
    • Rời ô khi mới gõ dấu mà chưa có chữ số (-, ,) thì ô về rỗng (chưa nhập), thay vì để model '-' hay '.'Number() đọc ra NaN.
    • Docs: ô vốn có sẵn hậu tố %, nhưng trang component lại khuyên đặt % ở nhãn hoặc addon-after, làm % hiện hai lần. Đã sửa hướng dẫn và bỏ (%) khỏi nhãn trong demo, story, playground. App đang ghi (%) ở nhãn cạnh CInputPercent thì bỏ đi; cần đơn vị khác (vd %/năm) thì truyền suffix.
  • nuxt-layer-base — Layer đặt <html lang="vi"> và khai kiểu permission trong RouteMeta.

    • Trước đây thẻ <html> không có lang: trình đọc màn hình đọc nội dung tiếng Việt bằng giọng mặc định của máy và trình duyệt gợi ý dịch trang. App cần ngôn ngữ khác thì ghi đè app.head.htmlAttrs.lang trong nuxt.config.ts.
    • definePageMeta({ permission: '/orders/search' }) đã được middleware permission.global hỗ trợ nhưng chưa có trong RouteMeta nên không có gợi ý kiểu. Không đổi hành vi.
  • cli · nuxt-layer-base — Sửa template và mock đang hướng dẫn sai registry và định dạng permission.

    • @tasco/cli: .npmrc.gitlab-ci.yml của app scaffold trỏ group endpointhttps://gitlab.dnpwater.vn/api/v4/groups/tasco/-/packages/npm/ thay cho instance endpoint — instance endpoint trả 404 với người không phải member của project core. .npmrc đọc token qua ${NPM_TOKEN} (Group Deploy Token, scope read_package_registry). App đã scaffold từ bản cũ nên chép lại 2 file này.
    • @tasco/nuxt-layer-base: dữ liệu mock của POST /auth/login (NUXT_AUTH_MOCK=true) đổi permission từ order.read/order.write sang URI /orders/search, /orders/create, /orders/update cho khớp IAM thật. Dự án đang dev bằng mock với permission dạng order.read sẽ bị 403 — đổi definePageMeta({ permissions }), can()v-can sang URI tương ứng.
  • cli · nuxt-layer-base — Template app trỏ tới các trang tài liệu mới của GĐ2 (hướng dẫn từng bước, mẫu trang, phân quyền, xử lý sự cố) trong README.mdCLAUDE.md. Sửa comment sai trong app.config.ts của layer: Nuxt nối mảng khi merge cấu hình giữa các layer (defu), không thay nguyên.

2.0.0

Thay đổi phá vỡ tương thích

  • cli · eslint-config — Nâng @tasco/eslint-config lên ESLint 10 và yêu cầu Node >=20.19.0. App scaffold mới cũng dùng ESLint 10.

  • composables · ui · utils — CTable có drawer Thiết lập: đổi thứ tự/ẩn cột, sắp xếp mặc định, lưu localStorage theo từng bảng

    • @tasco/ui — CTable: nút ⚙ (show-column-setting) mở drawer Thiết lập thay cho popover ẩn/hiện cột. Tab Hiển thị cột: kéo thả hoặc nút ↑↓ (bàn phím/cảm ứng), công tắc Hiện/Ẩn; cột fixed chỉ đổi thứ tự trong nhóm, cột title: '' giữ nguyên vị trí. Tab Khác: sắp xếp mặc định (cột có sorter + chiều). Làm việc trên bản nháp (Lưu lại / Đặt lại, đóng là bỏ nháp), focus trả về nút Thiết lập khi đóng.
      • Prop settingsKey: lưu localStorage['tasco:table:<settingsKey>'] — mỗi bảng một khoá nên nhiều CTable trên một trang không ghi đè nhau; thiết lập cũ tự hợp nhất khi columns đổi; dữ liệu hỏng/khác version bị bỏ qua.
      • v-model:settings (TableSettings) thay cho v-model:hiddenColumns (chưa phát hành).
      • Lưu sắp xếp mặc định mới → CTable phát @change (action sort, về trang 1) như khi bấm tiêu đề cột. change nay là emit khai báo của CTable (có type) nên lỗi từ handler async (vd useTable.onChange) đi qua error handler của Vue.
      • Khi dùng thiết lập, CTable điều khiển sortOrder của cột sorter để chỉ báo khớp sắp xếp đang áp dụng (trừ khi trang tự khai báo sortOrder, dùng sorter.multiple hoặc có cột nhóm).
      • settingsKey/settings mà cột cấu hình được thiếu key lẫn dataIndex → ném lỗi [@tasco/ui].
      • Export type TableSettings, TableSort, TableSortOrder. Footer drawer Lọc đổi thành hai nút chia đôi cho đồng bộ.
    • @tasco/composables — useTable: options.settingsKey (cùng khoá với CTable) — lần load đầu theo sắp xếp mặc định đã lưu. onChange nhận đúng dạng sorter của a-table: mảng (nhiều cột) lấy cột đầu, dataIndex lồng nối ., bỏ sắp xếp (không có order) thì xoá sortField/sortOrder — trước đây vẫn gửi sortField.
    • @tasco/utils: getTableSettings / setTableSettings / clearTableSettings (storage truyền vào được; storage bị chặn thì không ném lỗi), parseTableSettings (kiểm tra dữ liệu không tin cậy), tableSettingsKeyOf, isTableSortOrder; type TableSettings, TableSort, TableSortOrder, TableSettingsStorage.
  • nuxt-layer-base · theme · ui — CTable thành khung trang danh sách chuẩn; bật locale vi_VN của antdv cho toàn app

    • @tasco/ui — CTable: bọc bảng trong khung card (dựa trên CCard) có tiêu đề (title / slot #title) và toolbar tuỳ chọn: show-create (@create), show-search + v-model:searchValue (@search khi Enter hoặc bấm xoá), show-filter + filter-count (chấm đỏ, @filter), show-export (@export), show-reload (@reload, icon xoay khi loading), show-column-setting (nút Thiết lập — xem changeset drawer thiết lập), slot #toolbar; thêm striped, collapsible, defaultOpen, borderless, type. Phân trang mặc định "Tổng số dòng N" + chọn số dòng/trang + cỡ default (ghi đè từng key qua pagination, false để tắt). Cột vẫn do trang định nghĩa; props/slot/sự kiện a-table forward nguyên vẹn.
      • Thay đổi hành vi: class/style gắn vào khung ngoài (trước đây ở a-table); slot #title là tiêu đề khung (không còn forward làm title của a-table); bảng không có tiêu đề/toolbar vẫn nằm trong khung viền — dùng borderless khi đặt CTable bên trong card khác.
      • Thêm dependency @tabler/icons-vue cho icon toolbar.
    • @tasco/nuxt-layer-base: app.vue truyền locale vi_VN của antdv cho <a-config-provider> (phân trang "/ trang", Empty, Modal, Popconfirm…). Dự án tự override app.vue cần thêm :locale tương ứng.
    • @tasco/theme (base.css): sửa khoảng trống 16px ngay dưới header của bảng có scroll/sticky (padding ô áp nhầm vào hàng đo cột ant-table-measure-row); vạch ngăn cột ở header rõ hơn.
  • nuxt-layer-base — Gỡ phần OIDC còn sót trong @tasco/nuxt-layer-base

    Đăng nhập OIDC đã gỡ từ 1.2.0, sau đó layer đăng nhập bằng form first-party qua IAM. Phần OIDC giữ lại không còn tác dụng: session IAM không lưu refresh token nên nhánh refresh ở /api/** không bao giờ chạy, và không có phiên IdP nào để đăng xuất.

    • Xoá server/utils/oidc.ts. Nitro không còn auto-import getOidcMetadata, generatePkce, randomState, refreshAccessToken, mapUserInfo và các type Oidc* sang app.
    • Xoá runtimeConfig.oidc, nên env NUXT_OIDC_* (kể cả NUXT_OIDC_MOCK) không còn tác dụng.
    • /api/**: access token hết hạn thì bỏ token để backend trả 401, không thử refresh.
    • /auth/logout: xoá session rồi redirect về đường dẫn nội bộ, không chuyển tới end_session_endpoint của IdP.
    • TascoSession bỏ field refreshToken.
    • App tự dùng các hàm OIDC auto-import, đọc useRuntimeConfig().oidc hoặc truyền refreshToken vào setTascoSession cần bỏ phần đó. Trong monorepo không có nơi nào dùng; app sản phẩm xem Migration Guide v1 → v2. Đánh major ở Tasco vì đây là API đã phát hành cho team sản phẩm (RFC 0002).
  • theme · ui — Sửa tương phản WCAG AA (≥ 4.5:1) cho token primary (theme tối) và link (theme sáng)

    • @tasco/theme — token
      • Theme tối: primary #4f76d1#7090dc, primaryHover #7b98df#94ade6.
        • Chữ primary trên primary-soft (vd CTag color="primary") 3.44 → 4.77:1; trên surface 3.76 → 5.21:1; trên surface-elevated (modal/popover) 3.44 → 4.77:1.
        • gradientPrimary đổi thành #35549e → #4466b8, để chữ trắng của CButton primary ≥ 4.5:1 dọc cả dải, kể cả khi hover.
      • Theme sáng: link #1576f4#1068d6. Nút "Đặt lại" / "Xoá tất cả" của CTable trên surface từ 4.26 → 5.29:1; đạt ≥ 4.5:1 cả trên surface-muted, pagelink-soft.
    • @tasco/theme — base.css (chỉ theme tối)
      • Ở theme tối, primary là màu chữ sáng nên không làm nền cho chữ trắng được nữa (chỉ ~3:1). Các nền primary có chữ trắng chuyển sang --tasco-color-primary-active (≥ 7:1):
        • header bảng; cột đang sort/hover phủ thêm lớp tối;
        • header CCard type="primary";
        • nút primary thuần antdv (Modal.confirm của useConfirm, nút Gửi của CChat; không áp cho CButton);
        • ô ngày đang chọn của DatePicker / RangePicker.
      • Vì sao phải override: antdv suy màu nền từ seed colorPrimary (trộn 85% với #141414), và không có seed nào đạt ≥ 4.5:1 cho cả chữ primary lẫn chữ trắng trên nền primary. Các chỗ antdv khác còn nền colorPrimary + chữ trắng mà core chưa dùng (vd Steps, Radio solid, Tag checkable, Menu theme dark, Badge.Ribbon) chưa được override, tương phản khoảng 4:1.
      • ::selection theme tối: chữ primary-activeprimary-hover (2.07 → 6.66:1 trên primary-soft).
    • @tasco/ui — CChatMessage: bubble của người dùng dùng nền --tasco-color-primary-active thay cho primary. Theme sáng hơi đậm hơn (#182544, chữ trắng 15:1); theme tối #35549e (7.21:1).
    • Dự án nào đặt chữ trắng lên var(--tasco-color-primary) trong CSS riêng cần đổi sang --tasco-color-primary-active, nếu không theme tối chỉ còn ~3:1.
  • theme · ui — Sửa tương phản WCAG AA (≥ 4.5:1) cho màu semantic, accent, nút danger và chữ màu antdv tự vẽ ở theme tối; thêm token màu chữ accentText, successText, warningText

    • @tasco/theme — token mới: accentText, successText, warningText (--tasco-color-{accent,success,warning}-text), là màu chữ/icon trên nền sáng.
      • Theme sáng: accentText #9e5d02, successText #087e02, warningText #a55904. Đạt ≥ 4.5:1 trên nền *-soft, surface, pageinfo-soft (hàng đang chọn của bảng). Ở theme tối, ba token này trùng màu gốc.
      • Vì sao phải tách:
        • accent #ff9800 là màu nền thương hiệu; dùng làm chữ chỉ đạt 1.93:1 trên accent-soft.
        • successwarning còn là seed antdv. Seed tối làm tint nền antdv sinh ra bị xỉn (vd colorWarningBg #fff7e6#e6dfcf, ảnh hưởng Alert, Tag), nên giữ nguyên giá trị seed.
    • @tasco/theme — token đổi giá trị
      • Theme sáng: error #ee0033#d71431.
        • Chữ trên error-soft 4.08 → 4.73:1, trên surface 4.48 → 5.21:1.
        • Chữ trắng trên nền colorError của antdv (Badge, hover item danger của Dropdown) 4.48 → 5.21:1.
        • Tint nền antdv giữ gần như cũ (colorErrorBg #ffe6e7#ffe6e6).
      • gradientDanger (cả hai theme) → linear-gradient(135deg, #cf1444, #d71431). Chữ trắng của CButton danger ≥ 5.21:1 dọc cả dải, ≥ 4.56:1 khi hover (brightness(1.08)). Trước đây theme sáng 3.67 → 4.48:1, theme tối 2.77 → 3.67:1.
    • @tasco/theme — base.css
      • CSideNav, item hover/đang chọn (kể cả submenu cha lúc thu gọn): giữ nền cam accent, chữ và icon đổi từ trắng sang navy --tasco-color-sidebar-bottom. Theme sáng 2.16 → 7.03:1, theme tối 1.94 → 8.90:1.
      • Nút primary danger thuần antdv, cả hai theme (vd okButtonProps: { danger: true }): nền --tasco-gradient-danger, hover brightness(1.08) như CButton danger. Theme tối 3.61 → 5.21:1; hover theme sáng 3.83 → 4.56:1.
      • Theme tối, chữ màu antdv tự trộn từ seed:
        • Trang đang chọn của Pagination (CTable) và tab đang chọn dùng primary (4.03 → 5.21:1 trên surface); hover dùng primary-hover.
        • Item danger của Dropdown (menu thao tác dòng): chữ dùng error (4.14 → 5.39:1), hover nền gradient-danger (3.61 → 5.21:1).
      • Chưa phủ (core chưa dùng hoặc không nằm trong đợt này):
        • Theme tối: chữ lỗi Form trong Modal/Drawer (4.14:1); số của Badge (3.61:1); nền colorPrimary chữ trắng của Steps, Radio solid, Tag checkable (4.04:1); chữ của Radio.Button và item Dropdown đang chọn.
        • Theme sáng: chữ colorWarning/colorSuccess do antdv vẽ (Form warning/success: 3.19 và 3.68:1); chữ hover của nút danger viền (4.24:1).
    • @tasco/ui: CTag accent/success/warning, icon accent/success/warning của CStatistic, trend tăng của CStatistic và chữ trạng thái hoạt động của CStatus chuyển sang biến *-text. Chấm của CStatus vẫn giữ màu gốc, vì chấm là đồ hoạ, chỉ cần ≥ 3:1.
    • Dự án cần làm gì:
      • CSS riêng đang đặt chữ var(--tasco-color-{accent,success,warning}) trên nền sáng → đổi sang biến *-text.
      • Chữ trắng trên var(--tasco-color-accent) → đổi sang var(--tasco-color-sidebar-bottom).
      • Bảng cặp màu an toàn nằm ở docs/guide/theming.md.

Tính năng mới

  • ai — Package mới @tasco/ai — nền tảng AI dùng chung (Giai đoạn 2 lộ trình AI tooling):

    • useAiChat (client): chat streaming SSE qua BFF, state messages/status/error, abort/reset.
    • createAiChatProxy (@tasco/ai/server): Nitro handler proxy tới AI gateway OpenAI-compatible; whitelist body, allowlist model, giới hạn payload, key chỉ tồn tại server-side.
    • Parser SSE + sanitizeChatRequest tách rời, test đầy đủ (coverage gate 90%).
  • ai — Thêm prompt library (@tasco/ai/prompts: definePrompt/renderPrompt validate biến, bộ prompt chuẩn tascoAssistant/summarize/extractJson có version) và eval harness (@tasco/ai/eval: assertion tất định contains/regex/json-valid/max-lines, runner gọi gateway OpenAI-compatible, report). Golden cases ở evals/cases.ts, chạy bằng pnpm --filter @tasco/ai eval; CI thêm job ai-eval chạy khi MR đụng packages/ai/** (allow_failure, cần AI_GATEWAY_URL/AI_GATEWAY_KEY).

  • cli — Scaffold thêm CI/CD deploy đầy đủ, dựa trên hạ tầng Docker + GitLab Runner shell executor đã triển khai thực tế cho một sản phẩm Tasco: Dockerfile đa stage (check/build/runtime), docker-compose.yml/docker-compose.dev.yml/ docker-compose.prod.yml, scripts/deploy-prod.sh, .dockerignore, và .gitlab-ci.yml mới (stage check → build → deploy, tự deploy nhánh dev lên máy dev, prod deploy thủ công qua script). Thêm script check (lint+typecheck) vào package.json scaffold. Docs: mục mới CI/CD & Deploy.

  • cli — Scaffold thêm sẵn CLAUDE.md cho project mới: context chuẩn cho AI coding assistant (quy ước C*/useApi/definePageMeta, gotchas CSR/theme/cookie/registry) — project tạo từ create-tasco-app dùng được Claude Code/Cursor đúng chuẩn framework ngay từ đầu.

  • cli — Scaffold thêm cấu hình AI tooling: .mcp.json (GitLab self-host + Figma) và .claude/settings.json guardrail (deny đọc .env/~/.npmrc, deny force-push, allow sẵn các lệnh lint/typecheck/build/git read-only).

  • cli · mcp — Thêm package @tasco/mcp — MCP server nội bộ phơi tri thức framework (component C*, design token, docs, package registry) cho AI của team sản phẩm qua 5 tool (list_components, get_component, list_tokens, search_docs, list_packages). Tri thức sinh lúc build vào data/*.json nên server chạy standalone trong repo sản phẩm. Scaffold @tasco/cli thêm sẵn server "tasco" vào .mcp.json.

  • composables · ui — CTable có bộ lọc dựng sẵn: drawer chứa form lọc + thanh điều kiện lọc phía trên bảng; useTable giữ bộ lọc khi đổi trang

    • @tasco/ui — CTable: prop filterFields (trang khai báo trường lọc như columns: input, select (options, multiple), date, dateRange, custom qua slot #filterField; format để tuỳ chữ hiển thị) + v-model:filterValues. Bấm Lọc mở drawer làm việc trên bản nháp (Áp dụng / Đặt lại; đóng là bỏ nháp); điều kiện đang áp dụng hiện thành thẻ ngay trên bảng, bấm ✕ bỏ từng điều kiện hoặc Xoá tất cả. Chấm đỏ của nút Lọc tự đếm theo số điều kiện; focus trả về nút Lọc khi đóng drawer / bỏ thẻ cuối. Export type TableFilterField, TableFilterOption, TableFilterValues.
      • Không truyền filterFields → hành vi cũ giữ nguyên (@filter, filterCount).
      • Trường type: 'custom' thiếu slot #filterField → ném lỗi [@tasco/ui] khi dựng bảng.
    • @tasco/ui — CTag: thêm closable + closeText (nhãn đọc màn hình), sự kiện close; nút ✕ là <button> thao tác được bằng bàn phím.
    • @tasco/composables — useTable: thêm onFilter(values) (bind @update:filter-values, về trang 1 rồi tải lại), filterValues (bind :filter-values) và options.filters (bộ lọc mặc định). query.filters = filter cột của a-table gộp với bộ lọc form (trùng key thì form thắng) — trước đây onChange ghi đè query.filters bằng filter cột mỗi lần đổi trang.
  • nuxt-layer-base · ui — Thêm component chat AI: CChat (panel controlled — list auto-scroll + ô soạn, Enter gửi/Shift+Enter xuống dòng, chặn gửi giữa lúc gõ IME) và CChatMessage (bong bóng message + typing indicator). Thuần trình bày, wire với useAiChat của @tasco/ai qua props/emit.

Sửa lỗi và cải tiến

  • clicreate-tasco-app và README của template hướng dẫn bỏ qua đăng nhập khi dev bằng NUXT_AUTH_MOCK=true thay cho NUXT_OIDC_MOCK=true

    Không đổi API và runtime. Layer đăng nhập qua IAM, cờ bỏ qua đăng nhập là runtimeConfig.auth.mock (NUXT_AUTH_MOCK), khớp .env.example, CLAUDE.md của template và docs Auth. NUXT_OIDC_MOCK không bỏ qua đăng nhập (phần OIDC còn sót đọc cờ này cũng đã gỡ khỏi layer), nên làm theo hướng dẫn cũ vẫn phải đăng nhập IAM thật.

    • Lời nhắc sau khi scaffold và mục "Bắt đầu" trong README template: cấu hình IAM/backend hoặc NUXT_AUTH_MOCK=true.
    • README template: mục "Có sẵn từ layer" mô tả auth là đăng nhập IAM qua BFF thay vì OIDC.
  • cli · nuxt-layer-base — Sửa app scaffold bằng create-tasco-app fail pnpm installpnpm typecheck khi đặt ngoài monorepo (pnpm 11+)

    Không đổi API và runtime. Lỗi có sẵn từ 1.3.0, làm script check, stage check của Dockerfile và job check trong .gitlab-ci.yml của app mới fail ngay từ đầu. Monorepo không gặp vì có allowBuildspnpm-workspace.yaml gốc và @types/node được hoist từ package khác.

    • pnpm install thoát lỗi ERR_PNPM_IGNORED_BUILDS (@tasco/cli): pnpm 11+ chặn build script của dependency và fail khi còn package chưa được quyết định (esbuild, core-js). Template thêm pnpm-workspace.yaml khai báo allowBuilds (esbuild, @parcel/watcher được chạy; core-js bị chặn), Dockerfile copy file này trước pnpm install --frozen-lockfile. package.json pin packageManager cùng bản pnpm của core, để corepack trong Docker/CI cài đúng bản thay vì bản mặc định của corepack.
    • pnpm typecheck báo TS2307 node:crypto và TS2591 Buffer (@tasco/nuxt-layer-base): app typecheck thẳng source server/utils/{session,iam,oidc}.ts của layer nhưng không có type Node. Layer khai báo @types/node trong dependencies và các file dùng API Node có /// <reference types="node" />, nên app không cần tự cài @types/node. App tạo từ template cũ cũng hết lỗi sau khi nâng layer.
    • App tạo từ template cũ vẫn fail pnpm install với pnpm 11+ cho tới khi chép pnpm-workspace.yaml, field packageManager và dòng COPY trong Dockerfile từ template mới (sau đó chạy pnpm install để ghi bản pnpm vào pnpm-lock.yaml).
    • Test template của @tasco/cli (pnpm --filter @tasco/cli test) chặn tái phát: template thiếu allowBuilds/packageManager, Dockerfile không copy pnpm-workspace.yaml, hoặc server util dùng API Node mà không tham chiếu type Node.
  • cli — Template scaffold khai báo engines.node khớp yêu cầu của Nuxt 4.5 (^22.19.0 || ^24.11.0 || >=26.0.0)

    Không đổi API và runtime. Template cài nuxt@^4.0.0 nên app mới nhận Nuxt 4.5, bản chỉ hỗ trợ Node 22.19+, 24.11+ và 26+. Giá trị cũ >=20.19.0 ghi sai là chạy được trên Node 20, 23 và 25.

    • pnpm không chặn cài đặt theo engines.node của chính project khi chưa bật engine-strict, nên đổi này không làm fail pnpm install hay Docker build (node:24-alpine).
    • App tạo từ template cũ nên sửa engines.node trong package.json theo cùng giá trị.
  • nuxt-layer-base — Bổ sung CFilterBar vào khai báo GlobalComponents trong tasco.d.ts — component đã được đăng ký global qua @tasco/ui nhưng thiếu type, khiến template dùng <CFilterBar> không có gợi ý type/IntelliSense.

  • nuxt-layer-base — README và comment của @tasco/nuxt-layer-base mô tả đúng đăng nhập IAM thay cho OIDC

    Không đổi API và runtime.

    • README: auth là form đăng nhập first-party, BFF đổi credential lấy token IAM. Liệt kê đúng các route /auth/* (bỏ /auth/callback không tồn tại). Khối env dùng NUXT_AUTH_BASE_URLNUXT_AUTH_MOCK thay cho NUXT_OIDC_*NUXT_OIDC_MOCK. Access token hết hạn thì phải đăng nhập lại, vì session IAM không giữ refresh token.
    • nuxt.config.ts: comment của apiProxyTarget ghi tên env NUXT_API_PROXY_TARGET.
    • Plugin 02.auth.ts: bỏ comment nhắc OIDC là mặc định và callback ở /auth/callback.
  • nuxt-layer-base — Không publish file test trong server/ của @tasco/nuxt-layer-base

    Không đổi API và runtime. Trường files đang lấy cả thư mục server, nên tarball kèm các file server/utils/*.test.ts. Nuxt quét server/utils để auto-import, và app sản phẩm không cần các file này.

  • theme — Thêm Vitest cho @tasco/theme; test khoá tương phản WCAG AA của các cặp token mà component đang dùng

    Không đổi API và giá trị token.

    • pnpm --filter @tasco/theme test giờ chạy thật (vitest run --coverage).
    • Hàm thuần luminanceOf / contrastRatioOf tính tương phản theo WCAG 2.x (src/contrast.ts). Hiện chỉ test dùng nên chưa export ra package.
    • css-vars.test.ts đo trên cssVars('light')cssVars('dark'), yêu cầu ≥ 4.5:1 cho:
      • primary, primary-hover, link, text-muted trên các nền component đang dùng (primary-soft, surface, surface-elevated, surface-muted, page);
      • chữ trắng trên primary-active, gradient-primary, gradient-danger (theme sáng thêm primary và error); chữ sidebar-bottom trên accent (CSideNav);
      • toàn bộ bảng cặp AA trong docs/guide/theming.md: *-text của accent/success/warning và primary/error/info, trên nền soft và surface;
      • error trên surface-elevated (item danger của Dropdown); success-text và error trên info-soft (CStatus trong hàng đang chọn của bảng).
    • Cặp đang biết là chưa đạt được ghi thành ngoại lệ kèm lý do. Test khẳng định cặp đó vẫn dưới ngưỡng; khi sửa đạt, test sẽ đỏ để nhắc gỡ ngoại lệ và khoá cặp lại. Hiện còn một cặp: text-subtle trên surface (mô tả CEmpty), 2.54:1 ở theme sáng, 3.55:1 ở theme tối.
  • theme — Sửa tương phản WCAG AA cho chữ mục chưa chọn của CTopNav ở theme tối

    • @tasco/theme — base.css: mục chưa chọn của CTopNav (menu module ngang trên header CAppLayout) đổi màu chữ từ navy #253264 tô cứng sang --tasco-color-text.
      • Theme tối, trên surface #162033: 1.33 → 15.58:1. Trước đây chữ gần như chìm vào nền header.
      • Theme sáng: #10213f trên #ffffff, 12.22 → 16.01:1. Vẫn là tông navy, chỉ đậm hơn một nấc.
      • Mục đang chọn vẫn dùng primary + gạch chân. Màu hover không đổi, vì antdv vốn tô chữ hover bằng colorText (cùng giá trị text); hover vẫn được báo bằng gạch chân primary.
    • Test tương phản khoá thêm cặp text trên surface (≥ 4.5:1 ở cả hai theme). Không đổi API và giá trị token.
  • theme — Bật ESLint cho @tasco/themepnpm lint và CI giờ lint cả src/*.ts lẫn test Vitest src/*.test.ts của theme

    Không đổi API, giá trị token và file publish.

    • Trước đây package không có script lint nên Turbo bỏ qua theme khi chạy pnpm lint. Giờ đã thêm eslint.config.mjs theo mẫu @tasco/utils (base + noDirectAntdv) và script "lint": "eslint .".
    • noDirectAntdv khoá nguyên tắc theme không import ant-design-vue (giữ zero runtime dep).
    • Thêm devDependencies @tasco/eslint-config (workspace:*) và eslint (^10.10.0), cùng version với các package khác. Code hiện có đã đạt lint, không phải sửa.
  • ui — Sửa lỗi TS7022 ('name' implicitly has type 'any') ở bước vite:dts khi build, do vòng suy luận type của vue-tsc với v-for="(_, name) in $slots" trong CForm/CTable. Chuyển sang lặp useSlots() với type tường minh; hành vi forward slot (kể cả slotProps) giữ nguyên, có test kèm theo.

  • ui — Sửa index.d.ts của @tasco/ui lỗi TS2749 ($nextTick: nextTick); build Storybook không còn chạy plugin d.ts

    Không đổi API và runtime. Type của components giữ nguyên ý nghĩa.

    • d.ts publish hỏng: map components (install.ts) để TS tự suy luận nên d.ts in lại nguyên cây type từng component. Type của CTable chứa instance CButton (template ref), trong đó có typeof import('vue').nextTick. api-extractor (rollupTypes) viết lại thành $nextTick: nextTick (mất typeof), consumer đặt skipLibCheck: false gặp TS2749. Nay components khai báo type tường minh { CTable: typeof CTable, … }: index.d.ts hết lỗi và gọn từ 1815 xuống 1007 dòng.
    • Chốt chặn lúc build: vite.config.ts báo lỗi [@tasco/ui] khi d.ts sinh ra có typeof import(...), không để phát hành d.ts hỏng. Test/story không còn sinh d.ts.
    • Storybook (build Storybook fail): .storybook/main.ts bỏ plugin vite:dts kế thừa từ vite.config.ts. Trước đây Storybook bỏ build.lib nên plugin chạy api-extractor lại trên dist/index.d.ts, ghi đè file đó và đổ .d.ts vào storybook-static.
  • ui — Bỏ eslint-disable khỏi test của @tasco/ui; ngoại lệ lint cho stub antdv chuyển thành override có tên trong eslint.config.mjs

    Không đổi API, component và file publish.

    • 5 file test (CChat, CFilterBar, CForm, CTable, CTableFilterDrawer) bỏ dòng /* eslint-disable vue/one-component-per-file */ ở đầu file, đúng quy ước repo cấm eslint-disable. Directive trong CForm.test.ts vốn không dùng tới (ESLint báo "Unused eslint-disable directive").
    • Override tasco/ui-test-stubs chỉ tắt vue/one-component-per-file cho **/*.test.ts, vì file test gom nhiều stub antdv trong factory vi.mock('ant-design-vue'). Component .vue, story và code nguồn vẫn áp rule.
    • Stub ATextarea/AAlert trong CChat.test.ts khai báo default cho prop nên hết 2 cảnh báo vue/require-default-prop. pnpm --filter @tasco/ui exec eslint . --max-warnings 0 giờ sạch.