lifecycleion 0.0.6 → 0.0.8

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.6
1
+ # Lifecycleion v0.0.8
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/lifecycleion.svg)](https://badge.fury.io/js/lifecycleion)
4
4
 
@@ -89,6 +89,7 @@ Each library has comprehensive documentation in the [docs](./docs) folder. Click
89
89
  | [constants](./docs/constants.md) | `lifecycleion/constants` | Common string constants including whitespace helpers and Python-style character sets |
90
90
  | [curly-brackets](./docs/curly-brackets.md) | `lifecycleion/curly-brackets` | String templating with `{{placeholders}}`, fallbacks, escaping, and compiled templates |
91
91
  | [deep-clone](./docs/deep-clone.md) | `lifecycleion/deep-clone` | Deep clone utility with circular reference detection for objects, arrays, Maps, Sets, and more |
92
+ | [dev-mode](./docs/dev-mode.md) | `lifecycleion/dev-mode` | Runtime-settable dev/production mode flag with auto-detection from CLI args or NODE_ENV |
92
93
  | [error-to-string](./docs/error-to-string.md) | `lifecycleion/error-to-string` | Format errors into readable ASCII tables with support for nested info and sensitive field masking |
93
94
  | [event-emitter](./docs/event-emitter.md) | `lifecycleion/event-emitter` | Lightweight event emitter with protected and public variants, type safety, and memory management |
94
95
  | [id-helpers](./docs/id-helpers.md) | `lifecycleion/id-helpers` | Unified ID generation and validation for ObjectID, UUID v4, UUID v7, and ULID |
@@ -61,6 +61,14 @@ function getPathParts(path) {
61
61
  return parts;
62
62
  }
63
63
 
64
+ // src/lib/internal/stringify-template-value.ts
65
+ function stringifyTemplateValue(value) {
66
+ if (typeof value === "string") {
67
+ return value;
68
+ }
69
+ return String(value);
70
+ }
71
+
64
72
  // src/lib/curly-brackets.ts
65
73
  var PLACEHOLDER_PATTERN = /(?:\\)?{{(\s*[^{}]+?\s*)(?:\\)?\s*}}/g;
66
74
  var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
@@ -105,7 +113,7 @@ CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
105
113
  if (replacement === void 0 || replacement === null) {
106
114
  return fallback;
107
115
  }
108
- return String(replacement);
116
+ return stringifyTemplateValue(replacement);
109
117
  });
110
118
  };
111
119
  };
@@ -1 +1 @@
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
+ {"version":3,"sources":["../../src/lib/curly-brackets.ts","../../src/lib/internal/path-utils.ts","../../src/lib/internal/stringify-template-value.ts"],"sourcesContent":["import { getPathParts } from './internal/path-utils';\nimport { stringifyTemplateValue } from './internal/stringify-template-value';\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 return stringifyTemplateValue(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","/**\n * Normalizes values to the same string representation used by template rendering.\n */\nexport function stringifyTemplateValue(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n\n return String(value);\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;;;AC7CO,SAAS,uBAAuB,OAAwB;AAC7D,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,KAAK;AACrB;;;AFEA,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;AAEA,aAAO,uBAAuB,WAAW;AAAA,IAC3C,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":[]}
@@ -35,6 +35,14 @@ function getPathParts(path) {
35
35
  return parts;
36
36
  }
37
37
 
38
+ // src/lib/internal/stringify-template-value.ts
39
+ function stringifyTemplateValue(value) {
40
+ if (typeof value === "string") {
41
+ return value;
42
+ }
43
+ return String(value);
44
+ }
45
+
38
46
  // src/lib/curly-brackets.ts
39
47
  var PLACEHOLDER_PATTERN = /(?:\\)?{{(\s*[^{}]+?\s*)(?:\\)?\s*}}/g;
40
48
  var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
@@ -79,7 +87,7 @@ CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
79
87
  if (replacement === void 0 || replacement === null) {
80
88
  return fallback;
81
89
  }
82
- return String(replacement);
90
+ return stringifyTemplateValue(replacement);
83
91
  });
84
92
  };
85
93
  };
@@ -1 +1 @@
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":[]}
1
+ {"version":3,"sources":["../../src/lib/internal/path-utils.ts","../../src/lib/internal/stringify-template-value.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","/**\n * Normalizes values to the same string representation used by template rendering.\n */\nexport function stringifyTemplateValue(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n\n return String(value);\n}\n","import { getPathParts } from './internal/path-utils';\nimport { stringifyTemplateValue } from './internal/stringify-template-value';\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 return stringifyTemplateValue(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":"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","names":[]}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/dev-mode.ts
21
+ var dev_mode_exports = {};
22
+ __export(dev_mode_exports, {
23
+ getDevMode: () => getDevMode,
24
+ initDevMode: () => initDevMode,
25
+ overrideDevMode: () => overrideDevMode
26
+ });
27
+ module.exports = __toCommonJS(dev_mode_exports);
28
+ var GLOBAL_KEY = "__lifecycleion_is_dev__";
29
+ var INIT_PARAM_KEY = "__lifecycleion_init_param__";
30
+ var g = globalThis;
31
+ function detectValue(detect, isStrict) {
32
+ const argv = typeof process !== "undefined" ? process.argv ?? [] : [];
33
+ const env = typeof process !== "undefined" ? process.env?.NODE_ENV : void 0;
34
+ const cmdArg = argv.find((a) => a === "dev" || a === "prod");
35
+ if (isStrict && detect === "cmd" && cmdArg === void 0) {
36
+ throw new Error('initDevMode: expected "dev" or "prod" as a CLI argument');
37
+ }
38
+ const isFromCmd = cmdArg !== void 0 ? cmdArg === "dev" : false;
39
+ const isFromEnv = env === "development";
40
+ if (detect === "cmd") {
41
+ return isFromCmd;
42
+ }
43
+ if (detect === "node_env") {
44
+ return isFromEnv;
45
+ }
46
+ return cmdArg !== void 0 ? isFromCmd : isFromEnv;
47
+ }
48
+ function resolveInitParam(param) {
49
+ if (param === void 0) {
50
+ return detectValue("both");
51
+ }
52
+ if (typeof param === "boolean") {
53
+ return param;
54
+ }
55
+ return detectValue(param.detect, param.strict);
56
+ }
57
+ function getDevMode() {
58
+ return typeof g[GLOBAL_KEY] === "boolean" ? g[GLOBAL_KEY] : false;
59
+ }
60
+ function initDevMode(param) {
61
+ if (typeof g[GLOBAL_KEY] === "boolean") {
62
+ return;
63
+ }
64
+ g[INIT_PARAM_KEY] = param;
65
+ g[GLOBAL_KEY] = resolveInitParam(param);
66
+ }
67
+ function overrideDevMode(value) {
68
+ const savedParam = g[INIT_PARAM_KEY];
69
+ g[GLOBAL_KEY] = value === "redetect" ? resolveInitParam(savedParam) : value;
70
+ }
71
+ // Annotate the CommonJS export names for ESM import in node:
72
+ 0 && (module.exports = {
73
+ getDevMode,
74
+ initDevMode,
75
+ overrideDevMode
76
+ });
77
+ //# sourceMappingURL=dev-mode.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/dev-mode.ts"],"sourcesContent":["// Stored in globalThis so state survives module re-evaluation and works across\n// module boundaries.\nconst GLOBAL_KEY = '__lifecycleion_is_dev__';\nconst INIT_PARAM_KEY = '__lifecycleion_init_param__';\n\ntype DevModeSource = 'cmd' | 'node_env' | 'both';\ntype InitParam =\n | boolean\n | { detect: DevModeSource; strict?: boolean }\n | undefined;\n\n// Cast once at module level — avoids repeating the assertion in every function.\nconst g = globalThis as typeof globalThis & Record<string, unknown>;\n\nfunction detectValue(detect: DevModeSource, isStrict?: boolean): boolean {\n // Guard against environments where process or its properties may not exist\n // (e.g. browser without polyfill, Deno without Node compat, edge runtimes).\n // Vite statically replaces process.env.NODE_ENV at build time, so it is safe\n // on the client. process.argv is an empty array shim in browsers — harmless.\n const argv: string[] =\n typeof process !== 'undefined' ? (process.argv ?? []) : [];\n const env: string | undefined =\n typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined;\n\n const cmdArg = argv.find((a) => a === 'dev' || a === 'prod');\n\n if (isStrict && detect === 'cmd' && cmdArg === undefined) {\n throw new Error('initDevMode: expected \"dev\" or \"prod\" as a CLI argument');\n }\n\n const isFromCmd = cmdArg !== undefined ? cmdArg === 'dev' : false;\n const isFromEnv = env === 'development';\n\n if (detect === 'cmd') {\n return isFromCmd;\n }\n\n if (detect === 'node_env') {\n return isFromEnv;\n }\n\n // 'both': cmd wins when explicitly present, otherwise fall back to NODE_ENV\n return cmdArg !== undefined ? isFromCmd : isFromEnv;\n}\n\nfunction resolveInitParam(param: InitParam): boolean {\n if (param === undefined) {\n return detectValue('both');\n }\n\n if (typeof param === 'boolean') {\n return param;\n }\n\n return detectValue(param.detect, param.strict);\n}\n\n/**\n * Returns the current dev mode value.\n *\n * Reads `globalThis.__lifecycleion_is_dev__`. Defaults to `false` if\n * `initDevMode()` has not been called yet.\n */\nexport function getDevMode(): boolean {\n return typeof g[GLOBAL_KEY] === 'boolean' ? g[GLOBAL_KEY] : false;\n}\n\n/**\n * Sets the dev mode global for the current process.\n *\n * First-wins: if the global is already set (e.g. set by the HTML injection on\n * the client, or by a prior `initDevMode()` call), this is a no-op. Call once\n * at startup before serving any requests.\n *\n * @param param\n * - `true` / `false` — explicit value\n * - `{ detect: 'cmd' }` — read from `process.argv` (\"dev\" or \"prod\")\n * - `{ detect: 'node_env' }` — read from `NODE_ENV`\n * - `{ detect: 'both' }` — argv takes precedence, falls back to `NODE_ENV`\n * - `{ detect: 'cmd', strict: true }` — argv required, throws if absent\n * - omitted — same as `{ detect: 'both' }`\n */\nexport function initDevMode(\n param?: boolean | { detect: DevModeSource; strict?: boolean },\n): void {\n if (typeof g[GLOBAL_KEY] === 'boolean') {\n return; // first-wins — already initialized, no-op\n }\n\n g[INIT_PARAM_KEY] = param; // save original param so 'redetect' can replay it\n g[GLOBAL_KEY] = resolveInitParam(param);\n}\n\n/**\n * Bypasses first-wins semantics and forces the dev mode value.\n *\n * Intended for testing or tooling — not for production use.\n *\n * Pass `'redetect'` to re-run the same detection logic that\n * `initDevMode()` used originally.\n */\nexport function overrideDevMode(value: boolean | 'redetect'): void {\n const savedParam = g[INIT_PARAM_KEY] as InitParam;\n g[GLOBAL_KEY] = value === 'redetect' ? resolveInitParam(savedParam) : value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAM,aAAa;AACnB,IAAM,iBAAiB;AASvB,IAAM,IAAI;AAEV,SAAS,YAAY,QAAuB,UAA6B;AAKvE,QAAM,OACJ,OAAO,YAAY,cAAe,QAAQ,QAAQ,CAAC,IAAK,CAAC;AAC3D,QAAM,MACJ,OAAO,YAAY,cAAc,QAAQ,KAAK,WAAW;AAE3D,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,MAAM,SAAS,MAAM,MAAM;AAE3D,MAAI,YAAY,WAAW,SAAS,WAAW,QAAW;AACxD,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,QAAM,YAAY,WAAW,SAAY,WAAW,QAAQ;AAC5D,QAAM,YAAY,QAAQ;AAE1B,MAAI,WAAW,OAAO;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,YAAY;AACzB,WAAO;AAAA,EACT;AAGA,SAAO,WAAW,SAAY,YAAY;AAC5C;AAEA,SAAS,iBAAiB,OAA2B;AACnD,MAAI,UAAU,QAAW;AACvB,WAAO,YAAY,MAAM;AAAA,EAC3B;AAEA,MAAI,OAAO,UAAU,WAAW;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,MAAM,QAAQ,MAAM,MAAM;AAC/C;AAQO,SAAS,aAAsB;AACpC,SAAO,OAAO,EAAE,UAAU,MAAM,YAAY,EAAE,UAAU,IAAI;AAC9D;AAiBO,SAAS,YACd,OACM;AACN,MAAI,OAAO,EAAE,UAAU,MAAM,WAAW;AACtC;AAAA,EACF;AAEA,IAAE,cAAc,IAAI;AACpB,IAAE,UAAU,IAAI,iBAAiB,KAAK;AACxC;AAUO,SAAS,gBAAgB,OAAmC;AACjE,QAAM,aAAa,EAAE,cAAc;AACnC,IAAE,UAAU,IAAI,UAAU,aAAa,iBAAiB,UAAU,IAAI;AACxE;","names":[]}
@@ -0,0 +1,38 @@
1
+ type DevModeSource = 'cmd' | 'node_env' | 'both';
2
+ /**
3
+ * Returns the current dev mode value.
4
+ *
5
+ * Reads `globalThis.__lifecycleion_is_dev__`. Defaults to `false` if
6
+ * `initDevMode()` has not been called yet.
7
+ */
8
+ declare function getDevMode(): boolean;
9
+ /**
10
+ * Sets the dev mode global for the current process.
11
+ *
12
+ * First-wins: if the global is already set (e.g. set by the HTML injection on
13
+ * the client, or by a prior `initDevMode()` call), this is a no-op. Call once
14
+ * at startup before serving any requests.
15
+ *
16
+ * @param param
17
+ * - `true` / `false` — explicit value
18
+ * - `{ detect: 'cmd' }` — read from `process.argv` ("dev" or "prod")
19
+ * - `{ detect: 'node_env' }` — read from `NODE_ENV`
20
+ * - `{ detect: 'both' }` — argv takes precedence, falls back to `NODE_ENV`
21
+ * - `{ detect: 'cmd', strict: true }` — argv required, throws if absent
22
+ * - omitted — same as `{ detect: 'both' }`
23
+ */
24
+ declare function initDevMode(param?: boolean | {
25
+ detect: DevModeSource;
26
+ strict?: boolean;
27
+ }): void;
28
+ /**
29
+ * Bypasses first-wins semantics and forces the dev mode value.
30
+ *
31
+ * Intended for testing or tooling — not for production use.
32
+ *
33
+ * Pass `'redetect'` to re-run the same detection logic that
34
+ * `initDevMode()` used originally.
35
+ */
36
+ declare function overrideDevMode(value: boolean | 'redetect'): void;
37
+
38
+ export { getDevMode, initDevMode, overrideDevMode };
@@ -0,0 +1,38 @@
1
+ type DevModeSource = 'cmd' | 'node_env' | 'both';
2
+ /**
3
+ * Returns the current dev mode value.
4
+ *
5
+ * Reads `globalThis.__lifecycleion_is_dev__`. Defaults to `false` if
6
+ * `initDevMode()` has not been called yet.
7
+ */
8
+ declare function getDevMode(): boolean;
9
+ /**
10
+ * Sets the dev mode global for the current process.
11
+ *
12
+ * First-wins: if the global is already set (e.g. set by the HTML injection on
13
+ * the client, or by a prior `initDevMode()` call), this is a no-op. Call once
14
+ * at startup before serving any requests.
15
+ *
16
+ * @param param
17
+ * - `true` / `false` — explicit value
18
+ * - `{ detect: 'cmd' }` — read from `process.argv` ("dev" or "prod")
19
+ * - `{ detect: 'node_env' }` — read from `NODE_ENV`
20
+ * - `{ detect: 'both' }` — argv takes precedence, falls back to `NODE_ENV`
21
+ * - `{ detect: 'cmd', strict: true }` — argv required, throws if absent
22
+ * - omitted — same as `{ detect: 'both' }`
23
+ */
24
+ declare function initDevMode(param?: boolean | {
25
+ detect: DevModeSource;
26
+ strict?: boolean;
27
+ }): void;
28
+ /**
29
+ * Bypasses first-wins semantics and forces the dev mode value.
30
+ *
31
+ * Intended for testing or tooling — not for production use.
32
+ *
33
+ * Pass `'redetect'` to re-run the same detection logic that
34
+ * `initDevMode()` used originally.
35
+ */
36
+ declare function overrideDevMode(value: boolean | 'redetect'): void;
37
+
38
+ export { getDevMode, initDevMode, overrideDevMode };
@@ -0,0 +1,50 @@
1
+ // src/lib/dev-mode.ts
2
+ var GLOBAL_KEY = "__lifecycleion_is_dev__";
3
+ var INIT_PARAM_KEY = "__lifecycleion_init_param__";
4
+ var g = globalThis;
5
+ function detectValue(detect, isStrict) {
6
+ const argv = typeof process !== "undefined" ? process.argv ?? [] : [];
7
+ const env = typeof process !== "undefined" ? process.env?.NODE_ENV : void 0;
8
+ const cmdArg = argv.find((a) => a === "dev" || a === "prod");
9
+ if (isStrict && detect === "cmd" && cmdArg === void 0) {
10
+ throw new Error('initDevMode: expected "dev" or "prod" as a CLI argument');
11
+ }
12
+ const isFromCmd = cmdArg !== void 0 ? cmdArg === "dev" : false;
13
+ const isFromEnv = env === "development";
14
+ if (detect === "cmd") {
15
+ return isFromCmd;
16
+ }
17
+ if (detect === "node_env") {
18
+ return isFromEnv;
19
+ }
20
+ return cmdArg !== void 0 ? isFromCmd : isFromEnv;
21
+ }
22
+ function resolveInitParam(param) {
23
+ if (param === void 0) {
24
+ return detectValue("both");
25
+ }
26
+ if (typeof param === "boolean") {
27
+ return param;
28
+ }
29
+ return detectValue(param.detect, param.strict);
30
+ }
31
+ function getDevMode() {
32
+ return typeof g[GLOBAL_KEY] === "boolean" ? g[GLOBAL_KEY] : false;
33
+ }
34
+ function initDevMode(param) {
35
+ if (typeof g[GLOBAL_KEY] === "boolean") {
36
+ return;
37
+ }
38
+ g[INIT_PARAM_KEY] = param;
39
+ g[GLOBAL_KEY] = resolveInitParam(param);
40
+ }
41
+ function overrideDevMode(value) {
42
+ const savedParam = g[INIT_PARAM_KEY];
43
+ g[GLOBAL_KEY] = value === "redetect" ? resolveInitParam(savedParam) : value;
44
+ }
45
+ export {
46
+ getDevMode,
47
+ initDevMode,
48
+ overrideDevMode
49
+ };
50
+ //# sourceMappingURL=dev-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/dev-mode.ts"],"sourcesContent":["// Stored in globalThis so state survives module re-evaluation and works across\n// module boundaries.\nconst GLOBAL_KEY = '__lifecycleion_is_dev__';\nconst INIT_PARAM_KEY = '__lifecycleion_init_param__';\n\ntype DevModeSource = 'cmd' | 'node_env' | 'both';\ntype InitParam =\n | boolean\n | { detect: DevModeSource; strict?: boolean }\n | undefined;\n\n// Cast once at module level — avoids repeating the assertion in every function.\nconst g = globalThis as typeof globalThis & Record<string, unknown>;\n\nfunction detectValue(detect: DevModeSource, isStrict?: boolean): boolean {\n // Guard against environments where process or its properties may not exist\n // (e.g. browser without polyfill, Deno without Node compat, edge runtimes).\n // Vite statically replaces process.env.NODE_ENV at build time, so it is safe\n // on the client. process.argv is an empty array shim in browsers — harmless.\n const argv: string[] =\n typeof process !== 'undefined' ? (process.argv ?? []) : [];\n const env: string | undefined =\n typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined;\n\n const cmdArg = argv.find((a) => a === 'dev' || a === 'prod');\n\n if (isStrict && detect === 'cmd' && cmdArg === undefined) {\n throw new Error('initDevMode: expected \"dev\" or \"prod\" as a CLI argument');\n }\n\n const isFromCmd = cmdArg !== undefined ? cmdArg === 'dev' : false;\n const isFromEnv = env === 'development';\n\n if (detect === 'cmd') {\n return isFromCmd;\n }\n\n if (detect === 'node_env') {\n return isFromEnv;\n }\n\n // 'both': cmd wins when explicitly present, otherwise fall back to NODE_ENV\n return cmdArg !== undefined ? isFromCmd : isFromEnv;\n}\n\nfunction resolveInitParam(param: InitParam): boolean {\n if (param === undefined) {\n return detectValue('both');\n }\n\n if (typeof param === 'boolean') {\n return param;\n }\n\n return detectValue(param.detect, param.strict);\n}\n\n/**\n * Returns the current dev mode value.\n *\n * Reads `globalThis.__lifecycleion_is_dev__`. Defaults to `false` if\n * `initDevMode()` has not been called yet.\n */\nexport function getDevMode(): boolean {\n return typeof g[GLOBAL_KEY] === 'boolean' ? g[GLOBAL_KEY] : false;\n}\n\n/**\n * Sets the dev mode global for the current process.\n *\n * First-wins: if the global is already set (e.g. set by the HTML injection on\n * the client, or by a prior `initDevMode()` call), this is a no-op. Call once\n * at startup before serving any requests.\n *\n * @param param\n * - `true` / `false` — explicit value\n * - `{ detect: 'cmd' }` — read from `process.argv` (\"dev\" or \"prod\")\n * - `{ detect: 'node_env' }` — read from `NODE_ENV`\n * - `{ detect: 'both' }` — argv takes precedence, falls back to `NODE_ENV`\n * - `{ detect: 'cmd', strict: true }` — argv required, throws if absent\n * - omitted — same as `{ detect: 'both' }`\n */\nexport function initDevMode(\n param?: boolean | { detect: DevModeSource; strict?: boolean },\n): void {\n if (typeof g[GLOBAL_KEY] === 'boolean') {\n return; // first-wins — already initialized, no-op\n }\n\n g[INIT_PARAM_KEY] = param; // save original param so 'redetect' can replay it\n g[GLOBAL_KEY] = resolveInitParam(param);\n}\n\n/**\n * Bypasses first-wins semantics and forces the dev mode value.\n *\n * Intended for testing or tooling — not for production use.\n *\n * Pass `'redetect'` to re-run the same detection logic that\n * `initDevMode()` used originally.\n */\nexport function overrideDevMode(value: boolean | 'redetect'): void {\n const savedParam = g[INIT_PARAM_KEY] as InitParam;\n g[GLOBAL_KEY] = value === 'redetect' ? resolveInitParam(savedParam) : value;\n}\n"],"mappings":";AAEA,IAAM,aAAa;AACnB,IAAM,iBAAiB;AASvB,IAAM,IAAI;AAEV,SAAS,YAAY,QAAuB,UAA6B;AAKvE,QAAM,OACJ,OAAO,YAAY,cAAe,QAAQ,QAAQ,CAAC,IAAK,CAAC;AAC3D,QAAM,MACJ,OAAO,YAAY,cAAc,QAAQ,KAAK,WAAW;AAE3D,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,MAAM,SAAS,MAAM,MAAM;AAE3D,MAAI,YAAY,WAAW,SAAS,WAAW,QAAW;AACxD,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,QAAM,YAAY,WAAW,SAAY,WAAW,QAAQ;AAC5D,QAAM,YAAY,QAAQ;AAE1B,MAAI,WAAW,OAAO;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,YAAY;AACzB,WAAO;AAAA,EACT;AAGA,SAAO,WAAW,SAAY,YAAY;AAC5C;AAEA,SAAS,iBAAiB,OAA2B;AACnD,MAAI,UAAU,QAAW;AACvB,WAAO,YAAY,MAAM;AAAA,EAC3B;AAEA,MAAI,OAAO,UAAU,WAAW;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,MAAM,QAAQ,MAAM,MAAM;AAC/C;AAQO,SAAS,aAAsB;AACpC,SAAO,OAAO,EAAE,UAAU,MAAM,YAAY,EAAE,UAAU,IAAI;AAC9D;AAiBO,SAAS,YACd,OACM;AACN,MAAI,OAAO,EAAE,UAAU,MAAM,WAAW;AACtC;AAAA,EACF;AAEA,IAAE,cAAc,IAAI;AACpB,IAAE,UAAU,IAAI,iBAAiB,KAAK;AACxC;AAUO,SAAS,gBAAgB,OAAmC;AACjE,QAAM,aAAa,EAAE,cAAc;AACnC,IAAE,UAAU,IAAI,UAAU,aAAa,iBAAiB,UAAU,IAAI;AACxE;","names":[]}
@@ -971,6 +971,14 @@ function getPathParts(path) {
971
971
  return parts;
972
972
  }
973
973
 
974
+ // src/lib/internal/stringify-template-value.ts
975
+ function stringifyTemplateValue(value) {
976
+ if (typeof value === "string") {
977
+ return value;
978
+ }
979
+ return String(value);
980
+ }
981
+
974
982
  // src/lib/curly-brackets.ts
975
983
  var PLACEHOLDER_PATTERN = /(?:\\)?{{(\s*[^{}]+?\s*)(?:\\)?\s*}}/g;
976
984
  var CurlyBrackets = function(str = "", locals = {}, fallback = "(null)") {
@@ -1015,7 +1023,7 @@ CurlyBrackets.compileTemplate = function(str, fallback = "(null)") {
1015
1023
  if (replacement === void 0 || replacement === null) {
1016
1024
  return fallback;
1017
1025
  }
1018
- return String(replacement);
1026
+ return stringifyTemplateValue(replacement);
1019
1027
  });
1020
1028
  };
1021
1029
  };
@@ -1391,14 +1399,17 @@ function applyRedaction(params, redactedKeys, redactFunction) {
1391
1399
  const redactedParams = deepClone(params);
1392
1400
  for (const key of redactedKeys) {
1393
1401
  if (key.includes(".") || key.includes("[")) {
1394
- const value = getNestedValue(redactedParams, key);
1402
+ const value = getNestedValue(params, key);
1395
1403
  if (value !== void 0) {
1396
- const redactedValue = redactFn(key, value);
1404
+ const redactedValue = redactFn(key, stringifyTemplateValue(value));
1397
1405
  setNestedValue(redactedParams, key, redactedValue);
1398
1406
  }
1399
1407
  } else {
1400
- if (key in redactedParams) {
1401
- redactedParams[key] = redactFn(key, redactedParams[key]);
1408
+ if (key in params) {
1409
+ redactedParams[key] = redactFn(
1410
+ key,
1411
+ stringifyTemplateValue(params[key])
1412
+ );
1402
1413
  }
1403
1414
  }
1404
1415
  }
@@ -2478,8 +2489,9 @@ var Logger = class _Logger extends EventEmitter {
2478
2489
  const params = options?.params;
2479
2490
  const tags = options?.tags;
2480
2491
  const redactedKeys = options?.redactedKeys;
2481
- const message = params ? CurlyBrackets(template, params) : template;
2482
- const redactedParams = params ? applyRedaction(params, redactedKeys, this.redactFunction) : void 0;
2492
+ const redactedParams = params && redactedKeys && redactedKeys.length > 0 ? applyRedaction(params, redactedKeys, this.redactFunction) : void 0;
2493
+ const messageParams = redactedParams ?? params;
2494
+ const message = messageParams ? CurlyBrackets(template, messageParams) : template;
2483
2495
  const entry = {
2484
2496
  timestamp,
2485
2497
  type,