@regardio/js 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,12 +5,33 @@
5
5
  A collection of lightweight, tree-shakeable utility functions for common tasks
6
6
  like HTTP handling, internationalization, time formatting, and validation.
7
7
 
8
+ ## ⚠️ Pre-release Notice
9
+
10
+ **This package is currently in pre-release (v0.x).** While we use these utilities in production across our own projects, the API may still change between minor versions. We recommend:
11
+
12
+ - Pinning to exact versions in your `package.json`
13
+ - Reviewing changelogs before upgrading
14
+ - Expecting potential breaking changes until v1.0
15
+
16
+ ## Why This Package?
17
+
18
+ We created `@regardio/js` to:
19
+
20
+ - **Share battle-tested utilities** — These functions power real Regardio projects and have been refined through actual use
21
+ - **Reduce boilerplate** — Common patterns like cookie handling, language detection, and time formatting in one place
22
+ - **Stay framework-agnostic** — Works with any JavaScript/TypeScript project (React, Node, Deno, etc.)
23
+ - **Enable tree-shaking** — Import only what you need; unused utilities won't bloat your bundle
24
+
8
25
  ## Installation
9
26
 
10
27
  ```bash
11
28
  pnpm add @regardio/js
12
29
  ```
13
30
 
31
+ ## Documentation
32
+
33
+ See the [docs](./docs) folder for detailed documentation on each module.
34
+
14
35
  ## Modules
15
36
 
16
37
  ### async/delay
@@ -146,9 +167,34 @@ verifyAccept('image/png', 'image/*'); // true
146
167
  verifyAccept('video/mp4', 'image/*'); // false
147
168
  ```
148
169
 
170
+ ## Module Overview
171
+
172
+ | Module | Description |
173
+ |--------|-------------|
174
+ | `async/delay` | Promise-based delay utility |
175
+ | `browser/base64` | URL-safe base64 to Uint8Array conversion |
176
+ | `format/bytes` | Human-readable byte formatting |
177
+ | `format/measure` | Performance measurement with logging |
178
+ | `http/cookie` | Browser cookie get/set helpers |
179
+ | `http/domain` | Domain extraction from requests (proxy-aware) |
180
+ | `http/request-helpers` | URL cleaning utilities |
181
+ | `intl/language-detector` | Server-side language detection for i18n |
182
+ | `intl/locale` | Client locale extraction from headers |
183
+ | `time/time` | Time formatting and date utilities |
184
+ | `validation/invariant` | Runtime assertion utilities |
185
+ | `validation/verify-file-accept` | MIME type validation for file uploads |
186
+
187
+ ## Contributing
188
+
189
+ This package is primarily maintained for Regardio's internal use, but we welcome:
190
+
191
+ - Bug reports and fixes
192
+ - Documentation improvements
193
+ - Feature suggestions (though we may not implement all requests)
194
+
149
195
  ## License
150
196
 
151
- **MIT License** - Free to use in commercial and open source projects.
197
+ **MIT License** Free to use in commercial and open source projects.
152
198
 
153
199
  ---
154
200
 
@@ -1 +1 @@
1
- {"version":3,"file":"language-detector.d.ts","sourceRoot":"","sources":["../../src/intl/language-detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG3D,MAAM,WAAW,sBAAsB;IAMrC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAM7B,gBAAgB,EAAE,MAAM,CAAC;IAKzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAMhB,cAAc,CAAC,EAAE,cAAc,CAAC;IAMhC,UAAU,CAAC,EAAE,MAAM,CAAC;IAOpB,cAAc,CAAC,EAAE,MAAM,CAAC;IAWxB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;CAC7E;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,sBAAsB,CAAC;CACnC;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,OAAO,CAAgC;gBAEnC,OAAO,EAAE,6BAA6B;IAgBrC,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;CAG1D;AAOD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAyB;gBAE5B,OAAO,EAAE,sBAAsB;IAM9B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCtD,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,gBAAgB;YAQV,UAAU;YAcV,kBAAkB;IAYhC,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,aAAa;CAYtB"}
1
+ {"version":3,"file":"language-detector.d.ts","sourceRoot":"","sources":["../../src/intl/language-detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IAMrC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAM7B,gBAAgB,EAAE,MAAM,CAAC;IAKzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAMhB,cAAc,CAAC,EAAE,cAAc,CAAC;IAMhC,UAAU,CAAC,EAAE,MAAM,CAAC;IAOpB,cAAc,CAAC,EAAE,MAAM,CAAC;IAWxB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;CAC7E;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,sBAAsB,CAAC;CACnC;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,OAAO,CAAgC;gBAEnC,OAAO,EAAE,6BAA6B;IAgBrC,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;CAG1D;AAOD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAyB;gBAE5B,OAAO,EAAE,sBAAsB;IAM9B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCtD,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,gBAAgB;YAQV,UAAU;YAcV,kBAAkB;IAYhC,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,aAAa;CAYtB"}
@@ -1,5 +1,4 @@
1
1
  import { parseAcceptLanguage } from 'intl-parse-accept-language';
2
- import { getClientLocales } from './locale';
3
2
  export class LanguageDetectorLingui {
4
3
  detector;
5
4
  options;
@@ -112,3 +111,24 @@ export class LanguageDetector {
112
111
  return parsed[0] || this.options.fallbackLanguage;
113
112
  }
114
113
  }
114
+ function getClientLocales(requestOrHeaders) {
115
+ const headers = getHeaders(requestOrHeaders);
116
+ const acceptLanguage = headers.get('Accept-Language');
117
+ if (!acceptLanguage)
118
+ return undefined;
119
+ const locales = parseAcceptLanguage(acceptLanguage, {
120
+ ignoreWildcard: true,
121
+ validate: Intl.DateTimeFormat.supportedLocalesOf,
122
+ });
123
+ if (locales.length === 0)
124
+ return undefined;
125
+ if (locales.length === 1)
126
+ return locales[0];
127
+ return locales;
128
+ }
129
+ function getHeaders(requestOrHeaders) {
130
+ if (requestOrHeaders instanceof Request) {
131
+ return requestOrHeaders.headers;
132
+ }
133
+ return requestOrHeaders;
134
+ }
package/package.json CHANGED
@@ -48,10 +48,6 @@
48
48
  "import": "./dist/intl/language-detector.js",
49
49
  "types": "./dist/intl/language-detector.d.ts"
50
50
  },
51
- "./intl/locale": {
52
- "import": "./dist/intl/locale.js",
53
- "types": "./dist/intl/locale.d.ts"
54
- },
55
51
  "./time/time": {
56
52
  "import": "./dist/time/time.js",
57
53
  "types": "./dist/time/time.d.ts"
@@ -108,5 +104,5 @@
108
104
  },
109
105
  "sideEffects": false,
110
106
  "type": "module",
111
- "version": "0.2.3"
107
+ "version": "0.3.0"
112
108
  }
@@ -1,4 +0,0 @@
1
- export type Locales = string | string[] | undefined;
2
- export declare function getClientLocales(headers: Headers): Locales;
3
- export declare function getClientLocales(request: Request): Locales;
4
- //# sourceMappingURL=locale.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../src/intl/locale.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AAiBpD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;AAC5D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC"}
@@ -1,22 +0,0 @@
1
- import { parseAcceptLanguage } from 'intl-parse-accept-language';
2
- export function getClientLocales(requestOrHeaders) {
3
- const headers = getHeaders(requestOrHeaders);
4
- const acceptLanguage = headers.get('Accept-Language');
5
- if (!acceptLanguage)
6
- return undefined;
7
- const locales = parseAcceptLanguage(acceptLanguage, {
8
- ignoreWildcard: true,
9
- validate: Intl.DateTimeFormat.supportedLocalesOf,
10
- });
11
- if (locales.length === 0)
12
- return undefined;
13
- if (locales.length === 1)
14
- return locales[0];
15
- return locales;
16
- }
17
- function getHeaders(requestOrHeaders) {
18
- if (requestOrHeaders instanceof Request) {
19
- return requestOrHeaders.headers;
20
- }
21
- return requestOrHeaders;
22
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=locale.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"locale.test.d.ts","sourceRoot":"","sources":["../../src/intl/locale.test.ts"],"names":[],"mappings":""}
@@ -1,75 +0,0 @@
1
- import { describe, expect, test } from 'vitest';
2
- import { getClientLocales } from './locale';
3
- describe('getClientLocales', () => {
4
- describe('with Request object', () => {
5
- test('should return undefined when Accept-Language header is missing', () => {
6
- const request = new Request('https://example.com');
7
- expect(getClientLocales(request)).toBeUndefined();
8
- });
9
- test('should return single locale when only one is provided', () => {
10
- const request = new Request('https://example.com', {
11
- headers: { 'Accept-Language': 'en-US' },
12
- });
13
- expect(getClientLocales(request)).toBe('en-US');
14
- });
15
- test('should return array of locales sorted by quality', () => {
16
- const request = new Request('https://example.com', {
17
- headers: { 'Accept-Language': 'en-US,de;q=0.9,fr;q=0.8' },
18
- });
19
- const result = getClientLocales(request);
20
- expect(Array.isArray(result)).toBe(true);
21
- expect(result).toContain('en-US');
22
- expect(result).toContain('de');
23
- });
24
- test('should return undefined for invalid locales', () => {
25
- const request = new Request('https://example.com', {
26
- headers: { 'Accept-Language': 'invalid-locale-xyz' },
27
- });
28
- expect(getClientLocales(request)).toBeUndefined();
29
- });
30
- test('should ignore wildcard (*)', () => {
31
- const request = new Request('https://example.com', {
32
- headers: { 'Accept-Language': '*' },
33
- });
34
- expect(getClientLocales(request)).toBeUndefined();
35
- });
36
- });
37
- describe('with Headers object', () => {
38
- test('should return undefined when Accept-Language header is missing', () => {
39
- const headers = new Headers();
40
- expect(getClientLocales(headers)).toBeUndefined();
41
- });
42
- test('should return single locale when only one is provided', () => {
43
- const headers = new Headers({ 'Accept-Language': 'de-DE' });
44
- expect(getClientLocales(headers)).toBe('de-DE');
45
- });
46
- test('should return array of locales sorted by quality', () => {
47
- const headers = new Headers({ 'Accept-Language': 'fr-FR,en;q=0.9,de;q=0.8' });
48
- const result = getClientLocales(headers);
49
- expect(Array.isArray(result)).toBe(true);
50
- expect(result).toContain('fr-FR');
51
- });
52
- });
53
- describe('quality value handling', () => {
54
- test('should prioritize higher quality values', () => {
55
- const request = new Request('https://example.com', {
56
- headers: { 'Accept-Language': 'de;q=0.5,en;q=0.9' },
57
- });
58
- const result = getClientLocales(request);
59
- expect(Array.isArray(result)).toBe(true);
60
- if (Array.isArray(result)) {
61
- expect(result[0]).toBe('en');
62
- }
63
- });
64
- test('should handle locales without explicit quality (default q=1)', () => {
65
- const request = new Request('https://example.com', {
66
- headers: { 'Accept-Language': 'en,de;q=0.9' },
67
- });
68
- const result = getClientLocales(request);
69
- expect(Array.isArray(result)).toBe(true);
70
- if (Array.isArray(result)) {
71
- expect(result[0]).toBe('en');
72
- }
73
- });
74
- });
75
- });