Giao diện
@digiforce-nc/utils
| Package | @digiforce-nc/utils |
| Depends on | (standalone - không phụ thuộc core package nào) |
1) Tổng quan
@digiforce-nc/utils là thư viện utility dùng chung xuyên suốt hệ sinh thái Digiforce - từ server, client, đến plugins. Package cung cấp các công cụ cơ bản: cấu trúc dữ liệu, registry pattern, mã hóa, template engine, xử lý ngày tháng, và nhiều hơn nữa.
Foundation package
Đây là package nền tảng nhất trong Digiforce - không phụ thuộc bất kỳ core package nào khác. Mọi thay đổi ở đây ảnh hưởng toàn bộ hệ thống.
2) Collections & Graphs
Toposort<T> - Sắp xếp topo
Sắp xếp topological cho dependency ordering - đảm bảo phần tử A được xử lý trước B nếu B phụ thuộc A:
typescript
import { Toposort } from '@digiforce-nc/utils';
const sorter = new Toposort<string>();
// Thêm phần tử với dependencies
sorter.add('middleware-auth', {
before: 'middleware-acl', // auth chạy trước acl
tag: 'auth',
});
sorter.add('middleware-acl', {
after: 'middleware-auth', // acl chạy sau auth
tag: 'acl',
});
sorter.add('middleware-logger', {
group: 'pre', // nhóm ưu tiên
tag: 'logger',
});
// Lấy thứ tự đã sắp xếp
const sorted = sorter.sort();
// ['middleware-logger', 'middleware-auth', 'middleware-acl']Ứng dụng trong Digiforce:
| Nơi sử dụng | Mục đích |
|---|---|
Application.middleware | Sắp xếp thứ tự middleware (thay thế array đơn giản của Koa) |
PluginManager | Xác định thứ tự load plugin theo dependency |
Database.migrator | Thứ tự chạy migration |
CollectionsGraph
Xây dựng dependency graph cho database collections:
typescript
import { CollectionsGraph } from '@digiforce-nc/utils';
const graph = new CollectionsGraph();
graph.addNode('orders', {
depends: ['users', 'products'],
});
graph.addNode('order_items', {
depends: ['orders', 'products'],
});
graph.addNode('users');
graph.addNode('products');
const loadOrder = graph.sort();
// ['users', 'products', 'orders', 'order_items']3) Registry<T> - Generic type-safe registry
Pattern dùng xuyên suốt Digiforce để đăng ký và truy xuất components theo tên:
typescript
import { Registry } from '@digiforce-nc/utils';
// Tạo registry cho validators
interface Validator {
validate(value: any): boolean;
message: string;
}
const validators = new Registry<Validator>();
// Đăng ký
validators.register('email', {
validate: (value) => /^[\w.-]+@[\w.-]+\.\w+$/.test(value),
message: 'Email không hợp lệ',
});
validators.register('phone-vn', {
validate: (value) => /^(0[3-9])\d{8}$/.test(value),
message: 'Số điện thoại Việt Nam không hợp lệ',
});
// Truy xuất
const emailValidator = validators.get('email');
emailValidator.validate('test@example.com'); // true
// Kiểm tra tồn tại
validators.has('email'); // true
// Lấy tất cả
const all = validators.getEntities();
// Map { 'email' => {...}, 'phone-vn' => {...} }Registry API:
| Method | Mô tả |
|---|---|
register(name, value) | Đăng ký giá trị với tên |
get(name) | Lấy giá trị theo tên (throw nếu không tìm thấy) |
has(name) | Kiểm tra tên đã đăng ký chưa |
getEntities() | Lấy toàn bộ Map |
unregister(name) | Gỡ đăng ký |
Pattern phổ biến
Registry được dùng cho: evaluators, field types, action handlers, auth types, storage adapters, data source drivers, và nhiều hơn nữa.
4) Merge - Deep object merging
Utility merge object sâu, xử lý đúng arrays và nested objects:
typescript
import { merge } from '@digiforce-nc/utils';
const defaults = {
server: {
port: 3000,
host: '0.0.0.0',
cors: {
origin: '*',
methods: ['GET', 'POST'],
},
},
database: {
dialect: 'sqlite',
logging: false,
},
};
const userConfig = {
server: {
port: 8080,
cors: {
origin: 'https://app.example.com',
},
},
database: {
dialect: 'postgres',
host: 'db.example.com',
},
};
const config = merge(defaults, userConfig);
// {
// server: {
// port: 8080, ← override
// host: '0.0.0.0', ← giữ nguyên
// cors: {
// origin: 'https://app.example.com', ← override
// methods: ['GET', 'POST'], ← giữ nguyên
// },
// },
// database: {
// dialect: 'postgres', ← override
// logging: false, ← giữ nguyên
// host: 'db.example.com', ← thêm mới
// },
// }5) Crypto - Encryption & Hashing
Utilities mã hóa và hash:
typescript
import { encryptPassword, verifyPassword, randomString } from '@digiforce-nc/utils';
// Hash password (bcrypt-based)
const hashed = await encryptPassword('my-secret-password');
// '$2b$10$...'
// Verify password
const isValid = await verifyPassword('my-secret-password', hashed);
// true
// Sinh chuỗi ngẫu nhiên
const token = randomString(32);
// 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6'Bảo mật
Không bao giờ lưu password dạng plain text. Luôn sử dụng encryptPassword() trước khi lưu vào database.
6) Date - dayjs với plugins
Re-export dayjs đã cấu hình sẵn các plugin phổ biến:
typescript
import { dayjs } from '@digiforce-nc/utils';
// Đã có sẵn: utc, timezone, relativeTime, customParseFormat, etc.
const now = dayjs();
const formatted = now.format('DD/MM/YYYY HH:mm'); // '08/04/2026 15:30'
const relative = now.subtract(2, 'hour').fromNow(); // '2 giờ trước'
const utc = dayjs.utc('2026-04-08').tz('Asia/Ho_Chi_Minh');7) Handlebars / LiquidJS - Template engines
Handlebars
typescript
import { Handlebars } from '@digiforce-nc/utils';
const template = Handlebars.compile(
'Chào {{name}}, bạn có {{count}} thông báo mới.'
);
const result = template({ name: 'Minh', count: 5 });
// 'Chào Minh, bạn có 5 thông báo mới.'LiquidJS
typescript
import { Liquid } from '@digiforce-nc/utils';
const engine = new Liquid();
const result = await engine.parseAndRender(
'{% if items.size > 0 %}Có {{ items.size }} sản phẩm{% else %}Giỏ hàng trống{% endif %}',
{ items: ['A', 'B', 'C'] },
);
// 'Có 3 sản phẩm'8) i18n - Internationalization
Helpers cho đa ngôn ngữ:
typescript
import { i18n } from '@digiforce-nc/utils';
// Đăng ký locale
i18n.addResources('vi-VN', 'app', {
'welcome': 'Chào mừng',
'items.count': '{{count}} mục',
});
// Sử dụng
i18n.t('app:welcome'); // 'Chào mừng'
i18n.t('app:items.count', { count: 5 }); // '5 mục'9) UID - Unique ID generation
typescript
import { uid } from '@digiforce-nc/utils';
const id = uid();
// 'clfx7z8a00001qwerty' - unique, URL-safe, thời gian-ordered10) URL - URL utilities
typescript
import { parseURL, buildURL } from '@digiforce-nc/utils';
const parsed = parseURL('https://api.example.com/users?page=1&size=20');
// { protocol: 'https', host: 'api.example.com', pathname: '/users', query: { page: '1', size: '20' } }
const url = buildURL('https://api.example.com', '/users', {
page: 1,
filter: { status: 'active' },
});
// 'https://api.example.com/users?page=1&filter[status]=active'11) runSQL - SQL execution
Utility thực thi SQL trực tiếp (dùng cho migration, seed):
typescript
import { runSQL } from '@digiforce-nc/utils';
await runSQL(sequelize, `
CREATE INDEX IF NOT EXISTS idx_users_email
ON users (email);
`);Raw SQL
runSQL thực thi SQL không qua ORM. Cẩn thận SQL injection - chỉ dùng cho migration/admin scripts, không dùng cho user input.
12) Lodash - Re-exported functions
Package re-export một số hàm lodash phổ biến:
typescript
import {
get, set, has,
pick, omit,
cloneDeep,
merge as lodashMerge,
debounce, throttle,
isEmpty, isPlainObject,
groupBy, keyBy, sortBy,
uniq, uniqBy, flatten,
} from '@digiforce-nc/utils';
// Sử dụng bình thường như lodash
const value = get(obj, 'deeply.nested.path', 'default');
const subset = pick(user, ['id', 'name', 'email']);
const unique = uniqBy(items, 'id');13) Schema - Formily JSON Schema
Re-export Schema từ @formily/json-schema cho form schema processing:
typescript
import { Schema } from '@digiforce-nc/utils';
const schema = new Schema({
type: 'object',
properties: {
username: {
type: 'string',
title: 'Tên đăng nhập',
required: true,
'x-component': 'Input',
},
email: {
type: 'string',
title: 'Email',
format: 'email',
'x-component': 'Input',
},
},
});14) Code example tổng hợp
typescript
import {
Registry,
Toposort,
merge,
dayjs,
uid,
get,
pick,
isEmpty,
} from '@digiforce-nc/utils';
// --- Registry cho data processors ---
interface DataProcessor {
process(data: any[]): any[];
}
const processors = new Registry<DataProcessor>();
processors.register('sort-by-date', {
process: (data) => data.sort((a, b) =>
dayjs(b.createdAt).valueOf() - dayjs(a.createdAt).valueOf()
),
});
processors.register('add-uid', {
process: (data) => data.map((item) => ({
...item,
uid: item.uid || uid(),
})),
});
// --- Toposort cho pipeline ---
const pipeline = new Toposort<string>();
pipeline.add('add-uid', { tag: 'prepare' });
pipeline.add('sort-by-date', { after: 'add-uid', tag: 'sort' });
const steps = pipeline.sort(); // ['add-uid', 'sort-by-date']
let data = [
{ name: 'Item B', createdAt: '2026-03-15' },
{ name: 'Item A', createdAt: '2026-04-01' },
];
for (const step of steps) {
const processor = processors.get(step);
data = processor.process(data);
}
// data giờ có uid và đã sắp xếp theo ngày
// --- Merge cấu hình ---
const defaultConfig = { pageSize: 20, sort: 'createdAt' };
const userConfig = { pageSize: 50 };
const finalConfig = merge(defaultConfig, userConfig);
// { pageSize: 50, sort: 'createdAt' }15) Tổng kết modules
| Module | Export chính | Mục đích |
|---|---|---|
| Toposort | Toposort<T> | Sắp xếp topological |
| CollectionsGraph | CollectionsGraph | Dependency graph cho collections |
| Registry | Registry<T> | Type-safe registry pattern |
| merge | merge() | Deep object merge |
| crypto | encryptPassword, verifyPassword, randomString | Mã hóa & hash |
| dayjs | dayjs | Date/time với plugins |
| Handlebars | Handlebars | Template engine |
| LiquidJS | Liquid | Template engine |
| i18n | i18n | Internationalization |
| uid | uid() | Unique ID generation |
| URL | parseURL, buildURL | URL utilities |
| runSQL | runSQL() | Raw SQL execution |
| lodash | get, set, pick, omit, ... | Utility functions |
| Schema | Schema | Formily JSON Schema |