
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@docyrus/app-utils
Advanced tools
Utility functions for frontend applications using Docyrus as backend
Framework-agnostic utility functions for frontend applications using Docyrus as backend. Provides date/number formatting, template compilation (Handlebars + JSONata), and a comprehensive set of expression helpers — all configured from tenant preferences.
Y-m-d, H:i:s, etc.)HH:MM, decimal hours, or human-readable wordsnpm install @docyrus/app-utils
pnpm add @docyrus/app-utils
| Package | Version | Required |
|---|---|---|
@docyrus/api-client | >= 0.1.0 | Yes (for getTenantPreferences) |
handlebars | >= 4.7.0 | Yes (for template engine) |
jsonata | >= 2.0.0 | Yes (for formula evaluation) |
date-fns | >= 3.0.0 | Yes (for JSONata date helpers) |
import {
getTenantPreferences,
createDateUtils,
createNumberUtils,
createTemplateEngine
} from '@docyrus/app-utils';
// 1. Fetch tenant preferences
const preferences = await getTenantPreferences(apiClient);
// 2. Create utility instances
const dateUtils = createDateUtils({
preferences,
userTimezone: currentUser.timeZone?.id // e.g. 'Europe/Istanbul'
});
const numberUtils = createNumberUtils({ preferences });
// 3. Create template engine (optional)
const engine = createTemplateEngine({
dateUtils,
numberUtils,
user: currentUser,
extraJsonataBindings: { hasRole }
});
// 4. Use utilities
dateUtils.formatDate('2024-01-15'); // '15.01.2024' (tenant format)
numberUtils.formatNumber(1234.5); // '1.234,50' (tenant locale)
await engine.compileFormula('$sum(items.price)', data);
createAppConfigClient(client, appId)Manages the single JSON configuration object for an app (1:1 relationship — no config IDs).
import { createAppConfigClient } from '@docyrus/app-utils';
const config = createAppConfigClient(apiClient, appId);
config.get()Returns the app's configuration. Throws 404 if none exists.
const appConfig = await config.get();
// { id: '...', data: { theme: 'dark', ... }, status: 1, tenant_app_id: '...', ... }
config.upsert(body)Creates the config if it doesn't exist, or updates it if it does.
const updated = await config.upsert({
data: { theme: 'dark', sidebar: { collapsed: false } },
status: 1
});
config.remove()Hard deletes the config (irreversible).
await config.remove();
createDataViewClient(client, appId)Manages saved view configurations for data source tables (1:many). Views define columns, filters, sorting, color rules, and quick-filter shortcuts.
import { createDataViewClient } from '@docyrus/app-utils';
const dataViews = createDataViewClient(apiClient, appId);
dataViews.list(params?)Returns all non-archived views. Optionally filter by data source.
const views = await dataViews.list();
const filtered = await dataViews.list({ dataSourceId: 'ds-uuid' });
dataViews.get(viewId)Returns a single non-archived view by ID.
const view = await dataViews.get('view-uuid');
dataViews.create(body)Creates a new data view. name and tenant_data_source_id are required.
const view = await dataViews.create({
name: 'High Priority',
tenant_data_source_id: 'ds-uuid',
columns: { visible: ['title', 'priority', 'assignee'] },
filters: { priority: 'high' },
sort: { field: 'due_date', direction: 'asc' },
color: '#E74C3C',
icon: 'alert-triangle',
is_default: false,
sort_order: 2
});
dataViews.update(viewId, body)Partially updates a view. Use archived: true to soft-delete.
await dataViews.update('view-uuid', { name: 'Renamed View' });
await dataViews.update('view-uuid', { archived: true }); // soft-delete
dataViews.remove(viewId)Hard deletes a view (irreversible).
await dataViews.remove('view-uuid');
getTenantPreferences(client)Fetches tenant preferences from the Docyrus API.
import { getTenantPreferences } from '@docyrus/app-utils';
const preferences = await getTenantPreferences(apiClient);
// { date_format: 'd.m.Y', decimal_separator: ',', thousand_separator: '.', ... }
Parameters:
client — A configured RestApiClient from @docyrus/api-clientReturns: Promise<TenantPreferences>
createDateUtils(config)Creates date formatting utilities configured from tenant preferences.
import { createDateUtils } from '@docyrus/app-utils';
const dateUtils = createDateUtils({
preferences,
userTimezone: 'Europe/Istanbul' // defaults to 'UTC'
});
dateUtils.formatDate(date, options?)Formats a date using the tenant's date_format (default Y-m-d).
dateUtils.formatDate('2024-06-15'); // uses tenant format
dateUtils.formatDate(new Date(), { format: 'd/m/Y' }); // custom format
dateUtils.formatDate('2024-06-15', { timezone: 'US/Eastern' }); // custom timezone
dateUtils.formatDateTime(date, options?)Formats a datetime using the tenant's date_time_format (default Y-m-d H:i:s). Handles timezone-aware parsing — appends Z to strings without a timezone suffix.
dateUtils.formatDateTime('2024-06-15T14:30:00'); // parses as UTC
dateUtils.formatDateTime('2024-06-15T14:30:00+03:00'); // respects offset
dateUtils.formatDateLong(date, options?)Formats a date using the tenant's long_date_format (default Y-m-d H:i:s).
dateUtils.toUserTimezone(date)Converts a date to the user's timezone without formatting.
const localDate = dateUtils.toUserTimezone('2024-06-15T12:00:00Z');
Format strings use PHP date format syntax (via php-date-formatter):
| Token | Output | Example |
|---|---|---|
Y | 4-digit year | 2024 |
m | Month (01-12) | 06 |
d | Day (01-31) | 15 |
H | Hours 24h (00-23) | 14 |
i | Minutes (00-59) | 30 |
s | Seconds (00-59) | 00 |
D | Short day name | Sat |
l | Full day name | Saturday |
F | Full month name | June |
createNumberUtils(config)Creates number formatting utilities configured from tenant preferences.
import { createNumberUtils } from '@docyrus/app-utils';
const numberUtils = createNumberUtils({ preferences });
numberUtils.formatNumber(value, options?)Formats a number using the tenant's locale, separators, and precision.
numberUtils.formatNumber(1234567.89);
// With tenant settings: thousand_separator='.', decimal_separator=',', decimal_precision=2
// → '1.234.567,89'
// Override per call:
numberUtils.formatNumber(1234.5, {
decimalPrecision: 3,
decimalSeparator: '.',
thousandSeparator: ','
});
// → '1,234.500'
Formatting logic:
thousandSeparator and decimalSeparator are set → manual formatting with regextoLocaleString() with the tenant's localethousandSeparator: '' to disable groupingformatNumberToWords(value, options?)Converts a number to words with currency labels. Standalone function (no config needed).
import { formatNumberToWords } from '@docyrus/app-utils';
formatNumberToWords(1234.56);
// → 'BİNİKİYÜZOTUZDÖRT TÜRK LİRASI ELLİALTI KURUŞ'
formatNumberToWords(1234.56, { lang: 'EN', currency: 'USD' });
// → 'ONE THOUSAND TWO HUNDRED THIRTY FOUR DOLAR FIFTY SIX CENT'
formatNumberToWords(1234.56, { lang: 'TR', currency: 'EUR', useSpaces: true });
// → 'BİN İKİ YÜZ OTUZ DÖRT EURO ELLİ ALTI CENT'
Options:
| Option | Type | Default | Description |
|---|---|---|---|
lang | 'TR' | 'EN' | 'TR' | Word language |
currency | 'TRY' | 'EUR' | 'USD' | 'GBP' | 'JPY' | 'TRY' | Currency labels |
useSpaces | boolean | false | Add spaces between word groups |
Standalone functions for formatting durations (in seconds).
import {
formatDurationAsTime,
formatDurationAsHours,
formatDurationAsWords
} from '@docyrus/app-utils';
formatDurationAsTime(5461); // → '01:31'
formatDurationAsHours(5461); // → '1.52'
formatDurationAsHours(5461, 1); // → '1.5'
formatDurationAsWords(5461); // → '1 hr 31 mins'
formatDurationAsWords(45); // → '0 mins'
createTemplateEngine(config?)Creates a template engine combining Handlebars (with async support) and JSONata formula evaluation.
import { createTemplateEngine } from '@docyrus/app-utils';
const engine = createTemplateEngine({
dateUtils, // from createDateUtils()
numberUtils, // from createNumberUtils()
user: currentUser, // injected as metadata.user in formulas
extraJsonataBindings: { hasRole } // additional JSONata bindings
});
Each createTemplateEngine call creates an isolated Handlebars instance, so multiple engines (e.g., different tenants) won't conflict.
engine.compileTpl(templateString)Preprocesses and compiles a Handlebars template. Returns an async function.
const tpl = engine.compileTpl('Hello {{name}}, total: {{formatNumber amount}}');
const html = await tpl({ name: 'John', amount: 1234.5 });
// → 'Hello John, total: 1.234,50'
Template preprocessing:
{{formula $expr}} → wraps expression in quotes for the formula helper{{#if $expr}} → converts to {{#if (formula '$expr')}} for JSONata evaluation{{<content}} → strips HTML tags from inline contentengine.compileFormula(expression, data?)Evaluates a JSONata expression with all helpers bound. Injects metadata.user from config.
const total = await engine.compileFormula('$sum(items.price)', { items });
const greeting = await engine.compileFormula('"Hello " & name', { name: 'World' });
engine.jsonataHelpersDirect access to the full bindings object (all built-in helpers + extraJsonataBindings).
The following helpers are registered automatically when using compileTpl:
| Helper | Usage | Description |
|---|---|---|
formatDate | {{formatDate dateField format="d/m/Y"}} | Format date (uses tenant date_format by default) |
formatDateTime | {{formatDateTime dateField}} | Format datetime (uses tenant date_time_format) |
formatNumber | {{formatNumber amount decimalPrecision=2}} | Format number with tenant settings |
formatNumberToWords | {{formatNumberToWords amount lang="TR" currency="TRY"}} | Number to words |
formatDurationAsTime | {{formatDurationAsTime seconds}} | Duration as HH:MM |
formatDurationAsWords | {{formatDurationAsWords seconds}} | Duration as X hrs Y mins |
formatDurationAsHours | {{formatDurationAsHours seconds decimalPrecision=1}} | Duration as decimal hours |
| Helper | Usage | Description |
|---|---|---|
formula | {{formula "$sum(items.price)"}} or {{#formula}}$expr{{/formula}} | Evaluate JSONata expression |
path | {{#path "$.items[0]"}}...{{/path}} | JSONPath query — sets context to result |
repeat | {{#repeat 5}}...{{/repeat}} | Repeat block N times |
sum | {{sum items "price"}} or {{sum 1 2 3}} | Sum array field or values |
json | {{json data}} | Pretty-print JSON |
All helpers are available inside compileFormula and {{formula}} blocks. They are also exported as jsonataHelpers for direct use.
import { jsonataHelpers } from '@docyrus/app-utils';
All date functions accept strings or Date objects. Returns null for null/empty input.
Formatting:
formatDate, formatDistance, formatDistanceStrict, formatDistanceToNow, formatDistanceToNowStrict, formatDuration, formatISO, formatRelative
Arithmetic:
addYears, addMonths, addWeeks, addDays, addHours, addMinutes, addSeconds, addMilliseconds, subYears, subMonths, subWeeks, subDays, subHours, subMinutes, subSeconds, subMilliseconds
Comparison:
isAfter, isBefore, isDatesEqual, isPast
Difference:
differenceInYears, differenceInMonths, differenceInWeeks, differenceInDays, differenceInHours, differenceInMinutes, differenceInSeconds, differenceInBusinessDays, differenceInBusinessDaysCustom
Period boundaries:
startOfDay, startOfMonth, startOfWeek, startOfToday, startOfQuarter, endOfDay, endOfMonth, endOfWeek, endOfToday, endOfQuarter
differenceInBusinessDaysCustom(startDate, endDate, options?)Calculates business days with configurable work hours and lunch breaks.
$differenceInBusinessDaysCustom(startDate, endDate, {
"workStartHour": 9,
"workEndHour": 18,
"lunchBreakHours": 1,
"lunchStartHour": 12.5,
"lunchEndHour": 13.5,
"includePartialDays": true
})
| Function | Signature | Description |
|---|---|---|
formatDecimal | (number, precision?, decSep?, thousandSep?) | Format decimal with separators |
formatMoney | (number, precision?, decSep?, thousandSep?, currency?, position?) | Format as currency |
percentage | (value, total, decimals) | Calculate percentage |
truncate | (text, limit) | Truncate with ... |
join | (array, separator) | Join array to string |
formatDurationInSeconds | (seconds) | Human-readable duration via date-fns |
| Function | Signature | Description |
|---|---|---|
sha256 | (message) | SHA-256 hash (async) |
ascii | (str) | Strip accents and non-ASCII characters |
slug | (str, separator?) | URL-friendly slug |
extractNameFromEmail | (email, fallback?) | Extract name from email (e.g. john.doe@... → John Doe) |
filterEmpty | (array) | Remove null/undefined/empty from array |
padLeft | (value, length, char?) | Left-pad string |
padRight | (value, length, char?) | Right-pad string |
ifNull | (value, alternative) | Null coalescing |
objectToString | (object) | Convert object to key:value string |
startsWith | (haystack, needle) | String starts with |
endsWith | (haystack, needle) | String ends with |
includes | (haystack, needle) | String includes (null-safe) |
| Function | Signature | Description |
|---|---|---|
isEqual | (a, b) | Deep equality — compares by id, Date, JSON, or strict |
isEqualOrContained | (needle, haystack) | Check if value is equal to or contained in array |
isNotEqualOrContained | (needle, haystack) | Negation of isEqualOrContained |
getDbValue | (value) | Extract id from object or first item of array |
All types are exported:
import type {
TenantPreferences,
DateUtils,
DateUtilsConfig,
FormatOptions,
NumberUtils,
NumberUtilsConfig,
NumberFormatOptions,
NumberToWordsOptions,
NumberWordLang,
CurrencyCode,
TemplateEngine,
TemplateEngineConfig,
AppConfig,
UpsertAppConfigBody,
AppConfigClient,
DataView,
CreateDataViewBody,
UpdateDataViewBody,
ListDataViewsParams,
DataViewClient,
AppUtilsConfig
} from '@docyrus/app-utils';
MIT
FAQs
Utility functions for frontend applications using Docyrus as backend
We found that @docyrus/app-utils demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.