@unlev/exeq 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -95,19 +95,39 @@ declare function DesignerView({ apiKey, initialPdfUrl, initialTemplate, onSave,
95
95
 
96
96
  type TransformFn = (value: string) => string;
97
97
  type TransformMap = Record<string, TransformFn>;
98
+ /**
99
+ * Parse a date string robustly into a local Date (midnight, local TZ) so that
100
+ * getMonth/getDate/getFullYear are calendar-correct. Handles:
101
+ * - ISO date-only "YYYY-MM-DD" → parsed as LOCAL (avoids the classic UTC
102
+ * off-by-one where "2024-03-15" becomes the 14th in western timezones).
103
+ * - Excel serial numbers (a bare integer, e.g. "45000") → converted.
104
+ * - Anything else → native `new Date` (handles "M/D/YYYY", ISO datetimes, …).
105
+ * Returns null when unparseable.
106
+ */
107
+ declare function parseDate(value: string): Date | null;
98
108
  /** Built-in transforms that ship with exeq. */
99
109
  declare const BUILTIN_TRANSFORMS: TransformMap;
100
110
  /**
101
111
  * Resolve a single formula string against a set of fields and transforms.
102
112
  * Formula format: "{{Source Field Label | transform}}" or "{{Source Field Label}}" (no transform = raw value).
103
113
  * Multiple expressions can appear in one formula: "{{First}} {{Last}}" → "Jane Smith"
114
+ *
115
+ * Source resolution order for "{{Name}}":
116
+ * 1. a placed field whose label (case-insensitive) or id matches Name
117
+ * 2. `values[Name]` — imported/contextual data not tied to a placed field
118
+ * 3. the built-in `today` / `now` source (current date)
119
+ *
120
+ * @param values Extra named values (e.g. imported CSV/form data) made available
121
+ * to formula sources even when there is no placed field for them.
104
122
  */
105
- declare function resolveFormula(formula: string, fields: FormField[], customTransforms?: TransformMap): string;
123
+ declare function resolveFormula(formula: string, fields: FormField[], customTransforms?: TransformMap, values?: Record<string, string>): string;
106
124
  /**
107
125
  * Resolve all formula fields in a fields array.
108
126
  * Returns a new array with computed values filled in.
127
+ *
128
+ * @param values Extra named values available to formula sources (see resolveFormula).
109
129
  */
110
- declare function resolveAllFormulas(fields: FormField[], customTransforms?: TransformMap): FormField[];
130
+ declare function resolveAllFormulas(fields: FormField[], customTransforms?: TransformMap, values?: Record<string, string>): FormField[];
111
131
 
112
132
  interface SignerViewProps {
113
133
  /** API key for authentication */
@@ -283,6 +303,10 @@ interface FillPdfOptions {
283
303
  /** Custom transforms merged with `BUILTIN_TRANSFORMS` during formula
284
304
  * resolution. Only meaningful when `resolveFormulas` is true. */
285
305
  customTransforms?: TransformMap;
306
+ /** Extra named values made available to formula sources (e.g. imported CSV/
307
+ * form data) even when there is no placed field for them. Only meaningful
308
+ * when `resolveFormulas` is true. */
309
+ formulaValues?: Record<string, string>;
286
310
  /** Linear calibration applied to field positions before rendering. */
287
311
  calibration?: Calibration;
288
312
  }
@@ -335,4 +359,4 @@ declare function createPdfBuilder(defaults?: CreatePdfBuilderOptions): Promise<P
335
359
  declare function downloadPdf(bytes: Uint8Array, filename: string): void;
336
360
  declare function postPdfToCallback(bytes: Uint8Array, callbackUrl: string, filename: string): Promise<void>;
337
361
 
338
- export { A4, BUILTIN_TRANSFORMS, type Calibration, type CreatePdfBuilderOptions, DEFAULT_CALIBRATION, DEFAULT_SIGNER_ROLES, DesignerView, type DesignerViewProps, FIELD_DEFAULTS, FONT_FAMILIES, FieldNavigator, FieldPropertyPanel, type FieldType, type FillPdfOptions, type FormField, type PdfBuilder, PdfViewer, type RenderedPage, SIGNER_ROLE_COLORS, SignatureCanvas, SignerRoleSelector, SignerView, type SignerViewProps, type Template, type TextSubtype, type TransformFn, type TransformMap, US_LEGAL, US_LETTER, applyCalibration, createField, createPdfBuilder, downloadPdf, generateFilledPdf, generateId, getCssFontFamily, getFieldValues, getInputType, getSignerColor, isRedactField, isSignatureField, isTextLikeField, postPdfToCallback, preserveFieldValues, renderPdfPages, resolveAllFormulas, resolveFormula, sortFieldsByPosition, uniqueLabel };
362
+ export { A4, BUILTIN_TRANSFORMS, type Calibration, type CreatePdfBuilderOptions, DEFAULT_CALIBRATION, DEFAULT_SIGNER_ROLES, DesignerView, type DesignerViewProps, FIELD_DEFAULTS, FONT_FAMILIES, FieldNavigator, FieldPropertyPanel, type FieldType, type FillPdfOptions, type FormField, type PdfBuilder, PdfViewer, type RenderedPage, SIGNER_ROLE_COLORS, SignatureCanvas, SignerRoleSelector, SignerView, type SignerViewProps, type Template, type TextSubtype, type TransformFn, type TransformMap, US_LEGAL, US_LETTER, applyCalibration, createField, createPdfBuilder, downloadPdf, generateFilledPdf, generateId, getCssFontFamily, getFieldValues, getInputType, getSignerColor, isRedactField, isSignatureField, isTextLikeField, parseDate, postPdfToCallback, preserveFieldValues, renderPdfPages, resolveAllFormulas, resolveFormula, sortFieldsByPosition, uniqueLabel };
package/dist/index.d.ts CHANGED
@@ -95,19 +95,39 @@ declare function DesignerView({ apiKey, initialPdfUrl, initialTemplate, onSave,
95
95
 
96
96
  type TransformFn = (value: string) => string;
97
97
  type TransformMap = Record<string, TransformFn>;
98
+ /**
99
+ * Parse a date string robustly into a local Date (midnight, local TZ) so that
100
+ * getMonth/getDate/getFullYear are calendar-correct. Handles:
101
+ * - ISO date-only "YYYY-MM-DD" → parsed as LOCAL (avoids the classic UTC
102
+ * off-by-one where "2024-03-15" becomes the 14th in western timezones).
103
+ * - Excel serial numbers (a bare integer, e.g. "45000") → converted.
104
+ * - Anything else → native `new Date` (handles "M/D/YYYY", ISO datetimes, …).
105
+ * Returns null when unparseable.
106
+ */
107
+ declare function parseDate(value: string): Date | null;
98
108
  /** Built-in transforms that ship with exeq. */
99
109
  declare const BUILTIN_TRANSFORMS: TransformMap;
100
110
  /**
101
111
  * Resolve a single formula string against a set of fields and transforms.
102
112
  * Formula format: "{{Source Field Label | transform}}" or "{{Source Field Label}}" (no transform = raw value).
103
113
  * Multiple expressions can appear in one formula: "{{First}} {{Last}}" → "Jane Smith"
114
+ *
115
+ * Source resolution order for "{{Name}}":
116
+ * 1. a placed field whose label (case-insensitive) or id matches Name
117
+ * 2. `values[Name]` — imported/contextual data not tied to a placed field
118
+ * 3. the built-in `today` / `now` source (current date)
119
+ *
120
+ * @param values Extra named values (e.g. imported CSV/form data) made available
121
+ * to formula sources even when there is no placed field for them.
104
122
  */
105
- declare function resolveFormula(formula: string, fields: FormField[], customTransforms?: TransformMap): string;
123
+ declare function resolveFormula(formula: string, fields: FormField[], customTransforms?: TransformMap, values?: Record<string, string>): string;
106
124
  /**
107
125
  * Resolve all formula fields in a fields array.
108
126
  * Returns a new array with computed values filled in.
127
+ *
128
+ * @param values Extra named values available to formula sources (see resolveFormula).
109
129
  */
110
- declare function resolveAllFormulas(fields: FormField[], customTransforms?: TransformMap): FormField[];
130
+ declare function resolveAllFormulas(fields: FormField[], customTransforms?: TransformMap, values?: Record<string, string>): FormField[];
111
131
 
112
132
  interface SignerViewProps {
113
133
  /** API key for authentication */
@@ -283,6 +303,10 @@ interface FillPdfOptions {
283
303
  /** Custom transforms merged with `BUILTIN_TRANSFORMS` during formula
284
304
  * resolution. Only meaningful when `resolveFormulas` is true. */
285
305
  customTransforms?: TransformMap;
306
+ /** Extra named values made available to formula sources (e.g. imported CSV/
307
+ * form data) even when there is no placed field for them. Only meaningful
308
+ * when `resolveFormulas` is true. */
309
+ formulaValues?: Record<string, string>;
286
310
  /** Linear calibration applied to field positions before rendering. */
287
311
  calibration?: Calibration;
288
312
  }
@@ -335,4 +359,4 @@ declare function createPdfBuilder(defaults?: CreatePdfBuilderOptions): Promise<P
335
359
  declare function downloadPdf(bytes: Uint8Array, filename: string): void;
336
360
  declare function postPdfToCallback(bytes: Uint8Array, callbackUrl: string, filename: string): Promise<void>;
337
361
 
338
- export { A4, BUILTIN_TRANSFORMS, type Calibration, type CreatePdfBuilderOptions, DEFAULT_CALIBRATION, DEFAULT_SIGNER_ROLES, DesignerView, type DesignerViewProps, FIELD_DEFAULTS, FONT_FAMILIES, FieldNavigator, FieldPropertyPanel, type FieldType, type FillPdfOptions, type FormField, type PdfBuilder, PdfViewer, type RenderedPage, SIGNER_ROLE_COLORS, SignatureCanvas, SignerRoleSelector, SignerView, type SignerViewProps, type Template, type TextSubtype, type TransformFn, type TransformMap, US_LEGAL, US_LETTER, applyCalibration, createField, createPdfBuilder, downloadPdf, generateFilledPdf, generateId, getCssFontFamily, getFieldValues, getInputType, getSignerColor, isRedactField, isSignatureField, isTextLikeField, postPdfToCallback, preserveFieldValues, renderPdfPages, resolveAllFormulas, resolveFormula, sortFieldsByPosition, uniqueLabel };
362
+ export { A4, BUILTIN_TRANSFORMS, type Calibration, type CreatePdfBuilderOptions, DEFAULT_CALIBRATION, DEFAULT_SIGNER_ROLES, DesignerView, type DesignerViewProps, FIELD_DEFAULTS, FONT_FAMILIES, FieldNavigator, FieldPropertyPanel, type FieldType, type FillPdfOptions, type FormField, type PdfBuilder, PdfViewer, type RenderedPage, SIGNER_ROLE_COLORS, SignatureCanvas, SignerRoleSelector, SignerView, type SignerViewProps, type Template, type TextSubtype, type TransformFn, type TransformMap, US_LEGAL, US_LETTER, applyCalibration, createField, createPdfBuilder, downloadPdf, generateFilledPdf, generateId, getCssFontFamily, getFieldValues, getInputType, getSignerColor, isRedactField, isSignatureField, isTextLikeField, parseDate, postPdfToCallback, preserveFieldValues, renderPdfPages, resolveAllFormulas, resolveFormula, sortFieldsByPosition, uniqueLabel };
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ __export(lib_exports, {
59
59
  isRedactField: () => isRedactField,
60
60
  isSignatureField: () => isSignatureField,
61
61
  isTextLikeField: () => isTextLikeField,
62
+ parseDate: () => parseDate,
62
63
  postPdfToCallback: () => postPdfToCallback,
63
64
  preserveFieldValues: () => preserveFieldValues,
64
65
  renderPdfPages: () => renderPdfPages,
@@ -2160,39 +2161,56 @@ var import_react7 = require("react");
2160
2161
  var import_pdf_lib = require("pdf-lib");
2161
2162
 
2162
2163
  // src/utils/formulaResolver.ts
2164
+ function parseDate(value) {
2165
+ const s = value.trim();
2166
+ if (!s) return null;
2167
+ if (/^\d+(\.\d+)?$/.test(s)) {
2168
+ const serial = parseFloat(s);
2169
+ if (serial > 59) {
2170
+ const utc = new Date(Math.round((serial - 25569) * 864e5));
2171
+ if (!isNaN(utc.getTime())) {
2172
+ return new Date(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
2173
+ }
2174
+ }
2175
+ }
2176
+ const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
2177
+ if (iso) return new Date(+iso[1], +iso[2] - 1, +iso[3]);
2178
+ const d = new Date(s);
2179
+ return isNaN(d.getTime()) ? null : d;
2180
+ }
2163
2181
  var BUILTIN_TRANSFORMS = {
2164
2182
  // Date transforms (expects a parseable date string)
2165
2183
  month: (v) => {
2166
- const d = new Date(v);
2167
- return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
2184
+ const d = parseDate(v);
2185
+ return d ? String(d.getMonth() + 1) : "";
2168
2186
  },
2169
2187
  month2: (v) => {
2170
- const d = new Date(v);
2171
- return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
2188
+ const d = parseDate(v);
2189
+ return d ? String(d.getMonth() + 1).padStart(2, "0") : "";
2172
2190
  },
2173
2191
  monthname: (v) => {
2174
- const d = new Date(v);
2175
- return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
2192
+ const d = parseDate(v);
2193
+ return d ? d.toLocaleString("en", { month: "long" }) : "";
2176
2194
  },
2177
2195
  monthshort: (v) => {
2178
- const d = new Date(v);
2179
- return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
2196
+ const d = parseDate(v);
2197
+ return d ? d.toLocaleString("en", { month: "short" }) : "";
2180
2198
  },
2181
2199
  day: (v) => {
2182
- const d = new Date(v);
2183
- return isNaN(d.getTime()) ? "" : String(d.getDate());
2200
+ const d = parseDate(v);
2201
+ return d ? String(d.getDate()) : "";
2184
2202
  },
2185
2203
  day2: (v) => {
2186
- const d = new Date(v);
2187
- return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
2204
+ const d = parseDate(v);
2205
+ return d ? String(d.getDate()).padStart(2, "0") : "";
2188
2206
  },
2189
2207
  year: (v) => {
2190
- const d = new Date(v);
2191
- return isNaN(d.getTime()) ? "" : String(d.getFullYear());
2208
+ const d = parseDate(v);
2209
+ return d ? String(d.getFullYear()) : "";
2192
2210
  },
2193
2211
  year2: (v) => {
2194
- const d = new Date(v);
2195
- return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
2212
+ const d = parseDate(v);
2213
+ return d ? String(d.getFullYear()).slice(-2) : "";
2196
2214
  },
2197
2215
  // String transforms
2198
2216
  upper: (v) => v.toUpperCase(),
@@ -2221,15 +2239,27 @@ var BUILTIN_TRANSFORMS = {
2221
2239
  };
2222
2240
  var FORMULA_RE = /\{\{(.+?)\}\}/g;
2223
2241
  var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
2224
- function resolveFormula(formula, fields, customTransforms) {
2242
+ function todayIso() {
2243
+ const d = /* @__PURE__ */ new Date();
2244
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
2245
+ }
2246
+ function resolveFormula(formula, fields, customTransforms, values) {
2225
2247
  const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
2226
2248
  return formula.replace(FORMULA_RE, (_match, expr) => {
2227
2249
  const pipeMatch = expr.match(PIPE_RE);
2228
2250
  const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
2229
2251
  const transformName = pipeMatch ? pipeMatch[2].trim() : null;
2230
- const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
2231
- if (!sourceField) return "";
2232
- const rawValue = sourceField.value || "";
2252
+ const lowerLabel = sourceLabel.toLowerCase();
2253
+ const sourceField = fields.find((f) => f.label.toLowerCase() === lowerLabel) || fields.find((f) => f.id === sourceLabel);
2254
+ let rawValue = sourceField ? sourceField.value || "" : void 0;
2255
+ if (rawValue === void 0 && values) {
2256
+ const key = Object.keys(values).find((k) => k.toLowerCase() === lowerLabel);
2257
+ if (key !== void 0) rawValue = values[key] || "";
2258
+ }
2259
+ if (rawValue === void 0 && (lowerLabel === "today" || lowerLabel === "now")) {
2260
+ rawValue = todayIso();
2261
+ }
2262
+ if (rawValue === void 0) return "";
2233
2263
  if (!transformName) return rawValue;
2234
2264
  const fn = transforms[transformName];
2235
2265
  if (!fn) return rawValue;
@@ -2240,10 +2270,10 @@ function resolveFormula(formula, fields, customTransforms) {
2240
2270
  }
2241
2271
  });
2242
2272
  }
2243
- function resolveAllFormulas(fields, customTransforms) {
2273
+ function resolveAllFormulas(fields, customTransforms, values) {
2244
2274
  return fields.map((f) => {
2245
2275
  if (!f.formula) return f;
2246
- const computed = resolveFormula(f.formula, fields, customTransforms);
2276
+ const computed = resolveFormula(f.formula, fields, customTransforms, values);
2247
2277
  return computed !== f.value ? { ...f, value: computed } : f;
2248
2278
  });
2249
2279
  }
@@ -2450,7 +2480,7 @@ async function createPdfBuilder(defaults = {}) {
2450
2480
  doc,
2451
2481
  async addRecord(opts) {
2452
2482
  const pageSize = opts.pageSize ?? defaults.pageSize;
2453
- let fields = opts.resolveFormulas ? resolveAllFormulas(opts.fields, opts.customTransforms) : opts.fields;
2483
+ let fields = opts.resolveFormulas ? resolveAllFormulas(opts.fields, opts.customTransforms, opts.formulaValues) : opts.fields;
2454
2484
  if (opts.calibration) {
2455
2485
  fields = applyCalibration(
2456
2486
  fields,
@@ -3173,6 +3203,7 @@ function SignerRoleSelector({
3173
3203
  isRedactField,
3174
3204
  isSignatureField,
3175
3205
  isTextLikeField,
3206
+ parseDate,
3176
3207
  postPdfToCallback,
3177
3208
  preserveFieldValues,
3178
3209
  renderPdfPages,