@digitaldefiance/i18n-lib 3.0.0 → 3.6.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 +145 -0
- package/package.json +1 -1
- package/src/core/i18n-engine.d.ts.map +1 -1
- package/src/core/i18n-engine.js +18 -7
- package/src/core/i18n-engine.js.map +1 -1
- package/src/icu/ast.d.ts +48 -0
- package/src/icu/ast.d.ts.map +1 -0
- package/src/icu/ast.js +16 -0
- package/src/icu/ast.js.map +1 -0
- package/src/icu/compiler.d.ts +16 -0
- package/src/icu/compiler.d.ts.map +1 -0
- package/src/icu/compiler.js +81 -0
- package/src/icu/compiler.js.map +1 -0
- package/src/icu/formatter-registry.d.ts +10 -0
- package/src/icu/formatter-registry.d.ts.map +1 -0
- package/src/icu/formatter-registry.js +34 -0
- package/src/icu/formatter-registry.js.map +1 -0
- package/src/icu/formatters/base-formatter.d.ts +8 -0
- package/src/icu/formatters/base-formatter.d.ts.map +1 -0
- package/src/icu/formatters/base-formatter.js +3 -0
- package/src/icu/formatters/base-formatter.js.map +1 -0
- package/src/icu/formatters/date-formatter.d.ts +5 -0
- package/src/icu/formatters/date-formatter.d.ts.map +1 -0
- package/src/icu/formatters/date-formatter.js +31 -0
- package/src/icu/formatters/date-formatter.js.map +1 -0
- package/src/icu/formatters/number-formatter.d.ts +5 -0
- package/src/icu/formatters/number-formatter.d.ts.map +1 -0
- package/src/icu/formatters/number-formatter.js +30 -0
- package/src/icu/formatters/number-formatter.js.map +1 -0
- package/src/icu/formatters/plural-formatter.d.ts +5 -0
- package/src/icu/formatters/plural-formatter.d.ts.map +1 -0
- package/src/icu/formatters/plural-formatter.js +15 -0
- package/src/icu/formatters/plural-formatter.js.map +1 -0
- package/src/icu/formatters/select-formatter.d.ts +5 -0
- package/src/icu/formatters/select-formatter.d.ts.map +1 -0
- package/src/icu/formatters/select-formatter.js +10 -0
- package/src/icu/formatters/select-formatter.js.map +1 -0
- package/src/icu/formatters/selectordinal-formatter.d.ts +5 -0
- package/src/icu/formatters/selectordinal-formatter.d.ts.map +1 -0
- package/src/icu/formatters/selectordinal-formatter.js +22 -0
- package/src/icu/formatters/selectordinal-formatter.js.map +1 -0
- package/src/icu/formatters/time-formatter.d.ts +5 -0
- package/src/icu/formatters/time-formatter.d.ts.map +1 -0
- package/src/icu/formatters/time-formatter.js +31 -0
- package/src/icu/formatters/time-formatter.js.map +1 -0
- package/src/icu/helpers.d.ts +9 -0
- package/src/icu/helpers.d.ts.map +1 -0
- package/src/icu/helpers.js +31 -0
- package/src/icu/helpers.js.map +1 -0
- package/src/icu/parser.d.ts +31 -0
- package/src/icu/parser.d.ts.map +1 -0
- package/src/icu/parser.js +203 -0
- package/src/icu/parser.js.map +1 -0
- package/src/icu/runtime.d.ts +10 -0
- package/src/icu/runtime.d.ts.map +1 -0
- package/src/icu/runtime.js +33 -0
- package/src/icu/runtime.js.map +1 -0
- package/src/icu/tokenizer.d.ts +37 -0
- package/src/icu/tokenizer.d.ts.map +1 -0
- package/src/icu/tokenizer.js +187 -0
- package/src/icu/tokenizer.js.map +1 -0
- package/src/icu/validator.d.ts +11 -0
- package/src/icu/validator.d.ts.map +1 -0
- package/src/icu/validator.js +140 -0
- package/src/icu/validator.js.map +1 -0
- package/src/template.d.ts.map +1 -1
- package/src/template.js +12 -3
- package/src/template.js.map +1 -1
- package/src/utils/html-escape.d.ts +8 -0
- package/src/utils/html-escape.d.ts.map +1 -0
- package/src/utils/html-escape.js +33 -0
- package/src/utils/html-escape.js.map +1 -0
- package/src/utils/index.d.ts +7 -0
- package/src/utils/index.d.ts.map +1 -1
- package/src/utils/index.js +7 -0
- package/src/utils/index.js.map +1 -1
- package/src/utils/lru-cache.d.ts +13 -0
- package/src/utils/lru-cache.d.ts.map +1 -0
- package/src/utils/lru-cache.js +44 -0
- package/src/utils/lru-cache.js.map +1 -0
- package/src/utils/safe-object.d.ts +9 -0
- package/src/utils/safe-object.d.ts.map +1 -0
- package/src/utils/safe-object.js +44 -0
- package/src/utils/safe-object.js.map +1 -0
- package/src/utils/string-utils.d.ts +4 -1
- package/src/utils/string-utils.d.ts.map +1 -1
- package/src/utils/string-utils.js +5 -5
- package/src/utils/string-utils.js.map +1 -1
- package/src/utils/validation.d.ts +15 -0
- package/src/utils/validation.d.ts.map +1 -0
- package/src/utils/validation.js +44 -0
- package/src/utils/validation.js.map +1 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ICU MessageFormat Tokenizer
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Tokenizer = exports.TokenType = void 0;
|
|
7
|
+
var TokenType;
|
|
8
|
+
(function (TokenType) {
|
|
9
|
+
TokenType["TEXT"] = "TEXT";
|
|
10
|
+
TokenType["OPEN_BRACE"] = "OPEN_BRACE";
|
|
11
|
+
TokenType["CLOSE_BRACE"] = "CLOSE_BRACE";
|
|
12
|
+
TokenType["COMMA"] = "COMMA";
|
|
13
|
+
TokenType["HASH"] = "HASH";
|
|
14
|
+
TokenType["IDENTIFIER"] = "IDENTIFIER";
|
|
15
|
+
TokenType["EOF"] = "EOF";
|
|
16
|
+
})(TokenType || (exports.TokenType = TokenType = {}));
|
|
17
|
+
class Tokenizer {
|
|
18
|
+
input;
|
|
19
|
+
position = 0;
|
|
20
|
+
length;
|
|
21
|
+
braceDepth = 0;
|
|
22
|
+
lastTokenType = null;
|
|
23
|
+
formatDepths = new Set(); // Tracks which depths are format specs
|
|
24
|
+
lastIdentifierWasAtFormatDepth = false;
|
|
25
|
+
constructor(input) {
|
|
26
|
+
this.input = input;
|
|
27
|
+
this.length = input.length;
|
|
28
|
+
}
|
|
29
|
+
tokenize() {
|
|
30
|
+
const tokens = [];
|
|
31
|
+
while (this.position < this.length) {
|
|
32
|
+
const token = this.nextToken();
|
|
33
|
+
if (token.type !== TokenType.EOF) {
|
|
34
|
+
tokens.push(token);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
tokens.push({ type: TokenType.EOF, value: '', position: this.position });
|
|
38
|
+
return tokens;
|
|
39
|
+
}
|
|
40
|
+
nextToken() {
|
|
41
|
+
if (this.position >= this.length) {
|
|
42
|
+
return { type: TokenType.EOF, value: '', position: this.position };
|
|
43
|
+
}
|
|
44
|
+
const char = this.input[this.position];
|
|
45
|
+
// Handle escape sequences - quoted braces
|
|
46
|
+
if (char === "'") {
|
|
47
|
+
const next = this.peek();
|
|
48
|
+
if (next === '{' || next === '}') {
|
|
49
|
+
const pos = this.position;
|
|
50
|
+
this.position++; // Skip quote
|
|
51
|
+
this.position++; // Skip brace
|
|
52
|
+
return { type: TokenType.TEXT, value: next, position: pos };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Special characters
|
|
56
|
+
if (char === '{') {
|
|
57
|
+
const prevDepth = this.braceDepth;
|
|
58
|
+
this.braceDepth++;
|
|
59
|
+
// Add to format depths if:
|
|
60
|
+
// 1. Start of message (null)
|
|
61
|
+
// 2. After TEXT - new format in message (unless after case name)
|
|
62
|
+
// 3. After CLOSE_BRACE at depth 0 - consecutive formats
|
|
63
|
+
// 4. After OPEN_BRACE - nested format (unless after case name)
|
|
64
|
+
// NOT after IDENTIFIER at format depth - that's a case name, next brace is message content
|
|
65
|
+
if (this.lastTokenType === null ||
|
|
66
|
+
(this.lastTokenType === TokenType.TEXT && !this.lastIdentifierWasAtFormatDepth) ||
|
|
67
|
+
(this.lastTokenType === TokenType.CLOSE_BRACE && prevDepth === 0) ||
|
|
68
|
+
(this.lastTokenType === TokenType.OPEN_BRACE && !this.lastIdentifierWasAtFormatDepth)) {
|
|
69
|
+
this.formatDepths.add(this.braceDepth);
|
|
70
|
+
}
|
|
71
|
+
this.lastIdentifierWasAtFormatDepth = false;
|
|
72
|
+
const token = { type: TokenType.OPEN_BRACE, value: char, position: this.position++ };
|
|
73
|
+
this.lastTokenType = TokenType.OPEN_BRACE;
|
|
74
|
+
return token;
|
|
75
|
+
}
|
|
76
|
+
if (char === '}') {
|
|
77
|
+
this.formatDepths.delete(this.braceDepth);
|
|
78
|
+
this.braceDepth--;
|
|
79
|
+
this.lastIdentifierWasAtFormatDepth = false; // Reset after closing
|
|
80
|
+
const token = { type: TokenType.CLOSE_BRACE, value: char, position: this.position++ };
|
|
81
|
+
this.lastTokenType = TokenType.CLOSE_BRACE;
|
|
82
|
+
return token;
|
|
83
|
+
}
|
|
84
|
+
if (char === ',') {
|
|
85
|
+
// Comma at depth 1+ is format separator, at depth 0 is text
|
|
86
|
+
if (this.braceDepth >= 1) {
|
|
87
|
+
const token = { type: TokenType.COMMA, value: char, position: this.position++ };
|
|
88
|
+
this.lastTokenType = TokenType.COMMA;
|
|
89
|
+
return token;
|
|
90
|
+
}
|
|
91
|
+
// At depth 0, comma is just text
|
|
92
|
+
return this.readText();
|
|
93
|
+
}
|
|
94
|
+
if (char === '#') {
|
|
95
|
+
return { type: TokenType.HASH, value: char, position: this.position++ };
|
|
96
|
+
}
|
|
97
|
+
// Whitespace - skip at format level OR after identifier at depth 1
|
|
98
|
+
if (this.isWhitespace(char) && (this.isAtFormatLevel() || (this.braceDepth === 1 && this.lastTokenType === TokenType.IDENTIFIER))) {
|
|
99
|
+
this.position++;
|
|
100
|
+
return this.nextToken();
|
|
101
|
+
}
|
|
102
|
+
// Identifier (variable names, keywords) - at format level
|
|
103
|
+
if (this.isIdentifierStart(char)) {
|
|
104
|
+
if (this.isAtFormatLevel()) {
|
|
105
|
+
return this.readIdentifier();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Text (everything else)
|
|
109
|
+
return this.readText();
|
|
110
|
+
}
|
|
111
|
+
readIdentifier() {
|
|
112
|
+
const start = this.position;
|
|
113
|
+
while (this.position < this.length && this.isIdentifierChar(this.input[this.position])) {
|
|
114
|
+
this.position++;
|
|
115
|
+
}
|
|
116
|
+
const token = {
|
|
117
|
+
type: TokenType.IDENTIFIER,
|
|
118
|
+
value: this.input.substring(start, this.position),
|
|
119
|
+
position: start,
|
|
120
|
+
};
|
|
121
|
+
this.lastTokenType = TokenType.IDENTIFIER;
|
|
122
|
+
this.lastIdentifierWasAtFormatDepth = this.formatDepths.has(this.braceDepth);
|
|
123
|
+
return token;
|
|
124
|
+
}
|
|
125
|
+
readText() {
|
|
126
|
+
const start = this.position;
|
|
127
|
+
let text = '';
|
|
128
|
+
while (this.position < this.length) {
|
|
129
|
+
const char = this.input[this.position];
|
|
130
|
+
// Handle escaped quotes - stop before them
|
|
131
|
+
if (char === "'" && (this.peek() === '{' || this.peek() === '}')) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
// Stop at special characters (but not if they're escaped)
|
|
135
|
+
if (char === '{' || char === '#') {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
// For }, only stop if we're inside braces
|
|
139
|
+
if (char === '}' && this.braceDepth > 0) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
// Stop at comma if we're at format level (depth >= 1)
|
|
143
|
+
if (char === ',' && this.braceDepth >= 1) {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
text += char;
|
|
147
|
+
this.position++;
|
|
148
|
+
}
|
|
149
|
+
const token = {
|
|
150
|
+
type: TokenType.TEXT,
|
|
151
|
+
value: text,
|
|
152
|
+
position: start,
|
|
153
|
+
};
|
|
154
|
+
this.lastTokenType = TokenType.TEXT;
|
|
155
|
+
// Don't reset the identifier flag for whitespace-only text
|
|
156
|
+
if (text.trim() !== '') {
|
|
157
|
+
this.lastIdentifierWasAtFormatDepth = false;
|
|
158
|
+
}
|
|
159
|
+
return token;
|
|
160
|
+
}
|
|
161
|
+
peek() {
|
|
162
|
+
return this.position + 1 < this.length ? this.input[this.position + 1] : '';
|
|
163
|
+
}
|
|
164
|
+
isIdentifierStart(char) {
|
|
165
|
+
return /[a-zA-Z_]/.test(char);
|
|
166
|
+
}
|
|
167
|
+
isIdentifierChar(char) {
|
|
168
|
+
return /[a-zA-Z0-9_]/.test(char);
|
|
169
|
+
}
|
|
170
|
+
isWhitespace(char) {
|
|
171
|
+
return /\s/.test(char);
|
|
172
|
+
}
|
|
173
|
+
isAtFormatLevel() {
|
|
174
|
+
// We're at format level if current depth is marked as format AND:
|
|
175
|
+
// 1. After OPEN_BRACE (expecting variable name)
|
|
176
|
+
// 2. After COMMA (expecting format type or case name)
|
|
177
|
+
// 3. After CLOSE_BRACE (just finished a case message, expecting another case name)
|
|
178
|
+
if (!this.formatDepths.has(this.braceDepth)) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return this.lastTokenType === TokenType.OPEN_BRACE ||
|
|
182
|
+
this.lastTokenType === TokenType.COMMA ||
|
|
183
|
+
this.lastTokenType === TokenType.CLOSE_BRACE;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.Tokenizer = Tokenizer;
|
|
187
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/icu/tokenizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MessageNode } from './ast';
|
|
2
|
+
export declare class ValidationError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export interface ValidationOptions {
|
|
6
|
+
requireOtherCase?: boolean;
|
|
7
|
+
allowCircularReferences?: boolean;
|
|
8
|
+
maxDepth?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function validate(ast: MessageNode, options?: ValidationOptions): void;
|
|
11
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/icu/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAqE,MAAM,OAAO,CAAC;AAEvG,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAQD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAIhF"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ValidationError = void 0;
|
|
4
|
+
exports.validate = validate;
|
|
5
|
+
const ast_1 = require("./ast");
|
|
6
|
+
class ValidationError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'ValidationError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.ValidationError = ValidationError;
|
|
13
|
+
const DEFAULT_OPTIONS = {
|
|
14
|
+
requireOtherCase: true,
|
|
15
|
+
allowCircularReferences: false,
|
|
16
|
+
maxDepth: 10,
|
|
17
|
+
};
|
|
18
|
+
function validate(ast, options = {}) {
|
|
19
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
20
|
+
const context = new ValidationContext(opts);
|
|
21
|
+
validateMessage(ast, context);
|
|
22
|
+
}
|
|
23
|
+
class ValidationContext {
|
|
24
|
+
options;
|
|
25
|
+
argumentNames = new Set();
|
|
26
|
+
currentDepth = 0;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
}
|
|
30
|
+
addArgument(name) {
|
|
31
|
+
this.argumentNames.add(name);
|
|
32
|
+
}
|
|
33
|
+
hasArgument(name) {
|
|
34
|
+
return this.argumentNames.has(name);
|
|
35
|
+
}
|
|
36
|
+
enterDepth() {
|
|
37
|
+
this.currentDepth++;
|
|
38
|
+
if (this.currentDepth > this.options.maxDepth) {
|
|
39
|
+
throw new ValidationError(`Maximum nesting depth of ${this.options.maxDepth} exceeded`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exitDepth() {
|
|
43
|
+
this.currentDepth--;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function validateMessage(node, context) {
|
|
47
|
+
for (const element of node.elements) {
|
|
48
|
+
validateNode(element, context);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function validateNode(node, context) {
|
|
52
|
+
switch (node.type) {
|
|
53
|
+
case ast_1.NodeType.LITERAL:
|
|
54
|
+
break;
|
|
55
|
+
case ast_1.NodeType.ARGUMENT:
|
|
56
|
+
validateArgument(node, context);
|
|
57
|
+
break;
|
|
58
|
+
case ast_1.NodeType.PLURAL:
|
|
59
|
+
validatePlural(node, context);
|
|
60
|
+
break;
|
|
61
|
+
case ast_1.NodeType.SELECT:
|
|
62
|
+
validateSelect(node, context);
|
|
63
|
+
break;
|
|
64
|
+
case ast_1.NodeType.SELECTORDINAL:
|
|
65
|
+
validateSelectOrdinal(node, context);
|
|
66
|
+
break;
|
|
67
|
+
default:
|
|
68
|
+
throw new ValidationError(`Unknown node type: ${node.type}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function validateArgument(node, context) {
|
|
72
|
+
if (!node.name || node.name.trim() === '') {
|
|
73
|
+
throw new ValidationError('Argument name cannot be empty');
|
|
74
|
+
}
|
|
75
|
+
context.addArgument(node.name);
|
|
76
|
+
}
|
|
77
|
+
function validatePlural(node, context) {
|
|
78
|
+
if (!node.name || node.name.trim() === '') {
|
|
79
|
+
throw new ValidationError('Plural variable name cannot be empty');
|
|
80
|
+
}
|
|
81
|
+
context.addArgument(node.name);
|
|
82
|
+
if (!node.cases || Object.keys(node.cases).length === 0) {
|
|
83
|
+
throw new ValidationError(`Plural '${node.name}' must have at least one case`);
|
|
84
|
+
}
|
|
85
|
+
if (context.options.requireOtherCase && !node.cases.other) {
|
|
86
|
+
throw new ValidationError(`Plural '${node.name}' must have an 'other' case`);
|
|
87
|
+
}
|
|
88
|
+
const validCases = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
89
|
+
for (const caseName of Object.keys(node.cases)) {
|
|
90
|
+
if (!validCases.includes(caseName)) {
|
|
91
|
+
throw new ValidationError(`Invalid plural case '${caseName}' in '${node.name}'`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
context.enterDepth();
|
|
95
|
+
for (const caseMessage of Object.values(node.cases)) {
|
|
96
|
+
validateMessage(caseMessage, context);
|
|
97
|
+
}
|
|
98
|
+
context.exitDepth();
|
|
99
|
+
}
|
|
100
|
+
function validateSelect(node, context) {
|
|
101
|
+
if (!node.name || node.name.trim() === '') {
|
|
102
|
+
throw new ValidationError('Select variable name cannot be empty');
|
|
103
|
+
}
|
|
104
|
+
context.addArgument(node.name);
|
|
105
|
+
if (!node.cases || Object.keys(node.cases).length === 0) {
|
|
106
|
+
throw new ValidationError(`Select '${node.name}' must have at least one case`);
|
|
107
|
+
}
|
|
108
|
+
if (context.options.requireOtherCase && !node.cases.other) {
|
|
109
|
+
throw new ValidationError(`Select '${node.name}' must have an 'other' case`);
|
|
110
|
+
}
|
|
111
|
+
context.enterDepth();
|
|
112
|
+
for (const caseMessage of Object.values(node.cases)) {
|
|
113
|
+
validateMessage(caseMessage, context);
|
|
114
|
+
}
|
|
115
|
+
context.exitDepth();
|
|
116
|
+
}
|
|
117
|
+
function validateSelectOrdinal(node, context) {
|
|
118
|
+
if (!node.name || node.name.trim() === '') {
|
|
119
|
+
throw new ValidationError('SelectOrdinal variable name cannot be empty');
|
|
120
|
+
}
|
|
121
|
+
context.addArgument(node.name);
|
|
122
|
+
if (!node.cases || Object.keys(node.cases).length === 0) {
|
|
123
|
+
throw new ValidationError(`SelectOrdinal '${node.name}' must have at least one case`);
|
|
124
|
+
}
|
|
125
|
+
if (context.options.requireOtherCase && !node.cases.other) {
|
|
126
|
+
throw new ValidationError(`SelectOrdinal '${node.name}' must have an 'other' case`);
|
|
127
|
+
}
|
|
128
|
+
const validCases = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
129
|
+
for (const caseName of Object.keys(node.cases)) {
|
|
130
|
+
if (!validCases.includes(caseName)) {
|
|
131
|
+
throw new ValidationError(`Invalid selectordinal case '${caseName}' in '${node.name}'`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
context.enterDepth();
|
|
135
|
+
for (const caseMessage of Object.values(node.cases)) {
|
|
136
|
+
validateMessage(caseMessage, context);
|
|
137
|
+
}
|
|
138
|
+
context.exitDepth();
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/icu/validator.ts"],"names":[],"mappings":"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"}
|
package/src/template.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-i18n-lib/src/template.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAC7B,CAAC,SAAS,MAAM,EAChB,KAAK,IACH,CAAC,SAAS,GAAG,MAAM,KAAK,MAAM,IAAI,MAAM,GAAG,KAAK,MAAM,IAAI,EAAE,GAC5D,GAAG,SAAS,QAAQ,CAAC,KAAK,CAAC,GACzB,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,GAChC,KAAK,GACP,IAAI,CAAC;AAET;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,SAAS,SAAS,MAAM,EAExB,OAAO,EAAE,KAAK,EACd,WAAW,EAAE,CACX,GAAG,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,EACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EACtC,QAAQ,CAAC,EAAE,SAAS,KACjB,MAAM,EACX,QAAQ,EAAE,MAAM,IAEE,CAAC,SAAS,MAAM,EAChC,KAAK,mBAAmB,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,EAC3D,WAAW,SAAS,EACpB,GAAG,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,KAC9C,MAAM,
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-i18n-lib/src/template.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAC7B,CAAC,SAAS,MAAM,EAChB,KAAK,IACH,CAAC,SAAS,GAAG,MAAM,KAAK,MAAM,IAAI,MAAM,GAAG,KAAK,MAAM,IAAI,EAAE,GAC5D,GAAG,SAAS,QAAQ,CAAC,KAAK,CAAC,GACzB,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,GAChC,KAAK,GACP,IAAI,CAAC;AAET;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,SAAS,SAAS,MAAM,EAExB,OAAO,EAAE,KAAK,EACd,WAAW,EAAE,CACX,GAAG,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,EACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EACtC,QAAQ,CAAC,EAAE,SAAS,KACjB,MAAM,EACX,QAAQ,EAAE,MAAM,IAEE,CAAC,SAAS,MAAM,EAChC,KAAK,mBAAmB,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,EAC3D,WAAW,SAAS,EACpB,GAAG,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,KAC9C,MAAM,CAmCV"}
|
package/src/template.js
CHANGED
|
@@ -7,7 +7,8 @@ exports.createTemplateProcessor = createTemplateProcessor;
|
|
|
7
7
|
function createTemplateProcessor(enumObj, translateFn, enumName) {
|
|
8
8
|
return function t(str, language, ...otherVars) {
|
|
9
9
|
let varIndex = 0;
|
|
10
|
-
|
|
10
|
+
// Limited pattern to prevent ReDoS
|
|
11
|
+
const pattern = new RegExp(`\\{\\{${enumName}\\.(\\w{1,50})\\}\\}`, 'g');
|
|
11
12
|
// First replace enum patterns
|
|
12
13
|
let result = str.replace(pattern, (match, enumKey) => {
|
|
13
14
|
const enumValue = enumObj[enumKey];
|
|
@@ -21,8 +22,16 @@ function createTemplateProcessor(enumObj, translateFn, enumName) {
|
|
|
21
22
|
return translateFn(enumValue, vars, language);
|
|
22
23
|
});
|
|
23
24
|
// Then replace any remaining variables from all otherVars
|
|
24
|
-
const allVars =
|
|
25
|
-
|
|
25
|
+
const allVars = {};
|
|
26
|
+
for (const vars of otherVars) {
|
|
27
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
28
|
+
if (!['__proto__', 'constructor', 'prototype'].includes(key)) {
|
|
29
|
+
allVars[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Limited pattern to prevent ReDoS
|
|
34
|
+
result = result.replace(/\{(\w{1,50})\}/g, (match, varName) => {
|
|
26
35
|
return allVars[varName] !== undefined ? String(allVars[varName]) : match;
|
|
27
36
|
});
|
|
28
37
|
return result;
|
package/src/template.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../packages/digitaldefiance-i18n-lib/src/template.ts"],"names":[],"mappings":";;AAoBA,
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../packages/digitaldefiance-i18n-lib/src/template.ts"],"names":[],"mappings":";;AAoBA,0DAmDC;AAtDD;;GAEG;AACH,SAAgB,uBAAuB,CAIrC,OAAc,EACd,WAIW,EACX,QAAgB;IAEhB,OAAO,SAAS,CAAC,CACf,GAA2D,EAC3D,QAAoB,EACpB,GAAG,SAA4C;QAE/C,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,mCAAmC;QACnC,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,SAAS,QAAQ,sBAAsB,EAAE,GAAG,CAAC,CAAC;QAEzE,8BAA8B;QAC9B,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACnD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAsB,CAAC,CAAC;YAClD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,KAAK,CAAC,CAAC,wCAAwC;YACxD,CAAC;YAED,MAAM,SAAS,GAAI,SAAoB;iBACpC,WAAW,EAAE;iBACb,QAAQ,CAAC,UAAU,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,OAAO,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,0DAA0D;QAC1D,MAAM,OAAO,GAAoC,EAAE,CAAC;QACpD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,IAAI,CAAC,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC7D,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QACD,mCAAmC;QACnC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAC5D,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-escape.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/html-escape.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAepF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HTML escaping utilities for XSS prevention
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.escapeHtml = escapeHtml;
|
|
7
|
+
exports.safeStringify = safeStringify;
|
|
8
|
+
const HTML_ESCAPES = {
|
|
9
|
+
'&': '&',
|
|
10
|
+
'<': '<',
|
|
11
|
+
'>': '>',
|
|
12
|
+
'"': '"',
|
|
13
|
+
"'": ''',
|
|
14
|
+
'/': '/',
|
|
15
|
+
};
|
|
16
|
+
const HTML_ESCAPE_REGEX = /[&<>"'/]/g;
|
|
17
|
+
function escapeHtml(str) {
|
|
18
|
+
return str.replace(HTML_ESCAPE_REGEX, char => HTML_ESCAPES[char]);
|
|
19
|
+
}
|
|
20
|
+
function safeStringify(value, options) {
|
|
21
|
+
if (value === null || value === undefined) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
return options?.escapeHtml ? escapeHtml(value) : value;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
28
|
+
return String(value);
|
|
29
|
+
}
|
|
30
|
+
// Reject objects to prevent toString exploits
|
|
31
|
+
throw new Error('Invalid value type for stringification');
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=html-escape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-escape.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/html-escape.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAaH,gCAEC;AAED,sCAeC;AA9BD,MAAM,YAAY,GAA2B;IAC3C,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;CACd,CAAC;AAEF,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAEtC,SAAgB,UAAU,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAgB,aAAa,CAAC,KAAU,EAAE,OAAkC;IAC1E,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACzD,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,8CAA8C;IAC9C,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC"}
|
package/src/utils/index.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
export * from './plural-helpers';
|
|
2
|
+
export * from './safe-object';
|
|
3
|
+
export * from './string-utils';
|
|
4
|
+
export * from './currency';
|
|
5
|
+
export * from './timezone';
|
|
6
|
+
export * from './validation';
|
|
7
|
+
export * from './html-escape';
|
|
8
|
+
export * from './lru-cache';
|
|
2
9
|
//# sourceMappingURL=index.d.ts.map
|
package/src/utils/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
|
package/src/utils/index.js
CHANGED
|
@@ -2,4 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
tslib_1.__exportStar(require("./plural-helpers"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./safe-object"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./string-utils"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./currency"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./timezone"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./validation"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./html-escape"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./lru-cache"), exports);
|
|
5
12
|
//# sourceMappingURL=index.js.map
|
package/src/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/index.ts"],"names":[],"mappings":";;;AAAA,2DAAiC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/index.ts"],"names":[],"mappings":";;;AAAA,2DAAiC;AACjC,wDAA8B;AAC9B,yDAA+B;AAC/B,qDAA2B;AAC3B,qDAA2B;AAC3B,uDAA6B;AAC7B,wDAA8B;AAC9B,sDAA4B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple LRU Cache implementation for security
|
|
3
|
+
*/
|
|
4
|
+
export declare class LRUCache<K, V> {
|
|
5
|
+
private cache;
|
|
6
|
+
private readonly maxSize;
|
|
7
|
+
constructor(maxSize?: number);
|
|
8
|
+
get(key: K): V | undefined;
|
|
9
|
+
set(key: K, value: V): void;
|
|
10
|
+
clear(): void;
|
|
11
|
+
size(): number;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=lru-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-cache.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/lru-cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC;IACxB,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,MAAa;IAIlC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAU1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAiB3B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Simple LRU Cache implementation for security
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LRUCache = void 0;
|
|
7
|
+
class LRUCache {
|
|
8
|
+
cache = new Map();
|
|
9
|
+
maxSize;
|
|
10
|
+
constructor(maxSize = 1000) {
|
|
11
|
+
this.maxSize = maxSize;
|
|
12
|
+
}
|
|
13
|
+
get(key) {
|
|
14
|
+
const value = this.cache.get(key);
|
|
15
|
+
if (value !== undefined) {
|
|
16
|
+
// Move to end (most recently used)
|
|
17
|
+
this.cache.delete(key);
|
|
18
|
+
this.cache.set(key, value);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
set(key, value) {
|
|
23
|
+
// Remove if exists to update position
|
|
24
|
+
if (this.cache.has(key)) {
|
|
25
|
+
this.cache.delete(key);
|
|
26
|
+
}
|
|
27
|
+
// Remove oldest if at capacity
|
|
28
|
+
if (this.cache.size >= this.maxSize) {
|
|
29
|
+
const firstKey = this.cache.keys().next().value;
|
|
30
|
+
if (firstKey !== undefined) {
|
|
31
|
+
this.cache.delete(firstKey);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
this.cache.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
clear() {
|
|
37
|
+
this.cache.clear();
|
|
38
|
+
}
|
|
39
|
+
size() {
|
|
40
|
+
return this.cache.size;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.LRUCache = LRUCache;
|
|
44
|
+
//# sourceMappingURL=lru-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-cache.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/lru-cache.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,MAAa,QAAQ;IACX,KAAK,GAAG,IAAI,GAAG,EAAQ,CAAC;IACf,OAAO,CAAS;IAEjC,YAAY,UAAkB,IAAI;QAChC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,GAAG,CAAC,GAAM;QACR,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,mCAAmC;YACnC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,KAAQ;QAClB,sCAAsC;QACtC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAED,+BAA+B;QAC/B,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAChD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AA1CD,4BA0CC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe object utilities to prevent prototype pollution
|
|
3
|
+
*/
|
|
4
|
+
export declare function createSafeObject<T = any>(): Record<string, T>;
|
|
5
|
+
export declare function isDangerousKey(key: string): boolean;
|
|
6
|
+
export declare function safeAssign<T extends Record<string, any>>(target: T, ...sources: Array<Record<string, any> | undefined>): T;
|
|
7
|
+
export declare function safeObjectEntries<T>(obj: Record<string, T>): Array<[string, T]>;
|
|
8
|
+
export declare function validateObjectKeys(obj: Record<string, any>): void;
|
|
9
|
+
//# sourceMappingURL=safe-object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-object.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/safe-object.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,GAAG,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAE7D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtD,MAAM,EAAE,CAAC,EACT,GAAG,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,GACjD,CAAC,CAWH;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAE/E;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAUjE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Safe object utilities to prevent prototype pollution
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createSafeObject = createSafeObject;
|
|
7
|
+
exports.isDangerousKey = isDangerousKey;
|
|
8
|
+
exports.safeAssign = safeAssign;
|
|
9
|
+
exports.safeObjectEntries = safeObjectEntries;
|
|
10
|
+
exports.validateObjectKeys = validateObjectKeys;
|
|
11
|
+
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
12
|
+
function createSafeObject() {
|
|
13
|
+
return Object.create(null);
|
|
14
|
+
}
|
|
15
|
+
function isDangerousKey(key) {
|
|
16
|
+
return DANGEROUS_KEYS.includes(key);
|
|
17
|
+
}
|
|
18
|
+
function safeAssign(target, ...sources) {
|
|
19
|
+
for (const source of sources) {
|
|
20
|
+
if (!source)
|
|
21
|
+
continue;
|
|
22
|
+
for (const key of Object.keys(source)) {
|
|
23
|
+
if (!isDangerousKey(key)) {
|
|
24
|
+
target[key] = source[key];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return target;
|
|
29
|
+
}
|
|
30
|
+
function safeObjectEntries(obj) {
|
|
31
|
+
return Object.entries(obj).filter(([key]) => !isDangerousKey(key));
|
|
32
|
+
}
|
|
33
|
+
function validateObjectKeys(obj) {
|
|
34
|
+
if (!obj || typeof obj !== 'object')
|
|
35
|
+
return;
|
|
36
|
+
// Check for dangerous keys in Object.keys and getOwnPropertyNames
|
|
37
|
+
const allKeys = [...Object.keys(obj), ...Object.getOwnPropertyNames(obj)];
|
|
38
|
+
for (const key of allKeys) {
|
|
39
|
+
if (DANGEROUS_KEYS.includes(key)) {
|
|
40
|
+
throw new Error(`Dangerous key detected: ${key}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=safe-object.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-object.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-i18n-lib/src/utils/safe-object.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAIH,4CAEC;AAED,wCAEC;AAED,gCAcC;AAED,8CAEC;AAED,gDAUC;AAxCD,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;AAEjE,SAAgB,gBAAgB;IAC9B,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,cAAc,CAAC,GAAW;IACxC,OAAO,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,SAAgB,UAAU,CACxB,MAAS,EACT,GAAG,OAA+C;IAElD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM;YAAE,SAAS;QAEtB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAc,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,iBAAiB,CAAI,GAAsB;IACzD,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,SAAgB,kBAAkB,CAAC,GAAwB;IACzD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO;IAE5C,kEAAkE;IAClE,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1E,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* String utility functions
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
4
|
+
export interface ReplaceVariablesOptions {
|
|
5
|
+
escapeHtml?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function replaceVariables(str: string, vars?: Record<string, any>, constants?: Record<string, any>, options?: ReplaceVariablesOptions): string;
|
|
5
8
|
export declare function isTemplate(key: string): boolean;
|
|
6
9
|
//# sourceMappingURL=string-utils.d.ts.map
|