Bỏ qua, đến nội dung

@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/utilsthư 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ụngMục đích
Application.middlewareSắp xếp thứ tự middleware (thay thế array đơn giản của Koa)
PluginManagerXác định thứ tự load plugin theo dependency
Database.migratorThứ 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:

MethodMô 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-ordered

10) 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

ModuleExport chínhMục đích
ToposortToposort<T>Sắp xếp topological
CollectionsGraphCollectionsGraphDependency graph cho collections
RegistryRegistry<T>Type-safe registry pattern
mergemerge()Deep object merge
cryptoencryptPassword, verifyPassword, randomStringMã hóa & hash
dayjsdayjsDate/time với plugins
HandlebarsHandlebarsTemplate engine
LiquidJSLiquidTemplate engine
i18ni18nInternationalization
uiduid()Unique ID generation
URLparseURL, buildURLURL utilities
runSQLrunSQL()Raw SQL execution
lodashget, set, pick, omit, ...Utility functions
SchemaSchemaFormily JSON Schema