lifecycleion 0.0.4 → 0.0.6

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
@@ -1,4 +1,4 @@
1
- # Lifecycleion v0.0.4
1
+ # Lifecycleion v0.0.6
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/lifecycleion.svg)](https://badge.fury.io/js/lifecycleion)
4
4
 
@@ -23,6 +23,46 @@ __export(curly_brackets_exports, {
23
23
  CurlyBrackets: () => CurlyBrackets
24
24
  });
25
25
  module.exports = __toCommonJS(curly_brackets_exports);
26
+
27
+ // src/lib/internal/path-utils.ts
28
+ var PATH_SEGMENT_PATTERN = /(\w+)|\[(\d+)\]|\["((?:[^"\\]|\\.)*)"\]|\['((?:[^'\\]|\\.)*)'\]/y;
29
+ function unescapeQuotedPathPart(value) {
30
+ return value.replace(/\\(["'\\])/g, "$1");
31
+ }
32
+ function getPathParts(path) {
33
+ const parts = [];
34
+ let index = 0;
35
+ while (index < path.length) {
36
+ if (path[index] === ".") {
37
+ index++;
38
+ if (index >= path.length) {
39
+ return null;
40
+ }
41
+ }
42
+ PATH_SEGMENT_PATTERN.lastIndex = index;
43
+ const match = PATH_SEGMENT_PATTERN.exec(path);
44
+ if (!match) {
45
+ return null;
46
+ }
47
+ if (match[1] !== void 0) {
48
+ parts.push(match[1]);
49
+ } else if (match[2] !== void 0) {
50
+ parts.push(match[2]);
51
+ } else if (match[3] !== void 0) {
52
+ parts.push(unescapeQuotedPathPart(match[3]));
53
+ } else if (match[4] !== void 0) {
54
+ parts.push(unescapeQuotedPathPart(match[4]));
55
+ }
56
+ index = PATH_SEGMENT_PATTERN.lastIndex;
57
+ if (index < path.length && path[index] !== "." && path[index] !== "[") {
58
+ return null;
59
+ }
60
+ }
61
+ return parts;
62
+ }
63
+
64
+ // src/lib/curly-brackets.ts
65
+ var PLACEHOLDER_PATTERN = /(?:\\)?{{(\s*[^{}]+?\s*)(?:\\)?\s*}}/g;
26
66
  var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
27
67
  if (!str.includes("{{")) {
28
68
  return str;
@@ -31,9 +71,8 @@ var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
31
71
  return compiled(locals);
32
72
  };
33
73
  CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
34
- const pattern = /(?:\\)?{{(\s*[\w.]+?)(?:\\)?\s*}}/g;
35
74
  return (locals) => {
36
- return str.replace(pattern, (match, p1) => {
75
+ return str.replace(PLACEHOLDER_PATTERN, (match, p1) => {
37
76
  if (typeof p1 !== "string") {
38
77
  return match;
39
78
  }
@@ -50,7 +89,10 @@ CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
50
89
  return "{{" + p1.trim() + "}}";
51
90
  }
52
91
  const key = p1.trim();
53
- const parts = key.split(".");
92
+ const parts = getPathParts(key);
93
+ if (!parts || parts.length === 0) {
94
+ return match;
95
+ }
54
96
  let replacement = locals;
55
97
  for (const part of parts) {
56
98
  if (replacement !== void 0 && replacement !== null && typeof replacement === "object" && part in replacement) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/curly-brackets.ts"],"sourcesContent":["export type TemplateFunction = (locals: Record<string, unknown>) => string;\n\ninterface CurlyBracketsFunction {\n (str?: string, locals?: Record<string, unknown>, fallback?: string): string;\n compileTemplate: (str: string, fallback?: string) => TemplateFunction;\n escape: (str: string) => string;\n}\n\n/**\n * Processes a template string, replacing placeholders with corresponding values from a provided object.\n *\n * @param {string} str - The template string to process.\n * @param locals - An object containing key-value pairs for placeholder replacement.\n * @param fallback - A default string to use when a placeholder's corresponding value is not found.\n * @returns - The processed string with placeholders replaced by their corresponding values.\n */\n\nconst CurlyBrackets: CurlyBracketsFunction = function (\n str: string = '',\n locals: Record<string, unknown> = {},\n fallback: string = '(null)',\n): string {\n // Short-circuit if no brackets - no need to process\n if (!str.includes('{{')) {\n return str;\n }\n\n const compiled = CurlyBrackets.compileTemplate(str, fallback);\n\n return compiled(locals);\n} as CurlyBracketsFunction;\n\n/**\n * Compiles a template string into a reusable function, which can be called with different sets of locals.\n * This is more efficient when you have a template that you want to use with different sets of locals,\n * as it avoids the overhead of parsing the template string each time it is used.\n *\n * @param {string} str - The template string to compile.\n * @param {string} fallback - A default string to use when a placeholder's corresponding value is not found in locals.\n * @returns A function that takes an object of locals and returns a processed string.\n */\n\nCurlyBrackets.compileTemplate = function (\n str: string,\n fallback: string = '(null)',\n): TemplateFunction {\n const pattern = /(?:\\\\)?{{(\\s*[\\w.]+?)(?:\\\\)?\\s*}}/g;\n\n return (locals: Record<string, unknown>): string => {\n return str.replace(pattern, (match, p1: string) => {\n if (typeof p1 !== 'string') {\n return match;\n }\n\n const hasLeadingEscape = match.startsWith('\\\\');\n const hasEndingEscape = match.endsWith('\\\\}}');\n const isFullyEscaped = hasLeadingEscape && hasEndingEscape;\n\n if (isFullyEscaped) {\n return match.slice(1, -3) + '}}';\n }\n\n if (hasLeadingEscape) {\n return match.slice(1);\n }\n\n if (hasEndingEscape) {\n return '{{' + p1.trim() + '}}';\n }\n\n const key = p1.trim();\n const parts = key.split('.');\n\n // Use a more specific approach to ensure the type is consistent\n let replacement: unknown = locals;\n\n for (const part of parts) {\n if (\n replacement !== undefined &&\n replacement !== null &&\n typeof replacement === 'object' &&\n part in replacement\n ) {\n replacement = (replacement as Record<string, unknown>)[part];\n } else {\n replacement = undefined;\n break;\n }\n }\n\n if (replacement === undefined || replacement === null) {\n return fallback;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return String(replacement);\n });\n };\n};\n\n/**\n * Escapes placeholders in a string by prefixing them with a backslash, preventing them from being replaced when processed.\n *\n * @param {string} str - The string in which to escape placeholders.\n * @returns {string} - The string with placeholders escaped.\n */\n\nCurlyBrackets.escape = function (str: string): string {\n // Use a regex to replace instances of {{ and }} that are not already preceded by a backslash\n return str\n .replace(/(\\\\)?{{/g, (match, backslash) => (backslash ? match : '\\\\{{'))\n .replace(/(\\\\)?}}/g, (match, backslash) => (backslash ? match : '\\\\}}'));\n};\n\nexport { CurlyBrackets };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,IAAM,gBAAuC,SAC3C,MAAc,IACd,SAAkC,CAAC,GACnC,WAAmB,UACX;AAER,MAAI,CAAC,IAAI,SAAS,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,cAAc,gBAAgB,KAAK,QAAQ;AAE5D,SAAO,SAAS,MAAM;AACxB;AAYA,cAAc,kBAAkB,SAC9B,KACA,WAAmB,UACD;AAClB,QAAM,UAAU;AAEhB,SAAO,CAAC,WAA4C;AAClD,WAAO,IAAI,QAAQ,SAAS,CAAC,OAAO,OAAe;AACjD,UAAI,OAAO,OAAO,UAAU;AAC1B,eAAO;AAAA,MACT;AAEA,YAAM,mBAAmB,MAAM,WAAW,IAAI;AAC9C,YAAM,kBAAkB,MAAM,SAAS,MAAM;AAC7C,YAAM,iBAAiB,oBAAoB;AAE3C,UAAI,gBAAgB;AAClB,eAAO,MAAM,MAAM,GAAG,EAAE,IAAI;AAAA,MAC9B;AAEA,UAAI,kBAAkB;AACpB,eAAO,MAAM,MAAM,CAAC;AAAA,MACtB;AAEA,UAAI,iBAAiB;AACnB,eAAO,OAAO,GAAG,KAAK,IAAI;AAAA,MAC5B;AAEA,YAAM,MAAM,GAAG,KAAK;AACpB,YAAM,QAAQ,IAAI,MAAM,GAAG;AAG3B,UAAI,cAAuB;AAE3B,iBAAW,QAAQ,OAAO;AACxB,YACE,gBAAgB,UAChB,gBAAgB,QAChB,OAAO,gBAAgB,YACvB,QAAQ,aACR;AACA,wBAAe,YAAwC,IAAI;AAAA,QAC7D,OAAO;AACL,wBAAc;AACd;AAAA,QACF;AAAA,MACF;AAEA,UAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,eAAO;AAAA,MACT;AAGA,aAAO,OAAO,WAAW;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AASA,cAAc,SAAS,SAAU,KAAqB;AAEpD,SAAO,IACJ,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO,EACtE,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO;AAC3E;","names":[]}
1
+ {"version":3,"sources":["../../src/lib/curly-brackets.ts","../../src/lib/internal/path-utils.ts"],"sourcesContent":["import { getPathParts } from './internal/path-utils';\n\nexport type TemplateFunction = (locals: Record<string, unknown>) => string;\n\ninterface CurlyBracketsFunction {\n (str?: string, locals?: Record<string, unknown>, fallback?: string): string;\n compileTemplate: (str: string, fallback?: string) => TemplateFunction;\n escape: (str: string) => string;\n}\n\nconst PLACEHOLDER_PATTERN = /(?:\\\\)?{{(\\s*[^{}]+?\\s*)(?:\\\\)?\\s*}}/g;\n\n/**\n * Processes a template string, replacing placeholders with corresponding values from a provided object.\n *\n * @param {string} str - The template string to process.\n * @param locals - An object containing key-value pairs for placeholder replacement.\n * @param fallback - A default string to use when a placeholder's corresponding value is not found.\n * @returns - The processed string with placeholders replaced by their corresponding values.\n */\n\nconst CurlyBrackets: CurlyBracketsFunction = function (\n str: string = '',\n locals: Record<string, unknown> = {},\n fallback: string = '(null)',\n): string {\n // Short-circuit if no brackets - no need to process\n if (!str.includes('{{')) {\n return str;\n }\n\n const compiled = CurlyBrackets.compileTemplate(str, fallback);\n\n return compiled(locals);\n} as CurlyBracketsFunction;\n\n/**\n * Compiles a template string into a reusable function, which can be called with different sets of locals.\n * This is more efficient when you have a template that you want to use with different sets of locals,\n * as it avoids the overhead of parsing the template string each time it is used.\n *\n * @param {string} str - The template string to compile.\n * @param {string} fallback - A default string to use when a placeholder's corresponding value is not found in locals.\n * @returns A function that takes an object of locals and returns a processed string.\n */\n\nCurlyBrackets.compileTemplate = function (\n str: string,\n fallback: string = '(null)',\n): TemplateFunction {\n return (locals: Record<string, unknown>): string => {\n return str.replace(PLACEHOLDER_PATTERN, (match, p1: string) => {\n if (typeof p1 !== 'string') {\n return match;\n }\n\n const hasLeadingEscape = match.startsWith('\\\\');\n const hasEndingEscape = match.endsWith('\\\\}}');\n const isFullyEscaped = hasLeadingEscape && hasEndingEscape;\n\n if (isFullyEscaped) {\n return match.slice(1, -3) + '}}';\n }\n\n if (hasLeadingEscape) {\n return match.slice(1);\n }\n\n if (hasEndingEscape) {\n return '{{' + p1.trim() + '}}';\n }\n\n const key = p1.trim();\n const parts = getPathParts(key);\n\n if (!parts || parts.length === 0) {\n return match;\n }\n\n // Use a more specific approach to ensure the type is consistent\n let replacement: unknown = locals;\n\n for (const part of parts) {\n if (\n replacement !== undefined &&\n replacement !== null &&\n typeof replacement === 'object' &&\n part in replacement\n ) {\n replacement = (replacement as Record<string, unknown>)[part];\n } else {\n replacement = undefined;\n break;\n }\n }\n\n if (replacement === undefined || replacement === null) {\n return fallback;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return String(replacement);\n });\n };\n};\n\n/**\n * Escapes placeholders in a string by prefixing them with a backslash, preventing them from being replaced when processed.\n *\n * @param {string} str - The string in which to escape placeholders.\n * @returns {string} - The string with placeholders escaped.\n */\n\nCurlyBrackets.escape = function (str: string): string {\n // Use a regex to replace instances of {{ and }} that are not already preceded by a backslash\n return str\n .replace(/(\\\\)?{{/g, (match, backslash) => (backslash ? match : '\\\\{{'))\n .replace(/(\\\\)?}}/g, (match, backslash) => (backslash ? match : '\\\\}}'));\n};\n\nexport { CurlyBrackets };\n","const PATH_SEGMENT_PATTERN =\n /(\\w+)|\\[(\\d+)\\]|\\[\"((?:[^\"\\\\]|\\\\.)*)\"\\]|\\['((?:[^'\\\\]|\\\\.)*)'\\]/y;\n\nfunction unescapeQuotedPathPart(value: string): string {\n return value.replace(/\\\\([\"'\\\\])/g, '$1');\n}\n\n/**\n * Parses a mixed object/array path such as \"user.roles[0].name\" into lookup parts.\n */\nexport function getPathParts(path: string): string[] | null {\n const parts: string[] = [];\n let index = 0;\n\n while (index < path.length) {\n if (path[index] === '.') {\n index++;\n\n if (index >= path.length) {\n return null;\n }\n }\n\n PATH_SEGMENT_PATTERN.lastIndex = index;\n const match = PATH_SEGMENT_PATTERN.exec(path);\n\n if (!match) {\n return null;\n }\n\n if (match[1] !== undefined) {\n parts.push(match[1]);\n } else if (match[2] !== undefined) {\n parts.push(match[2]);\n } else if (match[3] !== undefined) {\n parts.push(unescapeQuotedPathPart(match[3]));\n } else if (match[4] !== undefined) {\n parts.push(unescapeQuotedPathPart(match[4]));\n }\n\n index = PATH_SEGMENT_PATTERN.lastIndex;\n\n if (index < path.length && path[index] !== '.' && path[index] !== '[') {\n return null;\n }\n }\n\n return parts;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAM,uBACJ;AAEF,SAAS,uBAAuB,OAAuB;AACrD,SAAO,MAAM,QAAQ,eAAe,IAAI;AAC1C;AAKO,SAAS,aAAa,MAA+B;AAC1D,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAQ;AAEZ,SAAO,QAAQ,KAAK,QAAQ;AAC1B,QAAI,KAAK,KAAK,MAAM,KAAK;AACvB;AAEA,UAAI,SAAS,KAAK,QAAQ;AACxB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,yBAAqB,YAAY;AACjC,UAAM,QAAQ,qBAAqB,KAAK,IAAI;AAE5C,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,CAAC,MAAM,QAAW;AAC1B,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,uBAAuB,MAAM,CAAC,CAAC,CAAC;AAAA,IAC7C,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,uBAAuB,MAAM,CAAC,CAAC,CAAC;AAAA,IAC7C;AAEA,YAAQ,qBAAqB;AAE7B,QAAI,QAAQ,KAAK,UAAU,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,MAAM,KAAK;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;ADtCA,IAAM,sBAAsB;AAW5B,IAAM,gBAAuC,SAC3C,MAAc,IACd,SAAkC,CAAC,GACnC,WAAmB,UACX;AAER,MAAI,CAAC,IAAI,SAAS,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,cAAc,gBAAgB,KAAK,QAAQ;AAE5D,SAAO,SAAS,MAAM;AACxB;AAYA,cAAc,kBAAkB,SAC9B,KACA,WAAmB,UACD;AAClB,SAAO,CAAC,WAA4C;AAClD,WAAO,IAAI,QAAQ,qBAAqB,CAAC,OAAO,OAAe;AAC7D,UAAI,OAAO,OAAO,UAAU;AAC1B,eAAO;AAAA,MACT;AAEA,YAAM,mBAAmB,MAAM,WAAW,IAAI;AAC9C,YAAM,kBAAkB,MAAM,SAAS,MAAM;AAC7C,YAAM,iBAAiB,oBAAoB;AAE3C,UAAI,gBAAgB;AAClB,eAAO,MAAM,MAAM,GAAG,EAAE,IAAI;AAAA,MAC9B;AAEA,UAAI,kBAAkB;AACpB,eAAO,MAAM,MAAM,CAAC;AAAA,MACtB;AAEA,UAAI,iBAAiB;AACnB,eAAO,OAAO,GAAG,KAAK,IAAI;AAAA,MAC5B;AAEA,YAAM,MAAM,GAAG,KAAK;AACpB,YAAM,QAAQ,aAAa,GAAG;AAE9B,UAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,eAAO;AAAA,MACT;AAGA,UAAI,cAAuB;AAE3B,iBAAW,QAAQ,OAAO;AACxB,YACE,gBAAgB,UAChB,gBAAgB,QAChB,OAAO,gBAAgB,YACvB,QAAQ,aACR;AACA,wBAAe,YAAwC,IAAI;AAAA,QAC7D,OAAO;AACL,wBAAc;AACd;AAAA,QACF;AAAA,MACF;AAEA,UAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,eAAO;AAAA,MACT;AAGA,aAAO,OAAO,WAAW;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AASA,cAAc,SAAS,SAAU,KAAqB;AAEpD,SAAO,IACJ,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO,EACtE,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO;AAC3E;","names":[]}
@@ -1,4 +1,42 @@
1
+ // src/lib/internal/path-utils.ts
2
+ var PATH_SEGMENT_PATTERN = /(\w+)|\[(\d+)\]|\["((?:[^"\\]|\\.)*)"\]|\['((?:[^'\\]|\\.)*)'\]/y;
3
+ function unescapeQuotedPathPart(value) {
4
+ return value.replace(/\\(["'\\])/g, "$1");
5
+ }
6
+ function getPathParts(path) {
7
+ const parts = [];
8
+ let index = 0;
9
+ while (index < path.length) {
10
+ if (path[index] === ".") {
11
+ index++;
12
+ if (index >= path.length) {
13
+ return null;
14
+ }
15
+ }
16
+ PATH_SEGMENT_PATTERN.lastIndex = index;
17
+ const match = PATH_SEGMENT_PATTERN.exec(path);
18
+ if (!match) {
19
+ return null;
20
+ }
21
+ if (match[1] !== void 0) {
22
+ parts.push(match[1]);
23
+ } else if (match[2] !== void 0) {
24
+ parts.push(match[2]);
25
+ } else if (match[3] !== void 0) {
26
+ parts.push(unescapeQuotedPathPart(match[3]));
27
+ } else if (match[4] !== void 0) {
28
+ parts.push(unescapeQuotedPathPart(match[4]));
29
+ }
30
+ index = PATH_SEGMENT_PATTERN.lastIndex;
31
+ if (index < path.length && path[index] !== "." && path[index] !== "[") {
32
+ return null;
33
+ }
34
+ }
35
+ return parts;
36
+ }
37
+
1
38
  // src/lib/curly-brackets.ts
39
+ var PLACEHOLDER_PATTERN = /(?:\\)?{{(\s*[^{}]+?\s*)(?:\\)?\s*}}/g;
2
40
  var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
3
41
  if (!str.includes("{{")) {
4
42
  return str;
@@ -7,9 +45,8 @@ var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
7
45
  return compiled(locals);
8
46
  };
9
47
  CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
10
- const pattern = /(?:\\)?{{(\s*[\w.]+?)(?:\\)?\s*}}/g;
11
48
  return (locals) => {
12
- return str.replace(pattern, (match, p1) => {
49
+ return str.replace(PLACEHOLDER_PATTERN, (match, p1) => {
13
50
  if (typeof p1 !== "string") {
14
51
  return match;
15
52
  }
@@ -26,7 +63,10 @@ CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
26
63
  return "{{" + p1.trim() + "}}";
27
64
  }
28
65
  const key = p1.trim();
29
- const parts = key.split(".");
66
+ const parts = getPathParts(key);
67
+ if (!parts || parts.length === 0) {
68
+ return match;
69
+ }
30
70
  let replacement = locals;
31
71
  for (const part of parts) {
32
72
  if (replacement !== void 0 && replacement !== null && typeof replacement === "object" && part in replacement) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/curly-brackets.ts"],"sourcesContent":["export type TemplateFunction = (locals: Record<string, unknown>) => string;\n\ninterface CurlyBracketsFunction {\n (str?: string, locals?: Record<string, unknown>, fallback?: string): string;\n compileTemplate: (str: string, fallback?: string) => TemplateFunction;\n escape: (str: string) => string;\n}\n\n/**\n * Processes a template string, replacing placeholders with corresponding values from a provided object.\n *\n * @param {string} str - The template string to process.\n * @param locals - An object containing key-value pairs for placeholder replacement.\n * @param fallback - A default string to use when a placeholder's corresponding value is not found.\n * @returns - The processed string with placeholders replaced by their corresponding values.\n */\n\nconst CurlyBrackets: CurlyBracketsFunction = function (\n str: string = '',\n locals: Record<string, unknown> = {},\n fallback: string = '(null)',\n): string {\n // Short-circuit if no brackets - no need to process\n if (!str.includes('{{')) {\n return str;\n }\n\n const compiled = CurlyBrackets.compileTemplate(str, fallback);\n\n return compiled(locals);\n} as CurlyBracketsFunction;\n\n/**\n * Compiles a template string into a reusable function, which can be called with different sets of locals.\n * This is more efficient when you have a template that you want to use with different sets of locals,\n * as it avoids the overhead of parsing the template string each time it is used.\n *\n * @param {string} str - The template string to compile.\n * @param {string} fallback - A default string to use when a placeholder's corresponding value is not found in locals.\n * @returns A function that takes an object of locals and returns a processed string.\n */\n\nCurlyBrackets.compileTemplate = function (\n str: string,\n fallback: string = '(null)',\n): TemplateFunction {\n const pattern = /(?:\\\\)?{{(\\s*[\\w.]+?)(?:\\\\)?\\s*}}/g;\n\n return (locals: Record<string, unknown>): string => {\n return str.replace(pattern, (match, p1: string) => {\n if (typeof p1 !== 'string') {\n return match;\n }\n\n const hasLeadingEscape = match.startsWith('\\\\');\n const hasEndingEscape = match.endsWith('\\\\}}');\n const isFullyEscaped = hasLeadingEscape && hasEndingEscape;\n\n if (isFullyEscaped) {\n return match.slice(1, -3) + '}}';\n }\n\n if (hasLeadingEscape) {\n return match.slice(1);\n }\n\n if (hasEndingEscape) {\n return '{{' + p1.trim() + '}}';\n }\n\n const key = p1.trim();\n const parts = key.split('.');\n\n // Use a more specific approach to ensure the type is consistent\n let replacement: unknown = locals;\n\n for (const part of parts) {\n if (\n replacement !== undefined &&\n replacement !== null &&\n typeof replacement === 'object' &&\n part in replacement\n ) {\n replacement = (replacement as Record<string, unknown>)[part];\n } else {\n replacement = undefined;\n break;\n }\n }\n\n if (replacement === undefined || replacement === null) {\n return fallback;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return String(replacement);\n });\n };\n};\n\n/**\n * Escapes placeholders in a string by prefixing them with a backslash, preventing them from being replaced when processed.\n *\n * @param {string} str - The string in which to escape placeholders.\n * @returns {string} - The string with placeholders escaped.\n */\n\nCurlyBrackets.escape = function (str: string): string {\n // Use a regex to replace instances of {{ and }} that are not already preceded by a backslash\n return str\n .replace(/(\\\\)?{{/g, (match, backslash) => (backslash ? match : '\\\\{{'))\n .replace(/(\\\\)?}}/g, (match, backslash) => (backslash ? match : '\\\\}}'));\n};\n\nexport { CurlyBrackets };\n"],"mappings":";AAiBA,IAAM,gBAAuC,SAC3C,MAAc,IACd,SAAkC,CAAC,GACnC,WAAmB,UACX;AAER,MAAI,CAAC,IAAI,SAAS,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,cAAc,gBAAgB,KAAK,QAAQ;AAE5D,SAAO,SAAS,MAAM;AACxB;AAYA,cAAc,kBAAkB,SAC9B,KACA,WAAmB,UACD;AAClB,QAAM,UAAU;AAEhB,SAAO,CAAC,WAA4C;AAClD,WAAO,IAAI,QAAQ,SAAS,CAAC,OAAO,OAAe;AACjD,UAAI,OAAO,OAAO,UAAU;AAC1B,eAAO;AAAA,MACT;AAEA,YAAM,mBAAmB,MAAM,WAAW,IAAI;AAC9C,YAAM,kBAAkB,MAAM,SAAS,MAAM;AAC7C,YAAM,iBAAiB,oBAAoB;AAE3C,UAAI,gBAAgB;AAClB,eAAO,MAAM,MAAM,GAAG,EAAE,IAAI;AAAA,MAC9B;AAEA,UAAI,kBAAkB;AACpB,eAAO,MAAM,MAAM,CAAC;AAAA,MACtB;AAEA,UAAI,iBAAiB;AACnB,eAAO,OAAO,GAAG,KAAK,IAAI;AAAA,MAC5B;AAEA,YAAM,MAAM,GAAG,KAAK;AACpB,YAAM,QAAQ,IAAI,MAAM,GAAG;AAG3B,UAAI,cAAuB;AAE3B,iBAAW,QAAQ,OAAO;AACxB,YACE,gBAAgB,UAChB,gBAAgB,QAChB,OAAO,gBAAgB,YACvB,QAAQ,aACR;AACA,wBAAe,YAAwC,IAAI;AAAA,QAC7D,OAAO;AACL,wBAAc;AACd;AAAA,QACF;AAAA,MACF;AAEA,UAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,eAAO;AAAA,MACT;AAGA,aAAO,OAAO,WAAW;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AASA,cAAc,SAAS,SAAU,KAAqB;AAEpD,SAAO,IACJ,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO,EACtE,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO;AAC3E;","names":[]}
1
+ {"version":3,"sources":["../../src/lib/internal/path-utils.ts","../../src/lib/curly-brackets.ts"],"sourcesContent":["const PATH_SEGMENT_PATTERN =\n /(\\w+)|\\[(\\d+)\\]|\\[\"((?:[^\"\\\\]|\\\\.)*)\"\\]|\\['((?:[^'\\\\]|\\\\.)*)'\\]/y;\n\nfunction unescapeQuotedPathPart(value: string): string {\n return value.replace(/\\\\([\"'\\\\])/g, '$1');\n}\n\n/**\n * Parses a mixed object/array path such as \"user.roles[0].name\" into lookup parts.\n */\nexport function getPathParts(path: string): string[] | null {\n const parts: string[] = [];\n let index = 0;\n\n while (index < path.length) {\n if (path[index] === '.') {\n index++;\n\n if (index >= path.length) {\n return null;\n }\n }\n\n PATH_SEGMENT_PATTERN.lastIndex = index;\n const match = PATH_SEGMENT_PATTERN.exec(path);\n\n if (!match) {\n return null;\n }\n\n if (match[1] !== undefined) {\n parts.push(match[1]);\n } else if (match[2] !== undefined) {\n parts.push(match[2]);\n } else if (match[3] !== undefined) {\n parts.push(unescapeQuotedPathPart(match[3]));\n } else if (match[4] !== undefined) {\n parts.push(unescapeQuotedPathPart(match[4]));\n }\n\n index = PATH_SEGMENT_PATTERN.lastIndex;\n\n if (index < path.length && path[index] !== '.' && path[index] !== '[') {\n return null;\n }\n }\n\n return parts;\n}\n","import { getPathParts } from './internal/path-utils';\n\nexport type TemplateFunction = (locals: Record<string, unknown>) => string;\n\ninterface CurlyBracketsFunction {\n (str?: string, locals?: Record<string, unknown>, fallback?: string): string;\n compileTemplate: (str: string, fallback?: string) => TemplateFunction;\n escape: (str: string) => string;\n}\n\nconst PLACEHOLDER_PATTERN = /(?:\\\\)?{{(\\s*[^{}]+?\\s*)(?:\\\\)?\\s*}}/g;\n\n/**\n * Processes a template string, replacing placeholders with corresponding values from a provided object.\n *\n * @param {string} str - The template string to process.\n * @param locals - An object containing key-value pairs for placeholder replacement.\n * @param fallback - A default string to use when a placeholder's corresponding value is not found.\n * @returns - The processed string with placeholders replaced by their corresponding values.\n */\n\nconst CurlyBrackets: CurlyBracketsFunction = function (\n str: string = '',\n locals: Record<string, unknown> = {},\n fallback: string = '(null)',\n): string {\n // Short-circuit if no brackets - no need to process\n if (!str.includes('{{')) {\n return str;\n }\n\n const compiled = CurlyBrackets.compileTemplate(str, fallback);\n\n return compiled(locals);\n} as CurlyBracketsFunction;\n\n/**\n * Compiles a template string into a reusable function, which can be called with different sets of locals.\n * This is more efficient when you have a template that you want to use with different sets of locals,\n * as it avoids the overhead of parsing the template string each time it is used.\n *\n * @param {string} str - The template string to compile.\n * @param {string} fallback - A default string to use when a placeholder's corresponding value is not found in locals.\n * @returns A function that takes an object of locals and returns a processed string.\n */\n\nCurlyBrackets.compileTemplate = function (\n str: string,\n fallback: string = '(null)',\n): TemplateFunction {\n return (locals: Record<string, unknown>): string => {\n return str.replace(PLACEHOLDER_PATTERN, (match, p1: string) => {\n if (typeof p1 !== 'string') {\n return match;\n }\n\n const hasLeadingEscape = match.startsWith('\\\\');\n const hasEndingEscape = match.endsWith('\\\\}}');\n const isFullyEscaped = hasLeadingEscape && hasEndingEscape;\n\n if (isFullyEscaped) {\n return match.slice(1, -3) + '}}';\n }\n\n if (hasLeadingEscape) {\n return match.slice(1);\n }\n\n if (hasEndingEscape) {\n return '{{' + p1.trim() + '}}';\n }\n\n const key = p1.trim();\n const parts = getPathParts(key);\n\n if (!parts || parts.length === 0) {\n return match;\n }\n\n // Use a more specific approach to ensure the type is consistent\n let replacement: unknown = locals;\n\n for (const part of parts) {\n if (\n replacement !== undefined &&\n replacement !== null &&\n typeof replacement === 'object' &&\n part in replacement\n ) {\n replacement = (replacement as Record<string, unknown>)[part];\n } else {\n replacement = undefined;\n break;\n }\n }\n\n if (replacement === undefined || replacement === null) {\n return fallback;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return String(replacement);\n });\n };\n};\n\n/**\n * Escapes placeholders in a string by prefixing them with a backslash, preventing them from being replaced when processed.\n *\n * @param {string} str - The string in which to escape placeholders.\n * @returns {string} - The string with placeholders escaped.\n */\n\nCurlyBrackets.escape = function (str: string): string {\n // Use a regex to replace instances of {{ and }} that are not already preceded by a backslash\n return str\n .replace(/(\\\\)?{{/g, (match, backslash) => (backslash ? match : '\\\\{{'))\n .replace(/(\\\\)?}}/g, (match, backslash) => (backslash ? match : '\\\\}}'));\n};\n\nexport { CurlyBrackets };\n"],"mappings":";AAAA,IAAM,uBACJ;AAEF,SAAS,uBAAuB,OAAuB;AACrD,SAAO,MAAM,QAAQ,eAAe,IAAI;AAC1C;AAKO,SAAS,aAAa,MAA+B;AAC1D,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAQ;AAEZ,SAAO,QAAQ,KAAK,QAAQ;AAC1B,QAAI,KAAK,KAAK,MAAM,KAAK;AACvB;AAEA,UAAI,SAAS,KAAK,QAAQ;AACxB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,yBAAqB,YAAY;AACjC,UAAM,QAAQ,qBAAqB,KAAK,IAAI;AAE5C,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,CAAC,MAAM,QAAW;AAC1B,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,MAAM,CAAC,CAAC;AAAA,IACrB,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,uBAAuB,MAAM,CAAC,CAAC,CAAC;AAAA,IAC7C,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,uBAAuB,MAAM,CAAC,CAAC,CAAC;AAAA,IAC7C;AAEA,YAAQ,qBAAqB;AAE7B,QAAI,QAAQ,KAAK,UAAU,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,MAAM,KAAK;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;ACtCA,IAAM,sBAAsB;AAW5B,IAAM,gBAAuC,SAC3C,MAAc,IACd,SAAkC,CAAC,GACnC,WAAmB,UACX;AAER,MAAI,CAAC,IAAI,SAAS,IAAI,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,cAAc,gBAAgB,KAAK,QAAQ;AAE5D,SAAO,SAAS,MAAM;AACxB;AAYA,cAAc,kBAAkB,SAC9B,KACA,WAAmB,UACD;AAClB,SAAO,CAAC,WAA4C;AAClD,WAAO,IAAI,QAAQ,qBAAqB,CAAC,OAAO,OAAe;AAC7D,UAAI,OAAO,OAAO,UAAU;AAC1B,eAAO;AAAA,MACT;AAEA,YAAM,mBAAmB,MAAM,WAAW,IAAI;AAC9C,YAAM,kBAAkB,MAAM,SAAS,MAAM;AAC7C,YAAM,iBAAiB,oBAAoB;AAE3C,UAAI,gBAAgB;AAClB,eAAO,MAAM,MAAM,GAAG,EAAE,IAAI;AAAA,MAC9B;AAEA,UAAI,kBAAkB;AACpB,eAAO,MAAM,MAAM,CAAC;AAAA,MACtB;AAEA,UAAI,iBAAiB;AACnB,eAAO,OAAO,GAAG,KAAK,IAAI;AAAA,MAC5B;AAEA,YAAM,MAAM,GAAG,KAAK;AACpB,YAAM,QAAQ,aAAa,GAAG;AAE9B,UAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,eAAO;AAAA,MACT;AAGA,UAAI,cAAuB;AAE3B,iBAAW,QAAQ,OAAO;AACxB,YACE,gBAAgB,UAChB,gBAAgB,QAChB,OAAO,gBAAgB,YACvB,QAAQ,aACR;AACA,wBAAe,YAAwC,IAAI;AAAA,QAC7D,OAAO;AACL,wBAAc;AACd;AAAA,QACF;AAAA,MACF;AAEA,UAAI,gBAAgB,UAAa,gBAAgB,MAAM;AACrD,eAAO;AAAA,MACT;AAGA,aAAO,OAAO,WAAW;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AASA,cAAc,SAAS,SAAU,KAAqB;AAEpD,SAAO,IACJ,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO,EACtE,QAAQ,YAAY,CAAC,OAAO,cAAe,YAAY,QAAQ,MAAO;AAC3E;","names":[]}
@@ -934,7 +934,45 @@ function ms() {
934
934
  return Date.now();
935
935
  }
936
936
 
937
+ // src/lib/internal/path-utils.ts
938
+ var PATH_SEGMENT_PATTERN = /(\w+)|\[(\d+)\]|\["((?:[^"\\]|\\.)*)"\]|\['((?:[^'\\]|\\.)*)'\]/y;
939
+ function unescapeQuotedPathPart(value) {
940
+ return value.replace(/\\(["'\\])/g, "$1");
941
+ }
942
+ function getPathParts(path) {
943
+ const parts = [];
944
+ let index = 0;
945
+ while (index < path.length) {
946
+ if (path[index] === ".") {
947
+ index++;
948
+ if (index >= path.length) {
949
+ return null;
950
+ }
951
+ }
952
+ PATH_SEGMENT_PATTERN.lastIndex = index;
953
+ const match = PATH_SEGMENT_PATTERN.exec(path);
954
+ if (!match) {
955
+ return null;
956
+ }
957
+ if (match[1] !== void 0) {
958
+ parts.push(match[1]);
959
+ } else if (match[2] !== void 0) {
960
+ parts.push(match[2]);
961
+ } else if (match[3] !== void 0) {
962
+ parts.push(unescapeQuotedPathPart(match[3]));
963
+ } else if (match[4] !== void 0) {
964
+ parts.push(unescapeQuotedPathPart(match[4]));
965
+ }
966
+ index = PATH_SEGMENT_PATTERN.lastIndex;
967
+ if (index < path.length && path[index] !== "." && path[index] !== "[") {
968
+ return null;
969
+ }
970
+ }
971
+ return parts;
972
+ }
973
+
937
974
  // src/lib/curly-brackets.ts
975
+ var PLACEHOLDER_PATTERN = /(?:\\)?{{(\s*[^{}]+?\s*)(?:\\)?\s*}}/g;
938
976
  var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
939
977
  if (!str.includes("{{")) {
940
978
  return str;
@@ -943,9 +981,8 @@ var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
943
981
  return compiled(locals);
944
982
  };
945
983
  CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
946
- const pattern = /(?:\\)?{{(\s*[\w.]+?)(?:\\)?\s*}}/g;
947
984
  return (locals) => {
948
- return str.replace(pattern, (match, p1) => {
985
+ return str.replace(PLACEHOLDER_PATTERN, (match, p1) => {
949
986
  if (typeof p1 !== "string") {
950
987
  return match;
951
988
  }
@@ -962,7 +999,10 @@ CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
962
999
  return "{{" + p1.trim() + "}}";
963
1000
  }
964
1001
  const key = p1.trim();
965
- const parts = key.split(".");
1002
+ const parts = getPathParts(key);
1003
+ if (!parts || parts.length === 0) {
1004
+ return match;
1005
+ }
966
1006
  let replacement = locals;
967
1007
  for (const part of parts) {
968
1008
  if (replacement !== void 0 && replacement !== null && typeof replacement === "object" && part in replacement) {
@@ -1308,10 +1348,16 @@ var defaultRedactFunction = (_keyName, value) => {
1308
1348
  return "***REDACTED***";
1309
1349
  };
1310
1350
  function setNestedValue(obj, path, value) {
1311
- const parts = path.split(".");
1351
+ const parts = getPathParts(path);
1352
+ if (!parts || parts.length === 0) {
1353
+ return;
1354
+ }
1312
1355
  let current = obj;
1313
1356
  for (let i = 0; i < parts.length - 1; i++) {
1314
1357
  const part = parts[i];
1358
+ if (current === void 0 || current === null || typeof current !== "object" || !(part in current)) {
1359
+ return;
1360
+ }
1315
1361
  const next = current[part];
1316
1362
  if (next === void 0 || next === null || typeof next !== "object") {
1317
1363
  return;
@@ -1319,12 +1365,15 @@ function setNestedValue(obj, path, value) {
1319
1365
  current = next;
1320
1366
  }
1321
1367
  const lastPart = parts[parts.length - 1];
1322
- if (lastPart !== void 0 && lastPart in current) {
1368
+ if (lastPart !== void 0 && current !== void 0 && current !== null && typeof current === "object" && lastPart in current) {
1323
1369
  current[lastPart] = value;
1324
1370
  }
1325
1371
  }
1326
1372
  function getNestedValue(obj, path) {
1327
- const parts = path.split(".");
1373
+ const parts = getPathParts(path);
1374
+ if (!parts || parts.length === 0) {
1375
+ return void 0;
1376
+ }
1328
1377
  let current = obj;
1329
1378
  for (const part of parts) {
1330
1379
  if (current === void 0 || current === null || typeof current !== "object" || !(part in current)) {
@@ -1341,7 +1390,7 @@ function applyRedaction(params, redactedKeys, redactFunction) {
1341
1390
  const redactFn = redactFunction || defaultRedactFunction;
1342
1391
  const redactedParams = deepClone(params);
1343
1392
  for (const key of redactedKeys) {
1344
- if (key.includes(".")) {
1393
+ if (key.includes(".") || key.includes("[")) {
1345
1394
  const value = getNestedValue(redactedParams, key);
1346
1395
  if (value !== void 0) {
1347
1396
  const redactedValue = redactFn(key, value);