@mdfriday/foundry 25.9.2 → 25.9.4

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.
Files changed (134) hide show
  1. package/README.md +10 -0
  2. package/dist/internal/application/ssg.d.ts.map +1 -1
  3. package/dist/internal/application/ssg.js +12 -0
  4. package/dist/internal/application/ssg.js.map +1 -1
  5. package/dist/internal/domain/config/entity/config.d.ts +8 -2
  6. package/dist/internal/domain/config/entity/config.d.ts.map +1 -1
  7. package/dist/internal/domain/config/entity/config.js +10 -3
  8. package/dist/internal/domain/config/entity/config.js.map +1 -1
  9. package/dist/internal/domain/config/entity/social.d.ts +66 -0
  10. package/dist/internal/domain/config/entity/social.d.ts.map +1 -0
  11. package/dist/internal/domain/config/entity/social.js +126 -0
  12. package/dist/internal/domain/config/entity/social.js.map +1 -0
  13. package/dist/internal/domain/config/factory/config.d.ts.map +1 -1
  14. package/dist/internal/domain/config/factory/config.js +2 -1
  15. package/dist/internal/domain/config/factory/config.js.map +1 -1
  16. package/dist/internal/domain/config/index.d.ts +2 -0
  17. package/dist/internal/domain/config/index.d.ts.map +1 -1
  18. package/dist/internal/domain/config/index.js +5 -1
  19. package/dist/internal/domain/config/index.js.map +1 -1
  20. package/dist/internal/domain/config/tests/entity-config.test.js +16 -12
  21. package/dist/internal/domain/config/tests/entity-config.test.js.map +1 -1
  22. package/dist/internal/domain/config/tests/social.test.d.ts +2 -0
  23. package/dist/internal/domain/config/tests/social.test.d.ts.map +1 -0
  24. package/dist/internal/domain/config/tests/social.test.js +164 -0
  25. package/dist/internal/domain/config/tests/social.test.js.map +1 -0
  26. package/dist/internal/domain/config/type.d.ts +22 -0
  27. package/dist/internal/domain/config/type.d.ts.map +1 -1
  28. package/dist/internal/domain/config/type.js.map +1 -1
  29. package/dist/internal/domain/config/vo/root.d.ts.map +1 -1
  30. package/dist/internal/domain/config/vo/root.js +2 -1
  31. package/dist/internal/domain/config/vo/root.js.map +1 -1
  32. package/dist/internal/domain/config/vo/social.d.ts +23 -0
  33. package/dist/internal/domain/config/vo/social.d.ts.map +1 -0
  34. package/dist/internal/domain/config/vo/social.js +212 -0
  35. package/dist/internal/domain/config/vo/social.js.map +1 -0
  36. package/dist/internal/domain/content/entity/page.d.ts +7 -1
  37. package/dist/internal/domain/content/entity/page.d.ts.map +1 -1
  38. package/dist/internal/domain/content/entity/page.js +36 -3
  39. package/dist/internal/domain/content/entity/page.js.map +1 -1
  40. package/dist/internal/domain/content/entity/pagebuilder.d.ts +3 -2
  41. package/dist/internal/domain/content/entity/pagebuilder.d.ts.map +1 -1
  42. package/dist/internal/domain/content/entity/pagebuilder.js +16 -5
  43. package/dist/internal/domain/content/entity/pagebuilder.js.map +1 -1
  44. package/dist/internal/domain/content/entity/pagemeta.d.ts +10 -2
  45. package/dist/internal/domain/content/entity/pagemeta.d.ts.map +1 -1
  46. package/dist/internal/domain/content/entity/pagemeta.js +17 -1
  47. package/dist/internal/domain/content/entity/pagemeta.js.map +1 -1
  48. package/dist/internal/domain/content/entity/paginator.d.ts +6 -0
  49. package/dist/internal/domain/content/entity/paginator.d.ts.map +1 -1
  50. package/dist/internal/domain/content/entity/paginator.js +26 -2
  51. package/dist/internal/domain/content/entity/paginator.js.map +1 -1
  52. package/dist/internal/domain/content/entity/section.js +5 -5
  53. package/dist/internal/domain/content/entity/section.js.map +1 -1
  54. package/dist/internal/domain/content/factory/hub.d.ts.map +1 -1
  55. package/dist/internal/domain/content/factory/hub.js +2 -1
  56. package/dist/internal/domain/content/factory/hub.js.map +1 -1
  57. package/dist/internal/domain/content/type.d.ts +59 -1
  58. package/dist/internal/domain/content/type.d.ts.map +1 -1
  59. package/dist/internal/domain/content/type.js.map +1 -1
  60. package/dist/internal/domain/content/vo/frontmatter.d.ts +29 -1
  61. package/dist/internal/domain/content/vo/frontmatter.d.ts.map +1 -1
  62. package/dist/internal/domain/content/vo/frontmatter.js +242 -1
  63. package/dist/internal/domain/content/vo/frontmatter.js.map +1 -1
  64. package/dist/internal/domain/markdown/entity/markdown.d.ts.map +1 -1
  65. package/dist/internal/domain/markdown/entity/markdown.js +5 -4
  66. package/dist/internal/domain/markdown/entity/markdown.js.map +1 -1
  67. package/dist/internal/domain/markdown/vo/content.d.ts +3 -6
  68. package/dist/internal/domain/markdown/vo/content.d.ts.map +1 -1
  69. package/dist/internal/domain/markdown/vo/content.js +32 -31
  70. package/dist/internal/domain/markdown/vo/content.js.map +1 -1
  71. package/dist/internal/domain/module/entity/module.d.ts.map +1 -1
  72. package/dist/internal/domain/module/entity/module.js +10 -6
  73. package/dist/internal/domain/module/entity/module.js.map +1 -1
  74. package/dist/internal/domain/module/vo/themes.test.js +12 -8
  75. package/dist/internal/domain/module/vo/themes.test.js.map +1 -1
  76. package/dist/internal/domain/site/entity/language.d.ts.map +1 -1
  77. package/dist/internal/domain/site/entity/language.js +2 -0
  78. package/dist/internal/domain/site/entity/language.js.map +1 -1
  79. package/dist/internal/domain/site/entity/menu-builder.d.ts +39 -21
  80. package/dist/internal/domain/site/entity/menu-builder.d.ts.map +1 -1
  81. package/dist/internal/domain/site/entity/menu-builder.js +264 -107
  82. package/dist/internal/domain/site/entity/menu-builder.js.map +1 -1
  83. package/dist/internal/domain/site/entity/page.d.ts +22 -8
  84. package/dist/internal/domain/site/entity/page.d.ts.map +1 -1
  85. package/dist/internal/domain/site/entity/page.js +67 -6
  86. package/dist/internal/domain/site/entity/page.js.map +1 -1
  87. package/dist/internal/domain/site/entity/pager.d.ts +2 -0
  88. package/dist/internal/domain/site/entity/pager.d.ts.map +1 -1
  89. package/dist/internal/domain/site/entity/pager.js +11 -0
  90. package/dist/internal/domain/site/entity/pager.js.map +1 -1
  91. package/dist/internal/domain/site/entity/site.d.ts +14 -8
  92. package/dist/internal/domain/site/entity/site.d.ts.map +1 -1
  93. package/dist/internal/domain/site/entity/site.js +35 -8
  94. package/dist/internal/domain/site/entity/site.js.map +1 -1
  95. package/dist/internal/domain/site/entity/url.d.ts.map +1 -1
  96. package/dist/internal/domain/site/entity/url.js +3 -1
  97. package/dist/internal/domain/site/entity/url.js.map +1 -1
  98. package/dist/internal/domain/site/factory/navigation-factory.d.ts +4 -2
  99. package/dist/internal/domain/site/factory/navigation-factory.d.ts.map +1 -1
  100. package/dist/internal/domain/site/factory/navigation-factory.js +5 -4
  101. package/dist/internal/domain/site/factory/navigation-factory.js.map +1 -1
  102. package/dist/internal/domain/site/factory/site.d.ts.map +1 -1
  103. package/dist/internal/domain/site/factory/site.js +7 -9
  104. package/dist/internal/domain/site/factory/site.js.map +1 -1
  105. package/dist/internal/domain/site/type.d.ts +11 -0
  106. package/dist/internal/domain/site/type.d.ts.map +1 -1
  107. package/dist/internal/domain/site/valueobject/author.d.ts +14 -2
  108. package/dist/internal/domain/site/valueobject/author.d.ts.map +1 -1
  109. package/dist/internal/domain/site/valueobject/author.js +29 -4
  110. package/dist/internal/domain/site/valueobject/author.js.map +1 -1
  111. package/dist/internal/domain/site/valueobject/menu.d.ts +28 -115
  112. package/dist/internal/domain/site/valueobject/menu.d.ts.map +1 -1
  113. package/dist/internal/domain/site/valueobject/menu.js +44 -192
  114. package/dist/internal/domain/site/valueobject/menu.js.map +1 -1
  115. package/dist/internal/domain/site/valueobject/organization.d.ts +66 -0
  116. package/dist/internal/domain/site/valueobject/organization.d.ts.map +1 -0
  117. package/dist/internal/domain/site/valueobject/organization.js +115 -0
  118. package/dist/internal/domain/site/valueobject/organization.js.map +1 -0
  119. package/dist/internal/domain/site/valueobject/organization.test.d.ts +2 -0
  120. package/dist/internal/domain/site/valueobject/organization.test.d.ts.map +1 -0
  121. package/dist/internal/domain/site/valueobject/organization.test.js +78 -0
  122. package/dist/internal/domain/site/valueobject/organization.test.js.map +1 -0
  123. package/dist/internal/domain/template/vo/namespace.d.ts.map +1 -1
  124. package/dist/internal/domain/template/vo/namespace.js +8 -0
  125. package/dist/internal/domain/template/vo/namespace.js.map +1 -1
  126. package/dist/internal/domain/template/vo/registry.d.ts +18 -0
  127. package/dist/internal/domain/template/vo/registry.d.ts.map +1 -1
  128. package/dist/internal/domain/template/vo/registry.js +387 -9
  129. package/dist/internal/domain/template/vo/registry.js.map +1 -1
  130. package/dist/pkg/maps/scratch.d.ts +1 -0
  131. package/dist/pkg/maps/scratch.d.ts.map +1 -1
  132. package/dist/pkg/maps/scratch.js +4 -0
  133. package/dist/pkg/maps/scratch.js.map +1 -1
  134. package/package.json +1 -1
@@ -9,6 +9,8 @@ exports.newPartialTemplateNamespace = newPartialTemplateNamespace;
9
9
  exports.newShortcodeTemplateNamespace = newShortcodeTemplateNamespace;
10
10
  const type_1 = require("../type");
11
11
  const path_1 = __importDefault(require("path"));
12
+ const log_1 = require("../../../../pkg/log");
13
+ const log = (0, log_1.getDomainLogger)('template', { component: 'namespace' });
12
14
  /**
13
15
  * Base namespace for template storage with common functionality
14
16
  * Abstract base class for different template namespace types
@@ -34,6 +36,12 @@ class BaseNamespace {
34
36
  if (this.templates.has(name)) {
35
37
  return this.templates.get(name) || null;
36
38
  }
39
+ if (!name.endsWith(".html")) {
40
+ const htmlName = name + '.html';
41
+ if (this.templates.has(htmlName)) {
42
+ return this.templates.get(htmlName) || null;
43
+ }
44
+ }
37
45
  for (const [key, template] of this.templates.entries()) {
38
46
  if (key.includes(name)) {
39
47
  return template;
@@ -1 +1 @@
1
- {"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../../../internal/domain/template/vo/namespace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../../../internal/domain/template/vo/namespace.ts"],"names":[],"mappings":"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"}
@@ -70,6 +70,18 @@ export declare class DefaultTemplateRegistry implements TemplateRegistry {
70
70
  * Equivalent to Go's registerStrings()
71
71
  */
72
72
  private registerStringFunctions;
73
+ /**
74
+ * HTML escape utility function
75
+ */
76
+ private escapeHTML;
77
+ /**
78
+ * Truncate plain text, breaking at word boundaries when possible
79
+ */
80
+ private truncateText;
81
+ /**
82
+ * Truncate HTML text while preserving tag structure
83
+ */
84
+ private truncateHTML;
73
85
  /**
74
86
  * Register math functions
75
87
  * Equivalent to Go's registerMath()
@@ -140,6 +152,12 @@ export declare class DefaultTemplateRegistry implements TemplateRegistry {
140
152
  * Equivalent to Go's transform package functions
141
153
  */
142
154
  private registerTransformFunctions;
155
+ /**
156
+ * Strip HTML tags from a string
157
+ * Equivalent to Go's helpers.StripHTML function
158
+ * Preserves line breaks from block elements and normalizes whitespace
159
+ */
160
+ private stripHTML;
143
161
  /**
144
162
  * Unmarshal content from string based on format
145
163
  * Supports JSON, YAML, TOML, CSV formats
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../../../internal/domain/template/vo/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,mBAAmB,EAAE,eAAe,EAAC,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsFlD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IACtD,yBAAyB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACzF,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACtF;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAE9D;;;OAGG;IACH,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAgCrD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;;OAGG;IACH,yBAAyB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAgBxF;;;OAGG;IACH,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,IAAI;IAiBpF;;;OAGG;IACH,OAAO,CAAC,gCAAgC;IAOxC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAgL5B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,0BAA0B;IAWlC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAuDjC;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAuN/B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAmO7B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAgM7B;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAkXnC;;;OAGG;IACH,OAAO,CAAC,OAAO;IAuCf;;OAEG;IACH,OAAO,CAAC,UAAU;IAelB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,WAAW;IAkCnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAoBjB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmEtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkC9B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA+I7B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAiI5B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAgDhC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAqH7B;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IA6DlC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAmCxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAiCpB;;;OAGG;IACH,OAAO,CAAC,SAAS;IA0CjB;;;OAGG;IACH,OAAO,CAAC,SAAS;IA4DjB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAuChB;;OAEG;IACH,OAAO,CAAC,YAAY;IAgCpB;;;OAGG;IACH,OAAO,CAAC,cAAc;CAwDvB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,eAAe,CAKnD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,eAAe,CAKpF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAQ3E"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../../../internal/domain/template/vo/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,mBAAmB,EAAE,eAAe,EAAC,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsFlD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IACtD,yBAAyB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACzF,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACtF;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAE9D;;;OAGG;IACH,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAgCrD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAQ/B;;;OAGG;IACH,yBAAyB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAgBxF;;;OAGG;IACH,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,mBAAmB,GAAG,IAAI;IAiBpF;;;OAGG;IACH,OAAO,CAAC,gCAAgC;IAOxC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAgL5B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,0BAA0B;IAWlC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAuDjC;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAma/B;;OAEG;IACH,OAAO,CAAC,UAAU;IASlB;;OAEG;IACH,OAAO,CAAC,YAAY;IAgCpB;;OAEG;IACH,OAAO,CAAC,YAAY;IAoGpB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAmO7B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAgM7B;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IA2ZnC;;;OAGG;IACH,OAAO,CAAC,OAAO;IAuCf;;OAEG;IACH,OAAO,CAAC,UAAU;IAelB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,WAAW;IAkCnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAoBjB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmEtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkC9B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA+I7B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAiI5B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAgDhC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAqH7B;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IA4ElC;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAuCjB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAmCxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAiCpB;;;OAGG;IACH,OAAO,CAAC,SAAS;IA0CjB;;;OAGG;IACH,OAAO,CAAC,SAAS;IA4DjB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAuChB;;OAEG;IACH,OAAO,CAAC,YAAY;IAgCpB;;;OAGG;IACH,OAAO,CAAC,cAAc;CAwDvB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,eAAe,CAKnD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,eAAe,CAKpF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAQ3E"}
@@ -454,7 +454,36 @@ class DefaultTemplateRegistry {
454
454
  funcMap.set('lower', (str) => str.toLowerCase());
455
455
  funcMap.set('title', (str) => str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()));
456
456
  funcMap.set('trim', (str) => str.trim());
457
- funcMap.set('replace', (str, old, newStr) => str.replace(new RegExp(old, 'g'), newStr));
457
+ funcMap.set('replace', (...args) => {
458
+ if (args.length < 3) {
459
+ throw new Error('replace requires at least 3 arguments: string, old, new');
460
+ }
461
+ const str = String(args[0]);
462
+ const old = String(args[1]);
463
+ const newStr = String(args[2]);
464
+ // Handle optional limit parameter like Go's implementation
465
+ if (args.length >= 4) {
466
+ const limit = Number(args[3]);
467
+ if (isNaN(limit)) {
468
+ throw new Error('replace limit must be a number');
469
+ }
470
+ // Implement limited replacement like Go's strings.Replace
471
+ let result = str;
472
+ let count = 0;
473
+ let startIndex = 0;
474
+ while (count < limit && startIndex < result.length) {
475
+ const index = result.indexOf(old, startIndex);
476
+ if (index === -1)
477
+ break;
478
+ result = result.substring(0, index) + newStr + result.substring(index + old.length);
479
+ startIndex = index + newStr.length;
480
+ count++;
481
+ }
482
+ return result;
483
+ }
484
+ // Default behavior: replace all occurrences (like Go's strings.ReplaceAll)
485
+ return str.split(old).join(newStr);
486
+ });
458
487
  // Split function - ensure first parameter is the string to split
459
488
  funcMap.set('split', (str, sep) => {
460
489
  // Handle null and undefined cases first
@@ -570,16 +599,22 @@ class DefaultTemplateRegistry {
570
599
  Replace: (s, old, newStr, limit) => {
571
600
  const str = String(s);
572
601
  if (limit === undefined) {
573
- return str.replace(new RegExp(old.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newStr);
602
+ // Replace all occurrences (like Go's strings.ReplaceAll)
603
+ return str.split(old).join(newStr);
574
604
  }
605
+ // Implement limited replacement like Go's strings.Replace
606
+ let result = str;
575
607
  let count = 0;
576
- return str.replace(new RegExp(old.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), (match) => {
577
- if (count < limit) {
578
- count++;
579
- return newStr;
580
- }
581
- return match;
582
- });
608
+ let startIndex = 0;
609
+ while (count < limit && startIndex < result.length) {
610
+ const index = result.indexOf(old, startIndex);
611
+ if (index === -1)
612
+ break;
613
+ result = result.substring(0, index) + newStr + result.substring(index + old.length);
614
+ startIndex = index + newStr.length;
615
+ count++;
616
+ }
617
+ return result;
583
618
  },
584
619
  // Splitting and slicing
585
620
  Split: (s, delimiter) => String(s).split(delimiter),
@@ -629,6 +664,265 @@ class DefaultTemplateRegistry {
629
664
  return str.charAt(0).toUpperCase() + str.slice(1);
630
665
  }
631
666
  }));
667
+ // Substr extracts parts of a string, beginning at the character at the specified
668
+ // position, and returns the specified number of characters.
669
+ // It normally takes two parameters: start and length.
670
+ // It can also take one parameter: start, i.e. length is omitted, in which case
671
+ // the substring starting from start until the end of the string will be returned.
672
+ // To extract characters from the end of the string, use a negative start number.
673
+ // If length is given and is negative, then that many characters will be omitted from
674
+ // the end of string.
675
+ // Equivalent to Go's Substr function in strings namespace
676
+ // Usage: {{ substr "hello world" 0 5 }} -> "hello"
677
+ // Usage: {{ substr "hello world" 6 }} -> "world"
678
+ // Usage: {{ substr "hello world" -5 }} -> "world"
679
+ // Usage: {{ substr "hello world" 0 -6 }} -> "hello"
680
+ funcMap.set('substr', (a, ...nums) => {
681
+ // Convert input to string
682
+ const s = String(a);
683
+ // Convert string to array of characters to handle Unicode properly
684
+ const chars = Array.from(s);
685
+ const rlen = chars.length;
686
+ let start;
687
+ let length;
688
+ // Parse arguments
689
+ switch (nums.length) {
690
+ case 0:
691
+ throw new Error('too few arguments');
692
+ case 1:
693
+ // Only start parameter provided
694
+ const startNum = Number(nums[0]);
695
+ if (isNaN(startNum) || !Number.isInteger(startNum)) {
696
+ throw new Error('start argument must be an integer');
697
+ }
698
+ start = startNum;
699
+ length = rlen;
700
+ break;
701
+ case 2:
702
+ // Both start and length parameters provided
703
+ const startNum2 = Number(nums[0]);
704
+ const lengthNum = Number(nums[1]);
705
+ if (isNaN(startNum2) || !Number.isInteger(startNum2)) {
706
+ throw new Error('start argument must be an integer');
707
+ }
708
+ if (isNaN(lengthNum) || !Number.isInteger(lengthNum)) {
709
+ throw new Error('length argument must be an integer');
710
+ }
711
+ start = startNum2;
712
+ length = lengthNum;
713
+ break;
714
+ default:
715
+ throw new Error('too many arguments');
716
+ }
717
+ // Handle empty string
718
+ if (rlen === 0) {
719
+ return '';
720
+ }
721
+ // Handle negative start (count from end)
722
+ if (start < 0) {
723
+ start += rlen;
724
+ }
725
+ // If start was originally negative beyond rlen, set to 0
726
+ if (start < 0) {
727
+ start = 0;
728
+ }
729
+ // If start is beyond the string length, return empty
730
+ if (start > rlen - 1) {
731
+ return '';
732
+ }
733
+ let end = rlen;
734
+ // Handle different length cases
735
+ if (length === 0) {
736
+ return '';
737
+ }
738
+ else if (length < 0) {
739
+ // Negative length: omit that many characters from end
740
+ end += length;
741
+ }
742
+ else if (length > 0) {
743
+ // Positive length: take that many characters from start
744
+ end = start + length;
745
+ }
746
+ // Validate bounds
747
+ if (start >= end) {
748
+ return '';
749
+ }
750
+ if (end < 0) {
751
+ return '';
752
+ }
753
+ // Clamp end to string length
754
+ if (end > rlen) {
755
+ end = rlen;
756
+ }
757
+ // Extract substring
758
+ return chars.slice(start, end).join('');
759
+ });
760
+ // Truncate truncates the string to the specified length
761
+ // Equivalent to Go's Truncate function in strings namespace
762
+ // Usage: {{ truncate 10 "hello world" }} -> "hello …"
763
+ // Usage: {{ truncate 10 "..." "hello world" }} -> "hello..."
764
+ // Supports HTML-aware truncation
765
+ funcMap.set('truncate', (lengthParam, ...options) => {
766
+ // Parse length parameter
767
+ const length = Number(lengthParam);
768
+ if (isNaN(length) || !Number.isInteger(length)) {
769
+ throw new Error('truncate length must be an integer');
770
+ }
771
+ let textParam;
772
+ let ellipsis = ' …';
773
+ // Parse options
774
+ switch (options.length) {
775
+ case 0:
776
+ throw new Error('truncate requires a length and a string');
777
+ case 1:
778
+ textParam = options[0];
779
+ ellipsis = ' …';
780
+ break;
781
+ case 2:
782
+ ellipsis = String(options[0]);
783
+ textParam = options[1];
784
+ break;
785
+ default:
786
+ throw new Error('too many arguments passed to truncate');
787
+ }
788
+ // Convert text to string
789
+ const text = String(textParam);
790
+ // Check if input is HTML (simple heuristic: contains HTML tags)
791
+ const isHTML = /<[^>]+>/.test(text);
792
+ // Convert to array of characters for proper Unicode handling
793
+ const chars = Array.from(text);
794
+ // If text is already shorter than or equal to length, return as-is
795
+ if (chars.length <= length) {
796
+ if (isHTML) {
797
+ return text; // Return as HTML
798
+ }
799
+ return this.escapeHTML(text);
800
+ }
801
+ if (isHTML) {
802
+ return this.truncateHTML(text, length, ellipsis);
803
+ }
804
+ else {
805
+ return this.truncateText(text, length, ellipsis);
806
+ }
807
+ });
808
+ }
809
+ /**
810
+ * HTML escape utility function
811
+ */
812
+ escapeHTML(text) {
813
+ return text
814
+ .replace(/&/g, '&amp;')
815
+ .replace(/</g, '&lt;')
816
+ .replace(/>/g, '&gt;')
817
+ .replace(/"/g, '&quot;')
818
+ .replace(/'/g, '&#39;');
819
+ }
820
+ /**
821
+ * Truncate plain text, breaking at word boundaries when possible
822
+ */
823
+ truncateText(text, length, ellipsis) {
824
+ const chars = Array.from(text);
825
+ if (chars.length <= length) {
826
+ return this.escapeHTML(text);
827
+ }
828
+ let lastWordIndex = 0;
829
+ let currentLen = 0;
830
+ // Find the best truncation point (word boundary)
831
+ for (let i = 0; i < chars.length && currentLen < length; i++) {
832
+ const char = chars[i];
833
+ currentLen++;
834
+ if (/\s/.test(char)) {
835
+ lastWordIndex = i;
836
+ }
837
+ // For CJK characters, each character can be a word boundary
838
+ if (/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(char)) {
839
+ lastWordIndex = i;
840
+ }
841
+ }
842
+ // Determine truncation point
843
+ let endPos = currentLen >= length ? (lastWordIndex > 0 ? lastWordIndex : length) : chars.length;
844
+ const truncated = chars.slice(0, endPos).join('');
845
+ return this.escapeHTML(truncated) + ellipsis;
846
+ }
847
+ /**
848
+ * Truncate HTML text while preserving tag structure
849
+ */
850
+ truncateHTML(text, length, ellipsis) {
851
+ const tagRegex = /^<(\/)?([^ ]+?)(?:(\s*\/)| .*?)?>/;
852
+ const htmlSinglets = new Set(['br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input']);
853
+ const tags = [];
854
+ let lastWordIndex = 0;
855
+ let lastNonSpace = 0;
856
+ let currentLen = 0;
857
+ let endTextPos = 0;
858
+ let i = 0;
859
+ const chars = Array.from(text);
860
+ while (i < chars.length) {
861
+ const currentChar = chars[i];
862
+ // Check for HTML tag
863
+ if (currentChar === '<') {
864
+ const remainingText = chars.slice(i).join('');
865
+ const match = remainingText.match(tagRegex);
866
+ if (match) {
867
+ const fullMatch = match[0];
868
+ const isClosing = match[1] === '/';
869
+ const tagName = match[2].toLowerCase();
870
+ const isSelfClosing = match[3] === '/';
871
+ // Skip the entire tag
872
+ i += fullMatch.length;
873
+ lastWordIndex = lastNonSpace;
874
+ // Track non-singlet tags for proper closing
875
+ const isSinglet = htmlSinglets.has(tagName);
876
+ if (!isSinglet && !isSelfClosing) {
877
+ tags.push({
878
+ name: tagName,
879
+ pos: i - fullMatch.length,
880
+ openTag: !isClosing
881
+ });
882
+ }
883
+ continue;
884
+ }
885
+ }
886
+ // Count actual text characters
887
+ currentLen++;
888
+ if (/\s/.test(currentChar)) {
889
+ lastWordIndex = lastNonSpace;
890
+ }
891
+ else if (/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(currentChar)) {
892
+ // CJK characters can break at any character
893
+ lastWordIndex = i;
894
+ }
895
+ else {
896
+ lastNonSpace = i + 1;
897
+ }
898
+ if (currentLen > length) {
899
+ endTextPos = lastWordIndex > 0 ? lastWordIndex : i;
900
+ break;
901
+ }
902
+ i++;
903
+ }
904
+ if (currentLen <= length) {
905
+ return text; // No truncation needed
906
+ }
907
+ let output = chars.slice(0, endTextPos).join('') + ellipsis;
908
+ // Close any open HTML tags
909
+ let currentTag = null;
910
+ for (let j = tags.length - 1; j >= 0; j--) {
911
+ const tag = tags[j];
912
+ if (tag.pos >= endTextPos || currentTag !== null) {
913
+ if (currentTag !== null && currentTag.name === tag.name) {
914
+ currentTag = null;
915
+ }
916
+ continue;
917
+ }
918
+ if (tag.openTag) {
919
+ output += `</${tag.name}>`;
920
+ }
921
+ else {
922
+ currentTag = tag;
923
+ }
924
+ }
925
+ return output;
632
926
  }
633
927
  /**
634
928
  * Register math functions
@@ -1355,6 +1649,42 @@ class DefaultTemplateRegistry {
1355
1649
  .join('&');
1356
1650
  return queryString;
1357
1651
  });
1652
+ // IsSet returns whether a given array, slice, or map has the given key defined
1653
+ // Equivalent to Go's IsSet function in collections namespace
1654
+ // Usage: {{ isset .array 0 }} or {{ isset .object "key" }}
1655
+ funcMap.set('isset', (c, key) => {
1656
+ if (c === null || c === undefined) {
1657
+ return false;
1658
+ }
1659
+ // Handle arrays and slices
1660
+ if (Array.isArray(c)) {
1661
+ // Convert key to integer for array indexing
1662
+ const k = Number(key);
1663
+ if (isNaN(k) || !Number.isInteger(k)) {
1664
+ console.warn(`isset unable to use key of type ${typeof key} as index`);
1665
+ return false;
1666
+ }
1667
+ // Check if index is valid (within bounds)
1668
+ return c.length > k && k >= 0;
1669
+ }
1670
+ // Handle objects/maps
1671
+ if (typeof c === 'object') {
1672
+ // For objects, check if the key exists as a property
1673
+ return key in c;
1674
+ }
1675
+ // Handle strings (similar to arrays, but for character indexing)
1676
+ if (typeof c === 'string') {
1677
+ const k = Number(key);
1678
+ if (isNaN(k) || !Number.isInteger(k)) {
1679
+ console.warn(`isset unable to use key of type ${typeof key} as index`);
1680
+ return false;
1681
+ }
1682
+ return c.length > k && k >= 0;
1683
+ }
1684
+ // Unsupported types
1685
+ console.warn(`calling isset with unsupported type "${typeof c}" (${c.constructor?.name || typeof c}) will always return false.`);
1686
+ return false;
1687
+ });
1358
1688
  }
1359
1689
  /**
1360
1690
  * Core indexing implementation following Go's doIndex logic
@@ -2023,6 +2353,14 @@ class DefaultTemplateRegistry {
2023
2353
  return null;
2024
2354
  }
2025
2355
  return this.unmarshalContent(dataStr, options);
2356
+ },
2357
+ // Plainify returns a copy of s with all HTML tags removed
2358
+ // Equivalent to Go's Plainify function in transform namespace
2359
+ // Usage: {{ "Hello <strong>world</strong>!" | transform.Plainify }} -> "Hello world!"
2360
+ // Usage: {{ transform.Plainify "<p>Text with <br> tags</p>" }} -> "Text with tags"
2361
+ Plainify: (s) => {
2362
+ const str = String(s);
2363
+ return this.stripHTML(str);
2026
2364
  }
2027
2365
  }));
2028
2366
  // Also register unmarshal as a standalone function for backward compatibility
@@ -2030,6 +2368,46 @@ class DefaultTemplateRegistry {
2030
2368
  const transform = funcMap.get('transform')();
2031
2369
  return transform.Unmarshal(...args);
2032
2370
  });
2371
+ // Register plainify as a standalone function for convenience
2372
+ funcMap.set('plainify', (s) => {
2373
+ const str = String(s);
2374
+ return this.stripHTML(str);
2375
+ });
2376
+ }
2377
+ /**
2378
+ * Strip HTML tags from a string
2379
+ * Equivalent to Go's helpers.StripHTML function
2380
+ * Preserves line breaks from block elements and normalizes whitespace
2381
+ */
2382
+ stripHTML(html) {
2383
+ // Shortcut for strings with no HTML tags
2384
+ if (!html.includes('<') && !html.includes('>')) {
2385
+ return html;
2386
+ }
2387
+ // First pass: Replace block elements that should create line breaks
2388
+ let result = html
2389
+ .replace(/\n/g, ' ') // Convert newlines to spaces first
2390
+ .replace(/<\/p>/gi, '\n') // </p> creates line break
2391
+ .replace(/<br\s*\/?>/gi, '\n') // <br> and <br/> create line breaks
2392
+ .replace(/<\/div>/gi, '\n') // </div> creates line break
2393
+ .replace(/<\/h[1-6]>/gi, '\n') // Heading end tags create line breaks
2394
+ .replace(/<\/li>/gi, '\n') // List items create line breaks
2395
+ .replace(/<\/tr>/gi, '\n') // Table rows create line breaks
2396
+ .replace(/<\/blockquote>/gi, '\n'); // Blockquotes create line breaks
2397
+ // Second pass: Remove all HTML tags
2398
+ result = result.replace(/<[^>]*>/g, '');
2399
+ // Third pass: Normalize whitespace
2400
+ // Convert multiple spaces to single spaces, but preserve intentional line breaks
2401
+ const lines = result.split('\n');
2402
+ const normalizedLines = lines.map(line => line.trim().replace(/\s+/g, ' '));
2403
+ // Join lines back together, removing empty lines but preserving intentional breaks
2404
+ result = normalizedLines
2405
+ .filter(line => line.length > 0)
2406
+ .join('\n')
2407
+ .trim();
2408
+ // Final cleanup: ensure we don't have multiple consecutive newlines
2409
+ result = result.replace(/\n\s*\n/g, '\n');
2410
+ return result;
2033
2411
  }
2034
2412
  /**
2035
2413
  * Unmarshal content from string based on format