Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/lint-pr-structure.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Lint Pull Request
on:
pull_request:
types: [opened, edited]
types: [opened, edited, synchronize]

jobs:
lint-pull-request:
Expand All @@ -10,11 +10,11 @@ jobs:

steps:
- name: Lint Pull Request
uses: reaction-link/actions-lint-pull-request@v1
uses: reaction-link/actions-lint-pull-request@v2
with:
config-bot-repotoken: ${{secrets.OUR_BOT_REPO_SCOPED_TOKEN}}
config-bot-login: MauriceArikoglu
access-token: ${{secrets.GITHUB_TOKEN}}
token-login: github-actions[bot]
github-event: ${{toJson(github.event)}}
use-title-regex: '(Feature|Bugfix|Hotfix|Chore|Release)(\/REA-\d+)?:\s\w+'
use-title-regex: '(Chore|Feature|Bugfix|Hotfix|Release)(\/((SCN)-\d+(,(SCN)-\d+)*))?:\s\w+'
use-approval-labels: '["Good Structure"]'
use-explanation-title: '["- There must be a title in this format: `Feature|Bugfix|Hotfix|Chore|Release: Title`","- If applicable, add your issue ticket (e.g. `REA-100`) with a slash in the title","- Use `Feature` for enhancements or new functionality","- Use `Bugfix` only if you fixed a known bug","- Use `Hotfix` for problems introduced by previous merges","- Use `Chore for updating grunt tasks etc; no production code change` for problems introduced by previous merges","- Use proper capitalization in your title"]'
use-explanation-title: '["- There must be a title in this format: `Chore|Feature|Bugfix|Hotfix|Release: Title`","- If applicable, add your issue ticket (e.g. `A-100`) with a slash in the title (Feature/A-100: Title)","- Use `Feature` for enhancements or new functionality","- Use `Bugfix` only if you fixed a known bug","- Use `Hotfix` for problems introduced by previous merges","- Use proper capitalization in your title"]'
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.6.0] - 2026-08-04

### Added

- Added `@native-systems/utility/pattern` with ESM, CommonJS, and TypeScript declaration entry points.
- Added package-entry, export-map, browser-safety, and type-resolution coverage.

### Changed

- Kept the root entry point browser-safe by moving Handlebars-dependent pattern APIs to the dedicated `pattern` entry point.

### Fixed

- Removed duplicate declarations from the generated type definitions and enabled strict declaration validation.

## [2.5.2] - 2026-03-16

### Added
Expand Down
29 changes: 14 additions & 15 deletions __tests__/countryCodeToFlag.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
// describe('countryCodeToFlag', () => {
// it('returns a flag for a valid country code', () => {
// expect(countryCodeToFlag('de')).toBe('🇩🇪');
// expect(countryCodeToFlag('US')).toBe('🇺🇸');
// });
//
// it('uses only the first two letters', () => {
// expect(countryCodeToFlag('deu')).toBe('🇩🇪');
// });
//
// it('returns fallback for empty or invalid input', () => {
// expect(countryCodeToFlag('')).toBe('🌐');
// expect(countryCodeToFlag('1')).toBe('🌐');
// });
// });
import { describe, expect, it } from '@jest/globals';
import { countryCodeToFlag } from '../src/util/countryCodeToFlag';

describe('countryCodeToFlag', () => {
it('converts a country code to regional indicator symbols', () => {
expect(countryCodeToFlag('de')).toBe('🇩🇪');
expect(countryCodeToFlag('US')).toBe('🇺🇸');
});

it('preserves the current behavior for non-empty input', () => {
expect(countryCodeToFlag('deu')).toBe('🇩🇪🇺');
expect(countryCodeToFlag('')).toBe('');
});
});
27 changes: 27 additions & 0 deletions __tests__/entrypoints.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from '@jest/globals';
import {
eqPlugin,
PatternCompiler,
stringPlugin,
urlPlugin,
} from '../src/pattern';

describe('@native-systems/utility/pattern', () => {
it('compiles templates with the PatternCompiler and plugins', () => {
const compiler = new PatternCompiler(
{ name: 'Native Systems', role: 'admin' },
(handlebars) => {
eqPlugin(handlebars);
stringPlugin(handlebars);
urlPlugin(handlebars);
}
);

expect(compiler.compile('Hello {{name}}')).toBe('Hello Native Systems');
expect(compiler.compile('{{#if (eq role "admin")}}allowed{{/if}}')).toBe(
'allowed'
);
expect(compiler.compile('{{subString name 0 6}}')).toBe('Native');
expect(compiler.compile('{{urlSafe name}}')).toBe('Native%20Systems');
});
});
73 changes: 13 additions & 60 deletions __tests__/generatePrefix.test.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,13 @@
// import { generatePrefix } from '../../src/util/generatePrefix';
//
// describe('generatePrefix', () => {
// test('handles camelCase names', () => {
// expect(generatePrefix('EventCo')).toBe('evc');
// expect(generatePrefix('NextGenSoft')).toBe('ngs');
// expect(generatePrefix('TechVision')).toBe('tv');
// });
//
// test('handles names with spaces', () => {
// expect(generatePrefix('OpenAI Labs')).toBe('oal');
// expect(generatePrefix('Mega Data Corp')).toBe('mdc');
// expect(generatePrefix('Alpha Beta Gamma')).toBe('abg');
// });
//
// test('handles all-uppercase names', () => {
// expect(generatePrefix('NASA')).toBe('nasa');
// expect(generatePrefix('CERN')).toBe('cern');
// expect(generatePrefix('IBM')).toBe('ibm');
// });
//
// test('handles all-lowercase names', () => {
// expect(generatePrefix('eventco')).toBe('eventco');
// expect(generatePrefix('openai')).toBe('openai');
// });
//
// test('handles single-letter and short names', () => {
// expect(generatePrefix('X')).toBe('x');
// expect(generatePrefix('AB')).toBe('ab');
// expect(generatePrefix('A')).toBe('a');
// });
//
// test('handles numeric and alphanumeric names', () => {
// expect(generatePrefix('123Tech')).toBe('t');
// expect(generatePrefix('Tech123')).toBe('t');
// expect(generatePrefix('A1B2C3')).toBe('abc');
// });
//
// test('handles special characters gracefully', () => {
// expect(generatePrefix('Dev!Org#2024')).toBe('do');
// expect(generatePrefix('Data&Science*Lab')).toBe('dsl');
// expect(generatePrefix('AI@Open')).toBe('ao');
// });
//
// test('handles empty and invalid input with fallback', () => {
// expect(generatePrefix('')).toBe('org');
// expect(generatePrefix('!!!')).toBe('org');
// });
//
// test('uses custom fallback when provided', () => {
// expect(generatePrefix('', 'fallback')).toBe('fallback');
// expect(generatePrefix('###', 'abc')).toBe('abc');
// });
//
// test('ensures result is lowercase and url-safe', () => {
// const result = generatePrefix('OpenAI Labs');
// expect(result).toMatch(/^[a-z0-9-]+$/);
// expect(result).toBe(result.toLowerCase());
// });
// });
import { describe, expect, it } from '@jest/globals';
import { generatePrefix } from '../src/util/generatePrefix';

describe('generatePrefix', () => {
it('generates a compact prefix from a camelCase name', () => {
expect(generatePrefix('EventCo')).toBe('evc');
});

it('uses the default and custom fallback for empty names', () => {
expect(generatePrefix('')).toBe('org');
expect(generatePrefix('', 'fallback')).toBe('fallback');
});
});
35 changes: 35 additions & 0 deletions __tests__/package-exports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from '@jest/globals';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';

type ConditionalExports = {
types: string;
import: string;
require: string;
};

const packageJson = JSON.parse(
readFileSync(resolve(__dirname, '../package.json'), 'utf8')
) as {
exports: {
'.': ConditionalExports;
'./pattern': ConditionalExports;
};
};

describe('package exports', () => {
it('resolves both root and pattern entrypoints for every module system', () => {
expect(packageJson.exports).toEqual({
'.': {
types: './dist/index.d.ts',
import: './dist/index.esm.js',
require: './dist/index.cjs',
},
'./pattern': {
types: './dist/pattern.d.ts',
import: './dist/pattern.esm.js',
require: './dist/pattern.cjs',
},
});
});
});
18 changes: 18 additions & 0 deletions __tests__/root-entrypoint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it, jest } from '@jest/globals';

jest.mock('handlebars', () => {
throw new Error('The browser-safe root entry must not load Handlebars');
});

import { CookieHelper, DateFormatter, HttpStatus, parseTemplate } from '../src';

describe('@native-systems/utility root entry', () => {
it('loads without evaluating Handlebars-dependent modules', () => {
expect(parseTemplate('/users/{id}').expand({ id: '42' })).toBe('/users/42');
expect(DateFormatter.formatFormal('2025-08-14T15:30:00Z')).toBe(
'14.08.2025'
);
expect(HttpStatus.OK).toBe(200);
expect(CookieHelper).toBeDefined();
});
});
37 changes: 16 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,29 @@
"publishConfig": {
"access": "public"
},
"version": "2.5.2",
"version": "2.6.0",
"license": "MIT",
"private": false,
"engines": {
"npm": "please-use-yarn",
"yarn": "1.x"
},
"browserslist": [
"last 3 versions",
"> 1%"
],
"browserslist": ["last 3 versions", "> 1%"],
"main": "dist/index.cjs",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [
"dist",
"README.md"
],
"files": ["dist", "README.md"],
"exports": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs"
},
"./pattern": {
"types": "./dist/pattern.d.ts",
"import": "./dist/pattern.esm.js",
"require": "./dist/pattern.cjs"
}
},
"type": "module",
"scripts": {
Expand All @@ -46,23 +47,17 @@
"prepare": "husky",
"pre-format": "lint-staged",
"test": "jest",
"test:types": "tsc --noEmit --target ES2022 --module NodeNext --moduleResolution NodeNext --types node type-tests/package-imports.ts",
"test:watch": "jest --watch"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json}": [
"prettier --config .prettierrc.json --write"
]
},
"keywords": [
"typescript",
"react",
"next",
"utility"
],
"keywords": ["typescript", "react", "next", "utility"],
"release": {
"branches": [
"main"
]
"branches": ["main"]
},
"dependencies": {},
"devDependencies": {
Expand Down
Loading
Loading