@pikku/addon-console 0.12.43 → 0.12.44

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 (113) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/.pikku/addon/agent/index.d.ts +1 -1
  3. package/dist/.pikku/addon/agent/index.js +1 -1
  4. package/dist/.pikku/addon/agent/pikku-agent-map.gen.d.ts +1 -1
  5. package/dist/.pikku/addon/agent/pikku-agent-types.gen.d.ts +1 -1
  6. package/dist/.pikku/addon/agent/pikku-model-aliases.gen.js +1 -1
  7. package/dist/.pikku/addon/agent/pikku-scorer-names.gen.d.ts +1 -1
  8. package/dist/.pikku/addon/auth/auth.types.d.ts +1 -1
  9. package/dist/.pikku/addon/auth/auth.types.js +1 -1
  10. package/dist/.pikku/addon/auth/index.d.ts +1 -1
  11. package/dist/.pikku/addon/auth/index.js +1 -1
  12. package/dist/.pikku/addon/auth/pikku-auth-types.gen.d.ts +1 -1
  13. package/dist/.pikku/addon/auth/pikku-auth-types.gen.js +1 -1
  14. package/dist/.pikku/addon/channel/index.d.ts +1 -1
  15. package/dist/.pikku/addon/channel/index.js +1 -1
  16. package/dist/.pikku/addon/channel/pikku-channel-types.gen.d.ts +1 -1
  17. package/dist/.pikku/addon/channel/pikku-channel-types.gen.js +1 -1
  18. package/dist/.pikku/addon/error/index.d.ts +1 -1
  19. package/dist/.pikku/addon/error/index.js +1 -1
  20. package/dist/.pikku/addon/error/pikku-error-types.gen.d.ts +1 -1
  21. package/dist/.pikku/addon/error/pikku-error-types.gen.js +1 -1
  22. package/dist/.pikku/addon/function/index.d.ts +1 -1
  23. package/dist/.pikku/addon/function/index.js +1 -1
  24. package/dist/.pikku/addon/function/pikku-function-types.gen.d.ts +1 -1
  25. package/dist/.pikku/addon/function/pikku-function-types.gen.js +1 -1
  26. package/dist/.pikku/addon/function/pikku-functions-meta-verbose.gen.json +3 -3
  27. package/dist/.pikku/addon/function/pikku-functions-meta.gen.js +1 -1
  28. package/dist/.pikku/addon/function/pikku-functions-meta.gen.json +1 -1
  29. package/dist/.pikku/addon/function/pikku-functions.gen.js +1 -1
  30. package/dist/.pikku/addon/http/index.d.ts +1 -1
  31. package/dist/.pikku/addon/http/index.js +1 -1
  32. package/dist/.pikku/addon/http/pikku-http-types.gen.d.ts +1 -1
  33. package/dist/.pikku/addon/http/pikku-http-types.gen.js +1 -1
  34. package/dist/.pikku/addon/mcp/index.d.ts +1 -1
  35. package/dist/.pikku/addon/mcp/index.js +1 -1
  36. package/dist/.pikku/addon/mcp/pikku-mcp-types.gen.d.ts +1 -1
  37. package/dist/.pikku/addon/mcp/pikku-mcp-types.gen.js +1 -1
  38. package/dist/.pikku/addon/middleware/index.d.ts +1 -1
  39. package/dist/.pikku/addon/middleware/index.js +1 -1
  40. package/dist/.pikku/addon/middleware/pikku-middleware-types.gen.d.ts +2 -2
  41. package/dist/.pikku/addon/middleware/pikku-middleware-types.gen.d.ts.map +1 -1
  42. package/dist/.pikku/addon/middleware/pikku-middleware-types.gen.js +1 -1
  43. package/dist/.pikku/addon/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  44. package/dist/.pikku/addon/pikku-bootstrap-scenarios.gen.js +1 -1
  45. package/dist/.pikku/addon/pikku-bootstrap.gen.d.ts +1 -1
  46. package/dist/.pikku/addon/pikku-bootstrap.gen.js +1 -1
  47. package/dist/.pikku/addon/pikku-package.gen.js +1 -1
  48. package/dist/.pikku/addon/pikku-services.gen.d.ts +1 -1
  49. package/dist/.pikku/addon/rpc/pikku-rpc-wirings-map.gen.d.ts +1 -1
  50. package/dist/.pikku/addon/rpc/pikku-rpc-wirings-map.internal.gen.d.ts +1 -1
  51. package/dist/.pikku/addon/rpc/pikku-rpc-wirings-map.remote.gen.d.ts +1 -1
  52. package/dist/.pikku/addon/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  53. package/dist/.pikku/addon/scenarios/index.d.ts +1 -1
  54. package/dist/.pikku/addon/scenarios/index.js +1 -1
  55. package/dist/.pikku/addon/scenarios/pikku-personas.gen.js +1 -1
  56. package/dist/.pikku/addon/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  57. package/dist/.pikku/addon/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  58. package/dist/.pikku/addon/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  59. package/dist/.pikku/addon/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  60. package/dist/.pikku/addon/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  61. package/dist/.pikku/addon/scenarios/schemas/register.gen.d.ts +1 -1
  62. package/dist/.pikku/addon/scenarios/schemas/register.gen.js +1 -1
  63. package/dist/.pikku/addon/schemas/register.gen.js +1 -1
  64. package/dist/.pikku/addon/scopes/index.d.ts +1 -1
  65. package/dist/.pikku/addon/scopes/index.js +1 -1
  66. package/dist/.pikku/addon/scopes/pikku-personas.gen.js +1 -1
  67. package/dist/.pikku/addon/scopes/pikku-roles.gen.d.ts +1 -1
  68. package/dist/.pikku/addon/scopes/pikku-scope-types.gen.d.ts +1 -1
  69. package/dist/.pikku/addon/scopes/pikku-scope-types.gen.js +1 -1
  70. package/dist/.pikku/addon/scopes/pikku-scopes.gen.d.ts +1 -1
  71. package/dist/.pikku/addon/secrets/index.d.ts +1 -1
  72. package/dist/.pikku/addon/secrets/index.js +1 -1
  73. package/dist/.pikku/addon/secrets/pikku-secret-types.gen.d.ts +1 -1
  74. package/dist/.pikku/addon/secrets/pikku-secret-types.gen.js +1 -1
  75. package/dist/.pikku/addon/secrets/pikku-secrets.gen.d.ts +1 -1
  76. package/dist/.pikku/addon/secrets/pikku-secrets.gen.js +1 -1
  77. package/dist/.pikku/addon/services/pikku-meta-service.gen.d.ts +1 -1
  78. package/dist/.pikku/addon/services/pikku-meta-service.gen.js +1 -1
  79. package/dist/.pikku/addon/setup/index.d.ts +1 -1
  80. package/dist/.pikku/addon/setup/index.js +1 -1
  81. package/dist/.pikku/addon/setup/pikku-addon-setup-types.gen.d.ts +1 -1
  82. package/dist/.pikku/addon/setup/pikku-addon-setup-types.gen.js +1 -1
  83. package/dist/.pikku/addon/setup/pikku-setup-types.gen.d.ts +1 -1
  84. package/dist/.pikku/addon/setup/pikku-setup-types.gen.js +1 -1
  85. package/dist/.pikku/addon/trigger/index.d.ts +1 -1
  86. package/dist/.pikku/addon/trigger/index.js +1 -1
  87. package/dist/.pikku/addon/trigger/pikku-trigger-types.gen.d.ts +1 -1
  88. package/dist/.pikku/addon/trigger/pikku-trigger-types.gen.js +1 -1
  89. package/dist/.pikku/addon/variables/index.d.ts +1 -1
  90. package/dist/.pikku/addon/variables/index.js +1 -1
  91. package/dist/.pikku/addon/variables/pikku-variable-types.gen.d.ts +1 -1
  92. package/dist/.pikku/addon/variables/pikku-variable-types.gen.js +1 -1
  93. package/dist/.pikku/addon/variables/pikku-variables.gen.d.ts +1 -1
  94. package/dist/.pikku/addon/variables/pikku-variables.gen.js +1 -1
  95. package/dist/.pikku/addon/workflow/index.d.ts +1 -1
  96. package/dist/.pikku/addon/workflow/index.js +1 -1
  97. package/dist/.pikku/addon/workflow/pikku-scenario-step-map.gen.d.ts +1 -1
  98. package/dist/.pikku/addon/workflow/pikku-workflow-map.gen.d.ts +1 -1
  99. package/dist/.pikku/addon/workflow/pikku-workflow-types.gen.d.ts +1 -1
  100. package/dist/.pikku/addon/workflow/pikku-workflow-types.gen.js +1 -1
  101. package/dist/.pikku/addon/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  102. package/dist/.pikku/addon/workflow/pikku-workflow-wirings.gen.d.ts +1 -1
  103. package/dist/.pikku/addon/workflow/pikku-workflow-wirings.gen.js +1 -1
  104. package/dist/src/functions/render-email-preview.function.d.ts.map +1 -1
  105. package/dist/src/functions/render-email-preview.function.js +6 -17
  106. package/dist/src/functions/render-email-preview.function.js.map +1 -1
  107. package/dist/src/functions/render-email-template.utils.d.ts +8 -3
  108. package/dist/src/functions/render-email-template.utils.d.ts.map +1 -1
  109. package/dist/src/functions/render-email-template.utils.js +71 -17
  110. package/dist/src/functions/render-email-template.utils.js.map +1 -1
  111. package/dist/src/functions/render-email-template.utils.test.js +56 -23
  112. package/dist/src/functions/render-email-template.utils.test.js.map +1 -1
  113. package/package.json +4 -4
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/secrets` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/secrets` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  export { defineSecret } from '@pikku/core/secret';
5
5
  //# sourceMappingURL=pikku-secret-types.gen.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  export { defineSecret } from '@pikku/core/secret';
5
5
  //# sourceMappingURL=pikku-secret-types.gen.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { TypedSecretService as CoreTypedSecretService } from '@pikku/core/services';
5
5
  import type { SecretService } from '@pikku/core/services';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { TypedSecretService as CoreTypedSecretService } from '@pikku/core/services';
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { LocalMetaService } from '@pikku/core/services/local-meta';
5
5
  export declare class PikkuMetaService extends LocalMetaService {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { LocalMetaService } from '@pikku/core/services/local-meta';
5
5
  export class PikkuMetaService extends LocalMetaService {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/setup` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/setup` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Addon package types for pikkuAddonConfig and pikkuAddonServices
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Addon package types for pikkuAddonConfig and pikkuAddonServices
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Config and service factory definitions, declared once per application
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Config and service factory definitions, declared once per application
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/trigger` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/trigger` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Trigger-specific type definitions for tree-shaking optimization
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Trigger-specific type definitions for tree-shaking optimization
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/variables` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/variables` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  export { defineVariable } from '@pikku/core/variable';
5
5
  //# sourceMappingURL=pikku-variable-types.gen.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  export { defineVariable } from '@pikku/core/variable';
5
5
  //# sourceMappingURL=pikku-variable-types.gen.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { TypedVariablesService as CoreTypedVariablesService } from '@pikku/core/services';
5
5
  import type { VariablesService } from '@pikku/core/services';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { TypedVariablesService as CoreTypedVariablesService, } from '@pikku/core/services';
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/workflow` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Subpath entry — `#pikku/workflow` resolves here.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Scenario step map: the names `scenario.given/when/then` accepts, with
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  /**
5
5
  * Workflow type map with input/output types for each workflow
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { WorkflowCancelledException } from '@pikku/core/workflow';
5
5
  import type { PikkuWorkflowGraphConfig, PikkuWorkflowGraphResult } from '@pikku/core/workflow';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { WorkflowCancelledException } from '@pikku/core/workflow';
5
5
  import { pikkuWorkflowGraph as corePikkuWorkflowGraph } from '@pikku/core/workflow';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.107
2
+ * This file was generated by @pikku/cli@0.12.109
3
3
  */
4
4
  import { pikkuState } from '@pikku/core/state';
5
5
  const workflowsMeta = {};
@@ -1,5 +1,5 @@
1
1
  export {};
2
2
  /**
3
- * This file was generated by @pikku/cli@0.12.107
3
+ * This file was generated by @pikku/cli@0.12.109
4
4
  */
5
5
  //# sourceMappingURL=pikku-workflow-wirings.gen.d.ts.map
@@ -1,5 +1,5 @@
1
1
  export {};
2
2
  /**
3
- * This file was generated by @pikku/cli@0.12.107
3
+ * This file was generated by @pikku/cli@0.12.109
4
4
  */
5
5
  //# sourceMappingURL=pikku-workflow-wirings.gen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-email-preview.function.d.ts","sourceRoot":"","sources":["../../../src/functions/render-email-preview.function.ts"],"names":[],"mappings":"AASA,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;AAClE,KAAK,kBAAkB,GACrB,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;AAE3D,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,eAAO,MAAM,kBAAkB,8eAsF7B,CAAA"}
1
+ {"version":3,"file":"render-email-preview.function.d.ts","sourceRoot":"","sources":["../../../src/functions/render-email-preview.function.ts"],"names":[],"mappings":"AAQA,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;AAClE,KAAK,kBAAkB,GACrB,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;AAE3D,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,eAAO,MAAM,kBAAkB,8eA4F7B,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { pikkuFunc } from '#pikku/addon/function';
2
2
  import { NotFoundError } from '#pikku/addon/error';
3
- import { getNestedValue, renderTemplate, renderPartial, } from './render-email-template.utils.js';
3
+ import { getNestedValue, renderTemplate, } from './render-email-template.utils.js';
4
4
  export const renderEmailPreview = pikkuFunc({
5
5
  title: 'Render Email Preview',
6
6
  description: 'Renders an email template preview from emailTemplatesDir using a locale and variable payload.',
@@ -24,34 +24,23 @@ export const renderEmailPreview = pikkuFunc({
24
24
  t: assets.strings,
25
25
  appName,
26
26
  };
27
- const subject = renderTemplate(assets.subject, baseContext).trim();
28
- const htmlWithPartials = assets.html.replace(/\{\{\s*>\s*([a-zA-Z0-9-_/.]+)\s*\}\}/g, (_match, partialName) => `@@PARTIAL:${String(partialName).trim()}@@`);
29
- let htmlBody = renderTemplate(htmlWithPartials, {
27
+ const subject = renderTemplate(assets.subject, baseContext, assets.partials, false).trim();
28
+ const htmlBody = renderTemplate(assets.html, {
30
29
  ...baseContext,
31
30
  subject,
32
- });
33
- const partialMatches = [...htmlBody.matchAll(/@@PARTIAL:([^@]+)@@/g)];
34
- for (const match of partialMatches) {
35
- if (!match[1])
36
- continue;
37
- const rendered = renderPartial(match[1], assets.partials, {
38
- ...baseContext,
39
- subject,
40
- });
41
- htmlBody = htmlBody.replace(match[0], rendered);
42
- }
31
+ }, assets.partials, true);
43
32
  const html = assets.layout
44
33
  ? renderTemplate(assets.layout, {
45
34
  ...baseContext,
46
35
  subject,
47
36
  content: htmlBody,
48
- })
37
+ }, assets.partials, true)
49
38
  : htmlBody;
50
39
  const text = assets.text
51
40
  ? renderTemplate(assets.text, {
52
41
  ...baseContext,
53
42
  subject,
54
- }).trim()
43
+ }, assets.partials, false).trim()
55
44
  : undefined;
56
45
  return {
57
46
  name: input.templateName,
@@ -1 +1 @@
1
- {"version":3,"file":"render-email-preview.function.js","sourceRoot":"","sources":["../../../src/functions/render-email-preview.function.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render-email-preview.function.js","sourceRoot":"","sources":["../../../src/functions/render-email-preview.function.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,kCAAkC,CAAA;AAyBzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAGzC;IACA,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EACT,+FAA+F;IACjG,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,CAAC,2BAA2B,CAAC;IACrC,IAAI,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE;QACrC,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,YAAY,EAAE,CAAA;QACnD,MAAM,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,CAC7B,CAAA;QAE/B,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,aAAa,CAAC,2BAA2B,KAAK,CAAC,YAAY,EAAE,CAAC,CAAA;QAC1E,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QAE3E,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,sBAAsB,CACrD,KAAK,CAAC,YAAY,EAClB,MAAM,CACP,CAAA;QAED,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAA;QAC1D,MAAM,OAAO,GACX,CAAC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC;YAClD,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;QAEzC,MAAM,WAAW,GAAG;YAClB,GAAG,IAAI;YACP,MAAM;YACN,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,CAAC,EAAE,MAAM,CAAC,OAAO;YACjB,OAAO;SACR,CAAA;QAED,MAAM,OAAO,GAAG,cAAc,CAC5B,MAAM,CAAC,OAAO,EACd,WAAW,EACX,MAAM,CAAC,QAAQ,EACf,KAAK,CACN,CAAC,IAAI,EAAE,CAAA;QAER,MAAM,QAAQ,GAAG,cAAc,CAC7B,MAAM,CAAC,IAAI,EACX;YACE,GAAG,WAAW;YACd,OAAO;SACR,EACD,MAAM,CAAC,QAAQ,EACf,IAAI,CACL,CAAA;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM;YACxB,CAAC,CAAC,cAAc,CACZ,MAAM,CAAC,MAAM,EACb;gBACE,GAAG,WAAW;gBACd,OAAO;gBACP,OAAO,EAAE,QAAQ;aAClB,EACD,MAAM,CAAC,QAAQ,EACf,IAAI,CACL;YACH,CAAC,CAAC,QAAQ,CAAA;QAEZ,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI;YACtB,CAAC,CAAC,cAAc,CACZ,MAAM,CAAC,IAAI,EACX;gBACE,GAAG,WAAW;gBACd,OAAO;aACR,EACD,MAAM,CAAC,QAAQ,EACf,KAAK,CACN,CAAC,IAAI,EAAE;YACV,CAAC,CAAC,SAAS,CAAA;QAEb,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,YAAY;YACxB,MAAM;YACN,OAAO;YACP,IAAI;YACJ,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,MAAM,EAAE,MAAM,CAAC,IAAI;YACnB,SAAS,EAAE,YAAY,CAAC,SAAS;YACjC,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,IAAI,EAAE;YACrD,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -1,5 +1,10 @@
1
+ export declare function escapeHtml(value: string): string;
1
2
  export declare function getNestedValue(source: Record<string, unknown>, path: string): string;
2
- export declare function applyTemplate(source: string, context: Record<string, unknown>): string;
3
- export declare function renderTemplate(source: string, context: Record<string, unknown>): string;
4
- export declare function renderPartial(name: string, partials: Record<string, string>, context: Record<string, unknown>): string;
3
+ export declare function expandPartials(source: string, partials: Record<string, string>, depth?: number): string;
4
+ /**
5
+ * A single substitution pass the replacement text is never rescanned, so a
6
+ * caller-supplied value can never be reinterpreted as a template.
7
+ */
8
+ export declare function substitute(source: string, context: Record<string, unknown>, escape: boolean): string;
9
+ export declare function renderTemplate(source: string, context: Record<string, unknown>, partials: Record<string, string>, escape: boolean): string;
5
10
  //# sourceMappingURL=render-email-template.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-email-template.utils.d.ts","sourceRoot":"","sources":["../../../src/functions/render-email-template.utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,GACX,MAAM,CAYR;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAWR;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAQR;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAGR"}
1
+ {"version":3,"file":"render-email-template.utils.d.ts","sourceRoot":"","sources":["../../../src/functions/render-email-template.utils.ts"],"names":[],"mappings":"AAyBA,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,GACX,MAAM,CAYR;AAWD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,KAAK,SAAI,GACR,MAAM,CASR;AA0BD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,OAAO,GACd,MAAM,CAYR;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,MAAM,EAAE,OAAO,GACd,MAAM,CAOR"}
@@ -1,3 +1,26 @@
1
+ const HTML_ESCAPES = {
2
+ '&': '&amp;',
3
+ '<': '&lt;',
4
+ '>': '&gt;',
5
+ '"': '&quot;',
6
+ "'": '&#39;',
7
+ };
8
+ /**
9
+ * Matches the raw `{{{ value }}}` form before the escaped `{{ value }}` form, so
10
+ * the opt-in escape hatch is never mistaken for a normal substitution.
11
+ */
12
+ const TEMPLATE_TOKEN = /\{\{\{\s*([^{}]+?)\s*\}\}\}|\{\{\s*([^}]+?)\s*\}\}/g;
13
+ const PARTIAL_TOKEN = /\{\{\s*>\s*([a-zA-Z0-9-_/.]+)\s*\}\}/g;
14
+ const MAX_TEMPLATE_DEPTH = 5;
15
+ /**
16
+ * theme.json and the locale files ship with the templates, so they are treated
17
+ * as template-author input: expanded before caller data and allowed to contain
18
+ * their own placeholders. Everything else is caller-supplied.
19
+ */
20
+ const TRUSTED_ROOTS = ['theme', 't'];
21
+ export function escapeHtml(value) {
22
+ return value.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char] ?? char);
23
+ }
1
24
  export function getNestedValue(source, path) {
2
25
  const segments = path.split('.');
3
26
  let current = source;
@@ -11,30 +34,61 @@ export function getNestedValue(source, path) {
11
34
  ? String(current)
12
35
  : '';
13
36
  }
14
- export function applyTemplate(source, context) {
15
- return source.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_match, rawKey) => {
16
- const key = String(rawKey).trim();
17
- if (key === 'content') {
18
- return typeof context.content === 'string' ? context.content : '';
19
- }
20
- if (key.startsWith('>')) {
21
- return '';
22
- }
23
- return getNestedValue(context, key);
37
+ function isTrustedKey(key) {
38
+ return TRUSTED_ROOTS.includes(String(key.split('.')[0]));
39
+ }
40
+ function readToken(rawTriple, rawDouble) {
41
+ const raw = typeof rawTriple === 'string';
42
+ return { raw, key: String(raw ? rawTriple : rawDouble).trim() };
43
+ }
44
+ export function expandPartials(source, partials, depth = 0) {
45
+ if (depth >= MAX_TEMPLATE_DEPTH)
46
+ return source;
47
+ let found = false;
48
+ const expanded = source.replace(PARTIAL_TOKEN, (_match, partialName) => {
49
+ found = true;
50
+ const partial = partials[String(partialName).trim()];
51
+ return typeof partial === 'string' ? partial : '';
24
52
  });
53
+ return found ? expandPartials(expanded, partials, depth + 1) : expanded;
25
54
  }
26
- export function renderTemplate(source, context) {
55
+ function expandTrusted(source, context, escape) {
27
56
  let rendered = source;
28
- for (let i = 0; i < 5; i += 1) {
29
- const next = applyTemplate(rendered, context);
30
- if (next === rendered)
57
+ for (let i = 0; i < MAX_TEMPLATE_DEPTH; i += 1) {
58
+ let found = false;
59
+ const next = rendered.replace(TEMPLATE_TOKEN, (match, rawTriple, rawDouble) => {
60
+ const { raw, key } = readToken(rawTriple, rawDouble);
61
+ if (!isTrustedKey(key))
62
+ return match;
63
+ found = true;
64
+ const value = getNestedValue(context, key);
65
+ return raw || !escape ? value : escapeHtml(value);
66
+ });
67
+ if (!found || next === rendered)
31
68
  break;
32
69
  rendered = next;
33
70
  }
34
71
  return rendered;
35
72
  }
36
- export function renderPartial(name, partials, context) {
37
- const partial = partials[name];
38
- return partial ? renderTemplate(partial, context) : '';
73
+ /**
74
+ * A single substitution pass — the replacement text is never rescanned, so a
75
+ * caller-supplied value can never be reinterpreted as a template.
76
+ */
77
+ export function substitute(source, context, escape) {
78
+ return source.replace(TEMPLATE_TOKEN, (_match, rawTriple, rawDouble) => {
79
+ const { raw, key } = readToken(rawTriple, rawDouble);
80
+ if (key === 'content') {
81
+ return typeof context.content === 'string' ? context.content : '';
82
+ }
83
+ if (key.startsWith('>')) {
84
+ return '';
85
+ }
86
+ const value = getNestedValue(context, key);
87
+ return raw || !escape ? value : escapeHtml(value);
88
+ });
89
+ }
90
+ export function renderTemplate(source, context, partials, escape) {
91
+ const composed = expandTrusted(expandPartials(source, partials), context, escape);
92
+ return substitute(composed, context, escape);
39
93
  }
40
94
  //# sourceMappingURL=render-email-template.utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-email-template.utils.js","sourceRoot":"","sources":["../../../src/functions/render-email-template.utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAC5B,MAA+B,EAC/B,IAAY;IAEZ,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAChC,IAAI,OAAO,GAAY,MAAM,CAAA;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,EAAE,CAAC;YACrE,OAAO,EAAE,CAAA;QACX,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAA;IACzD,CAAC;IACD,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ;QAC/D,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACjB,CAAC,CAAC,EAAE,CAAA;AACR,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,OAAgC;IAEhC,OAAO,MAAM,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;QAClE,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QACjC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QACnE,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,EAAE,CAAA;QACX,CAAC;QACD,OAAO,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,MAAc,EACd,OAAgC;IAEhC,IAAI,QAAQ,GAAG,MAAM,CAAA;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC7C,IAAI,IAAI,KAAK,QAAQ;YAAE,MAAK;QAC5B,QAAQ,GAAG,IAAI,CAAA;IACjB,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,QAAgC,EAChC,OAAgC;IAEhC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACxD,CAAC"}
1
+ {"version":3,"file":"render-email-template.utils.js","sourceRoot":"","sources":["../../../src/functions/render-email-template.utils.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { describe, test } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import { getNestedValue, applyTemplate, renderTemplate, renderPartial, } from './render-email-template.utils.js';
3
+ import { escapeHtml, expandPartials, getNestedValue, renderTemplate, substitute, } from './render-email-template.utils.js';
4
4
  describe('getNestedValue', () => {
5
5
  test('returns top-level string value', () => {
6
6
  assert.equal(getNestedValue({ name: 'Alice' }, 'name'), 'Alice');
@@ -21,45 +21,78 @@ describe('getNestedValue', () => {
21
21
  assert.equal(getNestedValue({ count: 42 }, 'count'), '42');
22
22
  });
23
23
  });
24
- describe('applyTemplate', () => {
24
+ describe('escapeHtml', () => {
25
+ test('escapes the five html-significant characters', () => {
26
+ assert.equal(escapeHtml(`<a href="x" title='y'>&</a>`), '&lt;a href=&quot;x&quot; title=&#39;y&#39;&gt;&amp;&lt;/a&gt;');
27
+ });
28
+ });
29
+ describe('substitute', () => {
25
30
  test('replaces simple placeholder', () => {
26
- assert.equal(applyTemplate('Hello {{ name }}!', { name: 'Bob' }), 'Hello Bob!');
31
+ assert.equal(substitute('Hello {{ name }}!', { name: 'Bob' }, false), 'Hello Bob!');
32
+ });
33
+ test('escapes the value when rendering html', () => {
34
+ assert.equal(substitute('<p>{{ name }}</p>', { name: '<b>Bob</b>' }, true), '<p>&lt;b&gt;Bob&lt;/b&gt;</p>');
35
+ });
36
+ test('leaves the value raw when rendering plain text', () => {
37
+ assert.equal(substitute('{{ name }}', { name: 'Ada & Bob' }, false), 'Ada & Bob');
27
38
  });
28
- test('replaces {{content}} with context.content string', () => {
29
- assert.equal(applyTemplate('Body: {{ content }}', { content: '<p>Hi</p>' }), 'Body: <p>Hi</p>');
39
+ test('renders the triple-brace form raw even in html', () => {
40
+ assert.equal(substitute('<p>{{{ name }}}</p>', { name: '<b>Bob</b>' }, true), '<p><b>Bob</b></p>');
41
+ });
42
+ test('replaces {{content}} with context.content string, unescaped', () => {
43
+ assert.equal(substitute('Body: {{ content }}', { content: '<p>Hi</p>' }, true), 'Body: <p>Hi</p>');
30
44
  });
31
45
  test('replaces {{content}} with empty string when not a string', () => {
32
- assert.equal(applyTemplate('{{ content }}', { content: 123 }), '');
46
+ assert.equal(substitute('{{ content }}', { content: 123 }, true), '');
33
47
  });
34
48
  test('strips partial tags (> prefix)', () => {
35
- assert.equal(applyTemplate('{{ > header }}', {}), '');
49
+ assert.equal(substitute('{{ > header }}', {}, true), '');
36
50
  });
37
51
  test('returns empty string for missing placeholder', () => {
38
- assert.equal(applyTemplate('{{ missing }}', {}), '');
52
+ assert.equal(substitute('{{ missing }}', {}, true), '');
39
53
  });
40
54
  test('resolves dot-path placeholders', () => {
41
- assert.equal(applyTemplate('{{ user.name }}', { user: { name: 'Carol' } }), 'Carol');
55
+ assert.equal(substitute('{{ user.name }}', { user: { name: 'Carol' } }, true), 'Carol');
56
+ });
57
+ test('does not rescan the substituted value', () => {
58
+ assert.equal(substitute('{{ a }}', { a: '{{ b }}', b: 'leaked' }, false), '{{ b }}');
42
59
  });
43
60
  });
44
- describe('renderTemplate', () => {
45
- test('resolves single pass', () => {
46
- assert.equal(renderTemplate('Hi {{ name }}', { name: 'Dave' }), 'Hi Dave');
61
+ describe('expandPartials', () => {
62
+ test('inlines a known partial', () => {
63
+ assert.equal(expandPartials('{{> header }}', { header: '<h1>{{ title }}</h1>' }), '<h1>{{ title }}</h1>');
47
64
  });
48
- test('resolves transitive placeholders up to 5 passes', () => {
49
- const ctx = { greeting: 'Hi {{ name }}', name: 'Eve' };
50
- assert.equal(renderTemplate('{{ greeting }}', ctx), 'Hi Eve');
65
+ test('inlines nested partials', () => {
66
+ assert.equal(expandPartials('{{> a }}', { a: 'A{{> b }}', b: 'B' }), 'AB');
51
67
  });
52
- test('stops early when no more changes', () => {
53
- assert.equal(renderTemplate('static', {}), 'static');
68
+ test('drops an unknown partial', () => {
69
+ assert.equal(expandPartials('x{{> missing }}y', {}), 'xy');
54
70
  });
55
71
  });
56
- describe('renderPartial', () => {
57
- test('renders a known partial with context', () => {
58
- const partials = { header: '<h1>{{ title }}</h1>' };
59
- assert.equal(renderPartial('header', partials, { title: 'Welcome' }), '<h1>Welcome</h1>');
72
+ describe('renderTemplate', () => {
73
+ test('resolves a caller variable in one pass', () => {
74
+ assert.equal(renderTemplate('Hi {{ name }}', { name: 'Dave' }, {}, false), 'Hi Dave');
75
+ });
76
+ test('expands a locale string that itself references a caller variable', () => {
77
+ const ctx = { t: { greeting: 'Hi {{ name }}' }, name: 'Eve' };
78
+ assert.equal(renderTemplate('{{ t.greeting }}', ctx, {}, false), 'Hi Eve');
79
+ });
80
+ test('never expands a caller value as a template', () => {
81
+ const ctx = { t: { note: 'secret' }, name: '{{ t.note }}' };
82
+ assert.equal(renderTemplate('{{ name }}', ctx, {}, false), '{{ t.note }}');
83
+ });
84
+ test('escapes a theme value landing in an attribute', () => {
85
+ const ctx = { theme: { fonts: { body: '"Segoe UI", sans-serif' } } };
86
+ assert.equal(renderTemplate('<p style="font-family:{{theme.fonts.body}};">', ctx, {}, true), '<p style="font-family:&quot;Segoe UI&quot;, sans-serif;">');
60
87
  });
61
- test('returns empty string for unknown partial', () => {
62
- assert.equal(renderPartial('missing', {}, {}), '');
88
+ test('renders partials before substitution', () => {
89
+ assert.equal(renderTemplate('{{> header }}', { title: 'Welcome' }, { header: '<h1>{{ title }}</h1>' }, true), '<h1>Welcome</h1>');
90
+ });
91
+ test('a caller value cannot forge a partial include', () => {
92
+ assert.equal(renderTemplate('{{ name }}', { name: '{{> header }}' }, { header: 'LEAKED' }, false), '{{> header }}');
93
+ });
94
+ test('stops early when no more changes', () => {
95
+ assert.equal(renderTemplate('static', {}, {}, true), 'static');
63
96
  });
64
97
  });
65
98
  //# sourceMappingURL=render-email-template.utils.test.js.map