open-agreements 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/bin/open-agreements.js +2 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.d.ts.map +1 -0
  6. package/dist/cli/index.js +102 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/commands/fill.d.ts +7 -0
  9. package/dist/commands/fill.d.ts.map +1 -0
  10. package/dist/commands/fill.js +84 -0
  11. package/dist/commands/fill.js.map +1 -0
  12. package/dist/commands/list.d.ts +6 -0
  13. package/dist/commands/list.d.ts.map +1 -0
  14. package/dist/commands/list.js +202 -0
  15. package/dist/commands/list.js.map +1 -0
  16. package/dist/commands/recipe.d.ts +21 -0
  17. package/dist/commands/recipe.d.ts.map +1 -0
  18. package/dist/commands/recipe.js +71 -0
  19. package/dist/commands/recipe.js.map +1 -0
  20. package/dist/commands/scan.d.ts +12 -0
  21. package/dist/commands/scan.d.ts.map +1 -0
  22. package/dist/commands/scan.js +122 -0
  23. package/dist/commands/scan.js.map +1 -0
  24. package/dist/commands/validate.d.ts +6 -0
  25. package/dist/commands/validate.d.ts.map +1 -0
  26. package/dist/commands/validate.js +139 -0
  27. package/dist/commands/validate.js.map +1 -0
  28. package/dist/core/command-generation/adapters/claude.d.ts +11 -0
  29. package/dist/core/command-generation/adapters/claude.d.ts.map +1 -0
  30. package/dist/core/command-generation/adapters/claude.js +85 -0
  31. package/dist/core/command-generation/adapters/claude.js.map +1 -0
  32. package/dist/core/command-generation/types.d.ts +14 -0
  33. package/dist/core/command-generation/types.d.ts.map +1 -0
  34. package/dist/core/command-generation/types.js +2 -0
  35. package/dist/core/command-generation/types.js.map +1 -0
  36. package/dist/core/engine.d.ts +13 -0
  37. package/dist/core/engine.d.ts.map +1 -0
  38. package/dist/core/engine.js +149 -0
  39. package/dist/core/engine.js.map +1 -0
  40. package/dist/core/external/index.d.ts +8 -0
  41. package/dist/core/external/index.d.ts.map +1 -0
  42. package/dist/core/external/index.js +92 -0
  43. package/dist/core/external/index.js.map +1 -0
  44. package/dist/core/external/types.d.ts +18 -0
  45. package/dist/core/external/types.d.ts.map +1 -0
  46. package/dist/core/external/types.js +2 -0
  47. package/dist/core/external/types.js.map +1 -0
  48. package/dist/core/fill-pipeline.d.ts +61 -0
  49. package/dist/core/fill-pipeline.d.ts.map +1 -0
  50. package/dist/core/fill-pipeline.js +279 -0
  51. package/dist/core/fill-pipeline.js.map +1 -0
  52. package/dist/core/fill-utils.d.ts +39 -0
  53. package/dist/core/fill-utils.d.ts.map +1 -0
  54. package/dist/core/fill-utils.js +127 -0
  55. package/dist/core/fill-utils.js.map +1 -0
  56. package/dist/core/metadata.d.ts +396 -0
  57. package/dist/core/metadata.d.ts.map +1 -0
  58. package/dist/core/metadata.js +126 -0
  59. package/dist/core/metadata.js.map +1 -0
  60. package/dist/core/recipe/cleaner.d.ts +13 -0
  61. package/dist/core/recipe/cleaner.d.ts.map +1 -0
  62. package/dist/core/recipe/cleaner.js +106 -0
  63. package/dist/core/recipe/cleaner.js.map +1 -0
  64. package/dist/core/recipe/downloader.d.ts +8 -0
  65. package/dist/core/recipe/downloader.d.ts.map +1 -0
  66. package/dist/core/recipe/downloader.js +58 -0
  67. package/dist/core/recipe/downloader.js.map +1 -0
  68. package/dist/core/recipe/index.d.ts +14 -0
  69. package/dist/core/recipe/index.d.ts.map +1 -0
  70. package/dist/core/recipe/index.js +91 -0
  71. package/dist/core/recipe/index.js.map +1 -0
  72. package/dist/core/recipe/ooxml-parts.d.ts +21 -0
  73. package/dist/core/recipe/ooxml-parts.d.ts.map +1 -0
  74. package/dist/core/recipe/ooxml-parts.js +33 -0
  75. package/dist/core/recipe/ooxml-parts.js.map +1 -0
  76. package/dist/core/recipe/patcher.d.ts +17 -0
  77. package/dist/core/recipe/patcher.d.ts.map +1 -0
  78. package/dist/core/recipe/patcher.js +240 -0
  79. package/dist/core/recipe/patcher.js.map +1 -0
  80. package/dist/core/recipe/types.d.ts +28 -0
  81. package/dist/core/recipe/types.d.ts.map +1 -0
  82. package/dist/core/recipe/types.js +2 -0
  83. package/dist/core/recipe/types.js.map +1 -0
  84. package/dist/core/recipe/verifier.d.ts +24 -0
  85. package/dist/core/recipe/verifier.d.ts.map +1 -0
  86. package/dist/core/recipe/verifier.js +143 -0
  87. package/dist/core/recipe/verifier.js.map +1 -0
  88. package/dist/core/validation/external.d.ts +16 -0
  89. package/dist/core/validation/external.d.ts.map +1 -0
  90. package/dist/core/validation/external.js +106 -0
  91. package/dist/core/validation/external.js.map +1 -0
  92. package/dist/core/validation/license.d.ts +15 -0
  93. package/dist/core/validation/license.d.ts.map +1 -0
  94. package/dist/core/validation/license.js +30 -0
  95. package/dist/core/validation/license.js.map +1 -0
  96. package/dist/core/validation/output.d.ts +12 -0
  97. package/dist/core/validation/output.d.ts.map +1 -0
  98. package/dist/core/validation/output.js +47 -0
  99. package/dist/core/validation/output.js.map +1 -0
  100. package/dist/core/validation/recipe.d.ts +19 -0
  101. package/dist/core/validation/recipe.d.ts.map +1 -0
  102. package/dist/core/validation/recipe.js +148 -0
  103. package/dist/core/validation/recipe.js.map +1 -0
  104. package/dist/core/validation/template.d.ts +11 -0
  105. package/dist/core/validation/template.d.ts.map +1 -0
  106. package/dist/core/validation/template.js +159 -0
  107. package/dist/core/validation/template.js.map +1 -0
  108. package/dist/index.d.ts +13 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +19 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/utils/paths.d.ts +15 -0
  113. package/dist/utils/paths.d.ts.map +1 -0
  114. package/dist/utils/paths.js +43 -0
  115. package/dist/utils/paths.js.map +1 -0
  116. package/external/LICENSE +27 -0
  117. package/external/README.md +38 -0
  118. package/external/yc-safe-discount/README.md +16 -0
  119. package/external/yc-safe-discount/clean.json +4 -0
  120. package/external/yc-safe-discount/metadata.yaml +71 -0
  121. package/external/yc-safe-discount/replacements.json +13 -0
  122. package/external/yc-safe-discount/template.docx +0 -0
  123. package/external/yc-safe-mfn/README.md +16 -0
  124. package/external/yc-safe-mfn/clean.json +4 -0
  125. package/external/yc-safe-mfn/metadata.yaml +64 -0
  126. package/external/yc-safe-mfn/replacements.json +12 -0
  127. package/external/yc-safe-mfn/template.docx +0 -0
  128. package/external/yc-safe-pro-rata-side-letter/README.md +16 -0
  129. package/external/yc-safe-pro-rata-side-letter/clean.json +4 -0
  130. package/external/yc-safe-pro-rata-side-letter/metadata.yaml +49 -0
  131. package/external/yc-safe-pro-rata-side-letter/replacements.json +9 -0
  132. package/external/yc-safe-pro-rata-side-letter/template.docx +0 -0
  133. package/external/yc-safe-valuation-cap/README.md +16 -0
  134. package/external/yc-safe-valuation-cap/clean.json +4 -0
  135. package/external/yc-safe-valuation-cap/metadata.yaml +64 -0
  136. package/external/yc-safe-valuation-cap/replacements.json +12 -0
  137. package/external/yc-safe-valuation-cap/template.docx +0 -0
  138. package/package.json +77 -0
  139. package/recipes/nvca-certificate-of-incorporation/clean.json +8 -0
  140. package/recipes/nvca-certificate-of-incorporation/metadata.yaml +43 -0
  141. package/recipes/nvca-certificate-of-incorporation/replacements.json +9 -0
  142. package/recipes/nvca-certificate-of-incorporation/schema.json +11 -0
  143. package/recipes/nvca-indemnification-agreement/clean.json +7 -0
  144. package/recipes/nvca-indemnification-agreement/metadata.yaml +83 -0
  145. package/recipes/nvca-indemnification-agreement/replacements.json +17 -0
  146. package/recipes/nvca-indemnification-agreement/schema.json +19 -0
  147. package/recipes/nvca-investors-rights-agreement/clean.json +12 -0
  148. package/recipes/nvca-investors-rights-agreement/metadata.yaml +75 -0
  149. package/recipes/nvca-investors-rights-agreement/replacements.json +18 -0
  150. package/recipes/nvca-investors-rights-agreement/schema.json +18 -0
  151. package/recipes/nvca-management-rights-letter/clean.json +7 -0
  152. package/recipes/nvca-management-rights-letter/metadata.yaml +50 -0
  153. package/recipes/nvca-management-rights-letter/replacements.json +11 -0
  154. package/recipes/nvca-management-rights-letter/schema.json +13 -0
  155. package/recipes/nvca-rofr-co-sale-agreement/clean.json +7 -0
  156. package/recipes/nvca-rofr-co-sale-agreement/metadata.yaml +80 -0
  157. package/recipes/nvca-rofr-co-sale-agreement/replacements.json +17 -0
  158. package/recipes/nvca-rofr-co-sale-agreement/schema.json +19 -0
  159. package/recipes/nvca-stock-purchase-agreement/clean.json +10 -0
  160. package/recipes/nvca-stock-purchase-agreement/metadata.yaml +74 -0
  161. package/recipes/nvca-stock-purchase-agreement/replacements.json +20 -0
  162. package/recipes/nvca-stock-purchase-agreement/schema.json +19 -0
  163. package/recipes/nvca-voting-agreement/README.md +53 -0
  164. package/recipes/nvca-voting-agreement/clean.json +7 -0
  165. package/recipes/nvca-voting-agreement/metadata.yaml +70 -0
  166. package/recipes/nvca-voting-agreement/replacements.json +18 -0
  167. package/recipes/nvca-voting-agreement/schema.json +28 -0
  168. package/skills/open-agreements/SKILL.md +166 -0
  169. package/templates/bonterms-mutual-nda/README.md +27 -0
  170. package/templates/bonterms-mutual-nda/metadata.yaml +58 -0
  171. package/templates/bonterms-mutual-nda/template.docx +0 -0
  172. package/templates/bonterms-professional-services-agreement/README.md +24 -0
  173. package/templates/bonterms-professional-services-agreement/metadata.yaml +40 -0
  174. package/templates/bonterms-professional-services-agreement/template.docx +0 -0
  175. package/templates/common-paper-ai-addendum/README.md +23 -0
  176. package/templates/common-paper-ai-addendum/metadata.yaml +33 -0
  177. package/templates/common-paper-ai-addendum/template.docx +0 -0
  178. package/templates/common-paper-ai-addendum-in-app/README.md +21 -0
  179. package/templates/common-paper-ai-addendum-in-app/metadata.yaml +23 -0
  180. package/templates/common-paper-ai-addendum-in-app/template.docx +0 -0
  181. package/templates/common-paper-amendment/README.md +27 -0
  182. package/templates/common-paper-amendment/metadata.yaml +53 -0
  183. package/templates/common-paper-amendment/template.docx +0 -0
  184. package/templates/common-paper-business-associate-agreement/README.md +29 -0
  185. package/templates/common-paper-business-associate-agreement/metadata.yaml +63 -0
  186. package/templates/common-paper-business-associate-agreement/template.docx +0 -0
  187. package/templates/common-paper-cloud-service-agreement/README.md +32 -0
  188. package/templates/common-paper-cloud-service-agreement/metadata.yaml +488 -0
  189. package/templates/common-paper-cloud-service-agreement/template.docx +0 -0
  190. package/templates/common-paper-csa-click-through/README.md +33 -0
  191. package/templates/common-paper-csa-click-through/metadata.yaml +83 -0
  192. package/templates/common-paper-csa-click-through/template.docx +0 -0
  193. package/templates/common-paper-csa-with-ai/README.md +49 -0
  194. package/templates/common-paper-csa-with-ai/metadata.yaml +166 -0
  195. package/templates/common-paper-csa-with-ai/template.docx +0 -0
  196. package/templates/common-paper-csa-with-sla/README.md +53 -0
  197. package/templates/common-paper-csa-with-sla/metadata.yaml +185 -0
  198. package/templates/common-paper-csa-with-sla/template.docx +0 -0
  199. package/templates/common-paper-csa-without-sla/README.md +47 -0
  200. package/templates/common-paper-csa-without-sla/metadata.yaml +155 -0
  201. package/templates/common-paper-csa-without-sla/template.docx +0 -0
  202. package/templates/common-paper-data-processing-agreement/README.md +46 -0
  203. package/templates/common-paper-data-processing-agreement/metadata.yaml +149 -0
  204. package/templates/common-paper-data-processing-agreement/template.docx +0 -0
  205. package/templates/common-paper-design-partner-agreement/README.md +29 -0
  206. package/templates/common-paper-design-partner-agreement/metadata.yaml +65 -0
  207. package/templates/common-paper-design-partner-agreement/template.docx +0 -0
  208. package/templates/common-paper-independent-contractor-agreement/README.md +27 -0
  209. package/templates/common-paper-independent-contractor-agreement/metadata.yaml +55 -0
  210. package/templates/common-paper-independent-contractor-agreement/template.docx +0 -0
  211. package/templates/common-paper-letter-of-intent/README.md +25 -0
  212. package/templates/common-paper-letter-of-intent/metadata.yaml +43 -0
  213. package/templates/common-paper-letter-of-intent/template.docx +0 -0
  214. package/templates/common-paper-mutual-nda/README.md +29 -0
  215. package/templates/common-paper-mutual-nda/metadata.yaml +59 -0
  216. package/templates/common-paper-mutual-nda/template.docx +0 -0
  217. package/templates/common-paper-one-way-nda/README.md +27 -0
  218. package/templates/common-paper-one-way-nda/metadata.yaml +60 -0
  219. package/templates/common-paper-one-way-nda/template.docx +0 -0
  220. package/templates/common-paper-order-form/README.md +36 -0
  221. package/templates/common-paper-order-form/metadata.yaml +98 -0
  222. package/templates/common-paper-order-form/template.docx +0 -0
  223. package/templates/common-paper-order-form-with-sla/README.md +42 -0
  224. package/templates/common-paper-order-form-with-sla/metadata.yaml +129 -0
  225. package/templates/common-paper-order-form-with-sla/template.docx +0 -0
  226. package/templates/common-paper-partnership-agreement/README.md +34 -0
  227. package/templates/common-paper-partnership-agreement/metadata.yaml +90 -0
  228. package/templates/common-paper-partnership-agreement/template.docx +0 -0
  229. package/templates/common-paper-pilot-agreement/README.md +34 -0
  230. package/templates/common-paper-pilot-agreement/metadata.yaml +90 -0
  231. package/templates/common-paper-pilot-agreement/template.docx +0 -0
  232. package/templates/common-paper-professional-services-agreement/README.md +44 -0
  233. package/templates/common-paper-professional-services-agreement/metadata.yaml +141 -0
  234. package/templates/common-paper-professional-services-agreement/template.docx +0 -0
  235. package/templates/common-paper-software-license-agreement/README.md +18 -0
  236. package/templates/common-paper-software-license-agreement/metadata.yaml +13 -0
  237. package/templates/common-paper-software-license-agreement/template.docx +0 -0
  238. package/templates/common-paper-statement-of-work/README.md +32 -0
  239. package/templates/common-paper-statement-of-work/metadata.yaml +78 -0
  240. package/templates/common-paper-statement-of-work/template.docx +0 -0
  241. package/templates/common-paper-term-sheet/README.md +22 -0
  242. package/templates/common-paper-term-sheet/metadata.yaml +28 -0
  243. package/templates/common-paper-term-sheet/template.docx +0 -0
@@ -0,0 +1,149 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { loadMetadata } from './metadata.js';
4
+ import { prepareFillData, fillDocx } from './fill-pipeline.js';
5
+ import { verifyTemplateFill } from './fill-utils.js';
6
+ /**
7
+ * Compute display fields for radio/checkbox groups in table rows.
8
+ * docx-templates only allows one {IF} per table row, so multi-option rows
9
+ * use a single computed display tag that the engine populates from input fields.
10
+ *
11
+ * This is template-specific logic — only activates when metadata declares
12
+ * the relevant display field names.
13
+ */
14
+ function computeDisplayFields(data, fieldNames) {
15
+ const str = (key) => String(data[key] ?? '');
16
+ const bool = (key) => data[key] === true;
17
+ // Order Date: "Date of last signature" or custom date
18
+ if (fieldNames.has('order_date_display') && !data['order_date_display']) {
19
+ data['order_date_display'] = bool('order_date_is_last_signature')
20
+ ? '( x )\tDate of last signature on this Order Form'
21
+ : `( x )\t${str('custom_order_date')}`;
22
+ }
23
+ // Pilot fee: paid or free trial
24
+ if (fieldNames.has('pilot_fee_display') && !data['pilot_fee_display']) {
25
+ data['pilot_fee_display'] = bool('pilot_is_free')
26
+ ? '( x )\tFree trial'
27
+ : `( x )\tFee for Pilot Period: ${str('pilot_fee')}`;
28
+ }
29
+ // Cloud Service Fees: build from selected checkboxes
30
+ if (fieldNames.has('fees_display') && !data['fees_display']) {
31
+ const lines = [];
32
+ if (bool('fee_is_per_unit')) {
33
+ lines.push(`[ x ] ${str('fees')} per ${str('fee_unit')}`);
34
+ }
35
+ if (bool('fee_is_other')) {
36
+ lines.push(`[ x ] Other fee structure: ${str('other_fee_structure')}`);
37
+ }
38
+ if (bool('fee_may_increase')) {
39
+ lines.push(`[ x ] Fees may increase up to ${str('fee_increase_cap_pct')}% per renewal`);
40
+ }
41
+ if (bool('fee_will_increase')) {
42
+ lines.push(`[ x ] Fees will increase ${str('fee_increase_fixed_pct')}% per renewal`);
43
+ }
44
+ if (bool('fee_inclusive_of_taxes')) {
45
+ lines.push('[ x ] Fees are inclusive of taxes (modifies Standard Terms Section 4.1)');
46
+ }
47
+ data['fees_display'] = lines.join('\n');
48
+ }
49
+ // Payment Process: invoice or automatic
50
+ if (fieldNames.has('payment_display') && !data['payment_display']) {
51
+ if (bool('payment_by_invoice')) {
52
+ const freq = str('payment_frequency');
53
+ const days = str('payment_terms_days');
54
+ const from = str('payment_due_from');
55
+ data['payment_display'] =
56
+ `( x )\tPay by invoice\n` +
57
+ `Provider will invoice Customer ${freq}.\n` +
58
+ `Customer will pay each invoice within ${days} days of ${from}.`;
59
+ }
60
+ else {
61
+ const freq = str('payment_frequency');
62
+ data['payment_display'] =
63
+ `( x )\tAutomatic payment\n` +
64
+ `Customer authorizes Provider to charge the payment method on file ${freq}.`;
65
+ }
66
+ }
67
+ // Auto-renewal
68
+ if (fieldNames.has('auto_renewal_display') && !data['auto_renewal_display']) {
69
+ if (bool('auto_renew')) {
70
+ const days = str('non_renewal_notice_days');
71
+ data['auto_renewal_display'] =
72
+ `( x )\tNon-Renewal Notice Date is ${days} days before the end of the current Subscription Period.`;
73
+ }
74
+ else {
75
+ data['auto_renewal_display'] = '( x )\tThis Order does not automatically renew.';
76
+ }
77
+ }
78
+ // Effective Date
79
+ if (fieldNames.has('effective_date_display') && !data['effective_date_display']) {
80
+ data['effective_date_display'] = bool('effective_date_is_last_signature')
81
+ ? '( x )\tDate of last Cover Page signature'
82
+ : `( x )\t${str('custom_effective_date')}`;
83
+ }
84
+ // Covered Claims
85
+ if (fieldNames.has('covered_claims_display') && !data['covered_claims_display']) {
86
+ const lines = [];
87
+ if (bool('has_provider_covered_claims')) {
88
+ lines.push('[ x ] Provider Covered Claims: [Any action, proceeding, or claim that the Cloud Service, when used by Customer as permitted under the Agreement, infringes or misappropriates a third party\u2019s intellectual property rights.]');
89
+ }
90
+ if (bool('has_customer_covered_claims')) {
91
+ lines.push('[ x ] Customer Covered Claims: [Any action, proceeding, or claim that (1) the Customer Content, when used according to the Agreement, infringes or misappropriates a third party\u2019s intellectual property rights; or (2) results from the Customer\u2019s breach of Section 2.4.]');
92
+ }
93
+ data['covered_claims_display'] = lines.join('\n');
94
+ }
95
+ // General Cap Amount
96
+ if (fieldNames.has('general_cap_display') && !data['general_cap_display']) {
97
+ if (bool('general_cap_is_multiplier')) {
98
+ data['general_cap_display'] = `( x )\t${str('general_cap_multiplier')}x the Fees paid or payable by Customer in the 12 month period immediately preceding the claim`;
99
+ }
100
+ else if (bool('general_cap_is_dollar')) {
101
+ data['general_cap_display'] = `( x )\t$${str('general_cap_dollar')}`;
102
+ }
103
+ else if (bool('general_cap_is_greater_of')) {
104
+ data['general_cap_display'] = `( x )\tThe greater of $${str('general_cap_greater_dollar')} or ${str('general_cap_greater_multiplier')}x the Fees paid or payable by Customer in the 12 month period immediately preceding the claim`;
105
+ }
106
+ }
107
+ }
108
+ export async function fillTemplate(options) {
109
+ const { templateDir, values, outputPath } = options;
110
+ const metadata = loadMetadata(templateDir);
111
+ const fieldNames = new Set(metadata.fields.map((f) => f.name));
112
+ // Prepare data using shared pipeline (defaults, booleans, display fields)
113
+ const data = prepareFillData({
114
+ values,
115
+ fields: metadata.fields,
116
+ useBlankPlaceholder: false,
117
+ coerceBooleans: true,
118
+ computeDisplayFields: (d) => computeDisplayFields(d, fieldNames),
119
+ });
120
+ const templatePath = join(templateDir, 'template.docx');
121
+ const templateBuf = readFileSync(templatePath);
122
+ // Warn about unknown keys not in metadata
123
+ const unknownKeys = Object.keys(data).filter((k) => !fieldNames.has(k));
124
+ if (unknownKeys.length > 0) {
125
+ console.warn(`Warning: unknown field(s) not in metadata: ${unknownKeys.join(', ')}`);
126
+ }
127
+ // Fill document using shared pipeline (currency sanitization + createReport)
128
+ const output = await fillDocx({
129
+ templateBuffer: templateBuf,
130
+ data,
131
+ fixSmartQuotes: true,
132
+ });
133
+ writeFileSync(outputPath, output);
134
+ // Verify output — warnings only, does not throw
135
+ const verifyResult = verifyTemplateFill(outputPath);
136
+ if (!verifyResult.passed) {
137
+ const failures = verifyResult.checks
138
+ .filter((c) => !c.passed)
139
+ .map((c) => `${c.name}: ${c.details ?? 'failed'}`)
140
+ .join('; ');
141
+ console.warn(`Warning: verification issues: ${failures}`);
142
+ }
143
+ return {
144
+ outputPath,
145
+ metadata,
146
+ fieldsUsed: Object.keys(data),
147
+ };
148
+ }
149
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ExternalFillOptions, ExternalFillResult } from './types.js';
2
+ /**
3
+ * Run the external fill pipeline: integrity check → clean → patch → fill → verify.
4
+ * Uses the local DOCX file from the external/ directory (never modifies the original).
5
+ */
6
+ export declare function runExternalFill(options: ExternalFillOptions): Promise<ExternalFillResult>;
7
+ export type { ExternalFillOptions, ExternalFillResult } from './types.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/external/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE1E;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA6F/F;AAED,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,92 @@
1
+ import { mkdtempSync, copyFileSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { tmpdir } from 'node:os';
4
+ import { createHash } from 'node:crypto';
5
+ import { loadExternalMetadata, loadCleanConfig } from '../metadata.js';
6
+ import { resolveExternalDir } from '../../utils/paths.js';
7
+ import { cleanDocument } from '../recipe/cleaner.js';
8
+ import { patchDocument } from '../recipe/patcher.js';
9
+ import { verifyOutput } from '../recipe/verifier.js';
10
+ import { prepareFillData, fillDocx } from '../fill-pipeline.js';
11
+ /**
12
+ * Run the external fill pipeline: integrity check → clean → patch → fill → verify.
13
+ * Uses the local DOCX file from the external/ directory (never modifies the original).
14
+ */
15
+ export async function runExternalFill(options) {
16
+ const { externalId, outputPath, values, keepIntermediate } = options;
17
+ const externalDir = resolveExternalDir(externalId);
18
+ const metadata = loadExternalMetadata(externalDir);
19
+ // SHA-256 integrity check
20
+ const docxPath = join(externalDir, 'template.docx');
21
+ const docxBuf = readFileSync(docxPath);
22
+ const actualHash = createHash('sha256').update(docxBuf).digest('hex');
23
+ if (actualHash !== metadata.source_sha256) {
24
+ throw new Error(`Integrity check failed for "${externalId}": DOCX has been modified.\n` +
25
+ ` Expected SHA-256: ${metadata.source_sha256}\n` +
26
+ ` Actual SHA-256: ${actualHash}`);
27
+ }
28
+ // CC-BY-ND redistribution warning
29
+ console.log(`\nNote: This is an external document (${metadata.license}). ` +
30
+ `You may fill it for your own use, but do not redistribute modified versions.\n` +
31
+ `See: ${metadata.source_url}\n`);
32
+ // Load clean config and replacements
33
+ const cleanConfig = loadCleanConfig(externalDir);
34
+ const replacementsPath = join(externalDir, 'replacements.json');
35
+ const replacements = JSON.parse(readFileSync(replacementsPath, 'utf-8'));
36
+ // Create temp directory for intermediate files
37
+ const tempDir = mkdtempSync(join(tmpdir(), `external-${externalId}-`));
38
+ const stages = { clean: '', patch: '', fill: '' };
39
+ try {
40
+ // Copy source to temp dir
41
+ const sourcePath = join(tempDir, 'source.docx');
42
+ copyFileSync(docxPath, sourcePath);
43
+ // Stage 1: Clean
44
+ const cleanedPath = join(tempDir, 'cleaned.docx');
45
+ await cleanDocument(sourcePath, cleanedPath, cleanConfig);
46
+ stages.clean = cleanedPath;
47
+ // Stage 2: Patch (replace [brackets] with {template_tags})
48
+ const patchedPath = join(tempDir, 'patched.docx');
49
+ await patchDocument(cleanedPath, patchedPath, replacements);
50
+ stages.patch = patchedPath;
51
+ // Stage 3: Fill (render template tags with values)
52
+ const fillData = prepareFillData({
53
+ values,
54
+ fields: metadata.fields,
55
+ useBlankPlaceholder: true,
56
+ });
57
+ const filledPath = join(tempDir, 'filled.docx');
58
+ const templateBuf = readFileSync(patchedPath);
59
+ const filledBuf = await fillDocx({
60
+ templateBuffer: templateBuf,
61
+ data: fillData,
62
+ });
63
+ writeFileSync(filledPath, filledBuf);
64
+ stages.fill = filledPath;
65
+ // Stage 4: Verify
66
+ const verifyResult = await verifyOutput(filledPath, values, replacements, cleanConfig);
67
+ if (!verifyResult.passed) {
68
+ const failures = verifyResult.checks
69
+ .filter((c) => !c.passed)
70
+ .map((c) => `${c.name}: ${c.details ?? 'failed'}`)
71
+ .join('; ');
72
+ console.warn(`Warning: verification issues: ${failures}`);
73
+ }
74
+ // Copy final output to destination
75
+ copyFileSync(filledPath, outputPath);
76
+ if (keepIntermediate) {
77
+ console.log(`Intermediate files preserved at: ${tempDir}`);
78
+ }
79
+ return {
80
+ outputPath,
81
+ metadata,
82
+ fieldsUsed: Object.keys(values),
83
+ stages,
84
+ };
85
+ }
86
+ finally {
87
+ if (!keepIntermediate) {
88
+ rmSync(tempDir, { recursive: true, force: true });
89
+ }
90
+ }
91
+ }
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/external/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { ExternalMetadata } from '../metadata.js';
2
+ export interface ExternalFillOptions {
3
+ externalId: string;
4
+ outputPath: string;
5
+ values: Record<string, string>;
6
+ keepIntermediate?: boolean;
7
+ }
8
+ export interface ExternalFillResult {
9
+ outputPath: string;
10
+ metadata: ExternalMetadata;
11
+ fieldsUsed: string[];
12
+ stages: {
13
+ clean: string;
14
+ patch: string;
15
+ fill: string;
16
+ };
17
+ }
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/external/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/external/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Shared fill pipeline used by all three fill paths (template, external, recipe).
3
+ * Centralizes: defaults, boolean coercion, display fields, currency sanitization,
4
+ * drafting note removal, and the docx-templates createReport() call.
5
+ */
6
+ import type { FieldDefinition } from './metadata.js';
7
+ export interface PrepareFillDataOptions {
8
+ /** User-provided values. */
9
+ values: Record<string, string | boolean>;
10
+ /** Field definitions from metadata. */
11
+ fields: FieldDefinition[];
12
+ /**
13
+ * When true, unfilled optional fields default to BLANK_PLACEHOLDER ('_______')
14
+ * so omissions are visible. When false, they default to '' (empty string).
15
+ * Recipes and external use true; templates use false.
16
+ */
17
+ useBlankPlaceholder?: boolean;
18
+ /** Coerce boolean-typed fields to actual JS booleans for IF conditions. */
19
+ coerceBooleans?: boolean;
20
+ /**
21
+ * Optional callback for computing display fields (template-specific).
22
+ * Called after defaults and boolean coercion are applied.
23
+ */
24
+ computeDisplayFields?: (data: Record<string, string | boolean>) => void;
25
+ }
26
+ export interface FillDocxOptions {
27
+ /** Template DOCX buffer (already patched with {tags}). */
28
+ templateBuffer: Buffer;
29
+ /** Prepared fill data from prepareFillData(). */
30
+ data: Record<string, string | boolean>;
31
+ /** Apply docx-templates smart quote normalization. */
32
+ fixSmartQuotes?: boolean;
33
+ /**
34
+ * Regex patterns for paragraphs to remove before filling.
35
+ * Paragraphs whose text matches any pattern are stripped from the DOCX.
36
+ * If a matched paragraph is the only content in a table row, the entire
37
+ * row is removed to avoid empty highlighted rows.
38
+ *
39
+ * Default: `[/\bDrafting note\b/i]` — removes Common Paper drafting notes.
40
+ * Pass `[]` to disable.
41
+ */
42
+ stripParagraphPatterns?: RegExp[];
43
+ }
44
+ /**
45
+ * Prepare fill data with all normalization steps:
46
+ * 1. Validate required fields are present
47
+ * 2. Apply defaults for optional fields not provided
48
+ * 3. Coerce boolean fields (optional)
49
+ * 4. Compute display fields (optional, template-specific)
50
+ */
51
+ export declare function prepareFillData(options: PrepareFillDataOptions): Record<string, string | boolean>;
52
+ /**
53
+ * Fill a DOCX template with prepared data:
54
+ * 1. Strip drafting note paragraphs (configurable, on by default)
55
+ * 2. Strip highlighting from runs with filled fields (unfilled keep their highlight)
56
+ * 3. Sanitize currency values by scanning the template buffer for ${field} patterns
57
+ * 4. Call docx-templates createReport() with standard delimiters
58
+ * 5. Return the filled buffer
59
+ */
60
+ export declare function fillDocx(options: FillDocxOptions): Promise<Uint8Array>;
61
+ //# sourceMappingURL=fill-pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fill-pipeline.d.ts","sourceRoot":"","sources":["../../src/core/fill-pipeline.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIrD,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAEzC,uCAAuC;IACvC,MAAM,EAAE,eAAe,EAAE,CAAC;IAE1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC;CACzE;AAED,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,cAAc,EAAE,MAAM,CAAC;IAEvB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAEvC,sDAAsD;IACtD,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AAKD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CA2CjG;AAkND;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAmC5E"}
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Shared fill pipeline used by all three fill paths (template, external, recipe).
3
+ * Centralizes: defaults, boolean coercion, display fields, currency sanitization,
4
+ * drafting note removal, and the docx-templates createReport() call.
5
+ */
6
+ import AdmZip from 'adm-zip';
7
+ import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
8
+ import { createReport } from 'docx-templates';
9
+ import { sanitizeCurrencyValuesFromDocx, BLANK_PLACEHOLDER } from './fill-utils.js';
10
+ import { enumerateTextParts, getGeneralTextPartNames } from './recipe/ooxml-parts.js';
11
+ const W_NS = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
12
+ /** Default patterns for paragraphs stripped before filling. */
13
+ const DEFAULT_STRIP_PATTERNS = [/\bDrafting note\b/i];
14
+ /**
15
+ * Prepare fill data with all normalization steps:
16
+ * 1. Validate required fields are present
17
+ * 2. Apply defaults for optional fields not provided
18
+ * 3. Coerce boolean fields (optional)
19
+ * 4. Compute display fields (optional, template-specific)
20
+ */
21
+ export function prepareFillData(options) {
22
+ const { values, fields, useBlankPlaceholder = false, coerceBooleans = false, computeDisplayFields, } = options;
23
+ // Validate required fields
24
+ const missing = fields
25
+ .filter((f) => f.required && !(f.name in values))
26
+ .map((f) => f.name);
27
+ if (missing.length > 0) {
28
+ throw new Error(`Missing required fields: ${missing.join(', ')}`);
29
+ }
30
+ // Apply defaults for optional fields
31
+ const defaultValue = useBlankPlaceholder ? BLANK_PLACEHOLDER : '';
32
+ const data = { ...values };
33
+ for (const field of fields) {
34
+ if (!(field.name in data)) {
35
+ data[field.name] = field.default ?? defaultValue;
36
+ }
37
+ }
38
+ // Coerce boolean fields to actual JS booleans.
39
+ // docx-templates evaluates {IF field} in JS context where "false" is truthy.
40
+ if (coerceBooleans) {
41
+ for (const field of fields) {
42
+ if (field.type === 'boolean' && field.name in data) {
43
+ const v = data[field.name];
44
+ data[field.name] = v === true || v === 'true';
45
+ }
46
+ }
47
+ }
48
+ // Compute display fields (template-specific: radio/checkbox groups)
49
+ if (computeDisplayFields) {
50
+ computeDisplayFields(data);
51
+ }
52
+ return data;
53
+ }
54
+ /* eslint-disable @typescript-eslint/no-explicit-any --
55
+ @xmldom/xmldom types are incompatible with global DOM types */
56
+ /**
57
+ * Extract paragraph text by concatenating all <w:t> elements.
58
+ */
59
+ function extractParagraphText(para) {
60
+ if (!para.getElementsByTagNameNS)
61
+ return '';
62
+ const tElements = para.getElementsByTagNameNS(W_NS, 't');
63
+ const parts = [];
64
+ for (let i = 0; i < tElements.length; i++) {
65
+ parts.push(tElements[i].textContent ?? '');
66
+ }
67
+ return parts.join('').trim();
68
+ }
69
+ /**
70
+ * Check if a table row contains ONLY paragraphs that match the strip patterns
71
+ * (or are empty). If so, the entire row can be safely removed.
72
+ */
73
+ function isRowOnlyDraftingNotes(tr, patterns) {
74
+ const paras = tr.getElementsByTagNameNS(W_NS, 'p');
75
+ if (paras.length === 0)
76
+ return false;
77
+ for (let i = 0; i < paras.length; i++) {
78
+ const text = extractParagraphText(paras[i]);
79
+ if (text === '')
80
+ continue; // empty paragraphs are fine to remove
81
+ if (!patterns.some((r) => r.test(text)))
82
+ return false; // non-matching content
83
+ }
84
+ return true;
85
+ }
86
+ /**
87
+ * Remove paragraphs matching the given patterns from a DOCX buffer.
88
+ * If a matched paragraph is the sole content of a table row, the entire
89
+ * row is removed (avoids empty highlighted rows in Common Paper docs).
90
+ *
91
+ * Returns a new DOCX buffer with the paragraphs removed.
92
+ */
93
+ function stripParagraphs(docxBuffer, patterns) {
94
+ if (patterns.length === 0)
95
+ return docxBuffer;
96
+ const zip = new AdmZip(docxBuffer);
97
+ const parser = new DOMParser();
98
+ const serializer = new XMLSerializer();
99
+ const parts = enumerateTextParts(zip);
100
+ const partNames = getGeneralTextPartNames(parts);
101
+ let modified = false;
102
+ for (const partName of partNames) {
103
+ const entry = zip.getEntry(partName);
104
+ if (!entry)
105
+ continue;
106
+ const xml = entry.getData().toString('utf-8');
107
+ const doc = parser.parseFromString(xml, 'text/xml');
108
+ // Collect table rows to remove (whole row when only drafting notes)
109
+ const rowsToRemove = new Set();
110
+ // Collect standalone paragraphs to remove
111
+ const parasToRemove = [];
112
+ const allParagraphs = doc.getElementsByTagNameNS(W_NS, 'p');
113
+ for (let i = 0; i < allParagraphs.length; i++) {
114
+ const para = allParagraphs[i];
115
+ const text = extractParagraphText(para);
116
+ if (!text || !patterns.some((r) => r.test(text)))
117
+ continue;
118
+ // Walk up to find if this is inside a table cell
119
+ let node = para.parentNode;
120
+ let inTableCell = false;
121
+ let tableRow = null;
122
+ while (node) {
123
+ if (node.localName === 'tc' && node.namespaceURI === W_NS) {
124
+ inTableCell = true;
125
+ }
126
+ if (node.localName === 'tr' && node.namespaceURI === W_NS) {
127
+ tableRow = node;
128
+ break;
129
+ }
130
+ node = node.parentNode;
131
+ }
132
+ if (inTableCell && tableRow && isRowOnlyDraftingNotes(tableRow, patterns)) {
133
+ rowsToRemove.add(tableRow);
134
+ }
135
+ else {
136
+ parasToRemove.push(para);
137
+ }
138
+ }
139
+ if (rowsToRemove.size > 0 || parasToRemove.length > 0) {
140
+ modified = true;
141
+ for (const row of rowsToRemove) {
142
+ row.parentNode?.removeChild(row);
143
+ }
144
+ for (const para of parasToRemove) {
145
+ para.parentNode?.removeChild(para);
146
+ }
147
+ // Update the zip entry
148
+ const outXml = serializer.serializeToString(doc);
149
+ zip.updateFile(partName, Buffer.from(outXml, 'utf-8'));
150
+ }
151
+ }
152
+ if (!modified)
153
+ return docxBuffer;
154
+ // Rebuild the zip from scratch (adm-zip data descriptor workaround)
155
+ const outZip = new AdmZip();
156
+ for (const entry of zip.getEntries()) {
157
+ outZip.addFile(entry.entryName, entry.getData());
158
+ }
159
+ return outZip.toBuffer();
160
+ }
161
+ /**
162
+ * Extract all text from a run (<w:r>) by concatenating its <w:t> elements.
163
+ */
164
+ function extractRunText(run) {
165
+ const tElements = run.getElementsByTagNameNS(W_NS, 't');
166
+ const parts = [];
167
+ for (let i = 0; i < tElements.length; i++) {
168
+ parts.push(tElements[i].textContent ?? '');
169
+ }
170
+ return parts.join('');
171
+ }
172
+ /**
173
+ * Remove <w:highlight> elements only from runs whose template tags
174
+ * are being filled with non-empty values. Unfilled fields keep their
175
+ * yellow highlighting so users can see what still needs attention.
176
+ *
177
+ * Works by finding runs containing {field_name} tags, checking whether
178
+ * the corresponding field has a non-empty value in the data, and only
179
+ * stripping the highlight if it does.
180
+ */
181
+ function stripFilledHighlighting(docxBuffer, filledFields) {
182
+ if (filledFields.size === 0)
183
+ return docxBuffer;
184
+ const zip = new AdmZip(docxBuffer);
185
+ const parser = new DOMParser();
186
+ const serializer = new XMLSerializer();
187
+ const parts = enumerateTextParts(zip);
188
+ const partNames = getGeneralTextPartNames(parts);
189
+ // Match {field_name} but not {IF ...} or {END-IF ...} etc.
190
+ const tagPattern = /\{(\w+)\}/g;
191
+ let modified = false;
192
+ for (const partName of partNames) {
193
+ const entry = zip.getEntry(partName);
194
+ if (!entry)
195
+ continue;
196
+ const xml = entry.getData().toString('utf-8');
197
+ const doc = parser.parseFromString(xml, 'text/xml');
198
+ const allRuns = doc.getElementsByTagNameNS(W_NS, 'r');
199
+ const toRemove = [];
200
+ for (let i = 0; i < allRuns.length; i++) {
201
+ const run = allRuns[i];
202
+ const rPr = run.getElementsByTagNameNS(W_NS, 'rPr');
203
+ if (rPr.length === 0)
204
+ continue;
205
+ const highlights = rPr[0].getElementsByTagNameNS(W_NS, 'highlight');
206
+ if (highlights.length === 0)
207
+ continue;
208
+ // Check if this run's text contains a tag for a filled field
209
+ const runText = extractRunText(run);
210
+ let hasFilled = false;
211
+ let match;
212
+ tagPattern.lastIndex = 0;
213
+ while ((match = tagPattern.exec(runText)) !== null) {
214
+ if (filledFields.has(match[1])) {
215
+ hasFilled = true;
216
+ break;
217
+ }
218
+ }
219
+ if (hasFilled) {
220
+ for (let j = 0; j < highlights.length; j++) {
221
+ toRemove.push(highlights[j]);
222
+ }
223
+ }
224
+ }
225
+ if (toRemove.length > 0) {
226
+ modified = true;
227
+ for (const el of toRemove) {
228
+ el.parentNode?.removeChild(el);
229
+ }
230
+ const outXml = serializer.serializeToString(doc);
231
+ zip.updateFile(partName, Buffer.from(outXml, 'utf-8'));
232
+ }
233
+ }
234
+ if (!modified)
235
+ return docxBuffer;
236
+ const outZip = new AdmZip();
237
+ for (const entry of zip.getEntries()) {
238
+ outZip.addFile(entry.entryName, entry.getData());
239
+ }
240
+ return outZip.toBuffer();
241
+ }
242
+ /**
243
+ * Fill a DOCX template with prepared data:
244
+ * 1. Strip drafting note paragraphs (configurable, on by default)
245
+ * 2. Strip highlighting from runs with filled fields (unfilled keep their highlight)
246
+ * 3. Sanitize currency values by scanning the template buffer for ${field} patterns
247
+ * 4. Call docx-templates createReport() with standard delimiters
248
+ * 5. Return the filled buffer
249
+ */
250
+ export async function fillDocx(options) {
251
+ const { data, fixSmartQuotes = false, stripParagraphPatterns = DEFAULT_STRIP_PATTERNS, } = options;
252
+ let { templateBuffer } = options;
253
+ // Step 1: Strip drafting notes (and other configured patterns)
254
+ if (stripParagraphPatterns.length > 0) {
255
+ templateBuffer = stripParagraphs(templateBuffer, stripParagraphPatterns);
256
+ }
257
+ // Step 2: Strip highlighting only from fields that have non-empty values.
258
+ // Unfilled fields keep their yellow highlight as a visual cue.
259
+ const filledFields = new Set();
260
+ for (const [key, val] of Object.entries(data)) {
261
+ if (typeof val === 'string' && val !== '' && val !== BLANK_PLACEHOLDER) {
262
+ filledFields.add(key);
263
+ }
264
+ else if (typeof val === 'boolean') {
265
+ filledFields.add(key); // booleans are always "filled"
266
+ }
267
+ }
268
+ templateBuffer = stripFilledHighlighting(templateBuffer, filledFields);
269
+ // Step 3: Strip leading $ from values where the template has ${ before the tag
270
+ const sanitizedData = sanitizeCurrencyValuesFromDocx(data, templateBuffer);
271
+ // Step 4: Fill template
272
+ return createReport({
273
+ template: templateBuffer,
274
+ data: sanitizedData,
275
+ cmdDelimiter: ['{', '}'],
276
+ fixSmartQuotes,
277
+ });
278
+ }
279
+ //# sourceMappingURL=fill-pipeline.js.map