@pixldocs/canvas-renderer 0.5.218 → 0.5.220

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.
@@ -13644,7 +13644,9 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
13644
13644
  function convert(defs, parentId) {
13645
13645
  var _a, _b;
13646
13646
  for (const def of defs) {
13647
- if (def.repeatable) {
13647
+ const isRepeatable = def.repeatable === true || def.type === "repeatable";
13648
+ const defFields = def.fields ?? def.entryFields ?? [];
13649
+ if (isRepeatable) {
13648
13650
  const rawPrefix = def.templateKeyPrefix || def.label.toLowerCase().replace(/\s+/g, "_");
13649
13651
  const labelPrefix = rawPrefix.startsWith("field_") ? rawPrefix : `field_${rawPrefix}`;
13650
13652
  const treeNodeId = repeatableNodeMap == null ? void 0 : repeatableNodeMap.get(def.label.trim().toLowerCase());
@@ -13658,7 +13660,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
13658
13660
  templateKeyPrefix: prefix,
13659
13661
  minEntries,
13660
13662
  maxEntries: def.maxEntries,
13661
- entryFields: def.fields.map((f, i) => ({
13663
+ entryFields: defFields.map((f, i) => ({
13662
13664
  key: f.key,
13663
13665
  label: f.label,
13664
13666
  type: mapFormDefFieldType(f.type),
@@ -13683,7 +13685,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
13683
13685
  label: def.label,
13684
13686
  order: def.order ?? 0,
13685
13687
  type: "single",
13686
- fields: def.fields.map((f, i) => ({
13688
+ fields: defFields.map((f, i) => ({
13687
13689
  key: f.key,
13688
13690
  label: f.label,
13689
13691
  type: mapFormDefFieldType(f.type),
@@ -17294,7 +17296,7 @@ function repeatablePageToSection(page) {
17294
17296
  label: page.label,
17295
17297
  description: page.description,
17296
17298
  order: typeof page.order === "number" ? page.order + 1e4 : 1e4,
17297
- fields: page.fields,
17299
+ fields: page.fields ?? page.entryFields ?? [],
17298
17300
  repeatable: true,
17299
17301
  minEntries: page.minEntries,
17300
17302
  maxEntries: page.maxEntries,
@@ -17366,7 +17368,14 @@ function buildRepeatablePagesInputForApply(schema, sectionState) {
17366
17368
  const minEntries = page.minEntries != null ? Math.max(0, page.minEntries) : 1;
17367
17369
  let entryCount;
17368
17370
  if (Array.isArray(entries)) {
17369
- entryCount = minEntries === 0 ? entries.length : Math.max(1, entries.length);
17371
+ const realEntries = entries.filter((entry) => {
17372
+ if (!isRecord(entry)) return false;
17373
+ return Object.keys(entry).some(
17374
+ (k) => k !== ENTRY_ID_KEY && k !== ENTRY_NAME_KEY
17375
+ );
17376
+ });
17377
+ const count = minEntries === 0 ? realEntries.length : Math.max(1, realEntries.length);
17378
+ entryCount = count;
17370
17379
  } else {
17371
17380
  entryCount = minEntries === 0 ? 0 : minEntries;
17372
17381
  }
@@ -17377,6 +17386,25 @@ function buildRepeatablePagesInputForApply(schema, sectionState) {
17377
17386
  };
17378
17387
  });
17379
17388
  }
17389
+ function sanitizeSectionStateAgainstSchema(state, schema) {
17390
+ if (!state) return {};
17391
+ if (!schema) return { ...state };
17392
+ const known = /* @__PURE__ */ new Set();
17393
+ for (const s of schema.sections ?? []) known.add(s.id);
17394
+ for (const p of schema.repeatablePages ?? []) known.add(p.id);
17395
+ const out = {};
17396
+ for (const [key, value] of Object.entries(state)) {
17397
+ if (known.has(key)) {
17398
+ out[key] = value;
17399
+ continue;
17400
+ }
17401
+ const compositeMatch = /^(.+?)_(\d+)_(.+)$/.exec(key);
17402
+ if (compositeMatch && known.has(compositeMatch[1]) && known.has(compositeMatch[3])) {
17403
+ out[key] = value;
17404
+ }
17405
+ }
17406
+ return out;
17407
+ }
17380
17408
  async function fetchRow(supabaseUrl, anonKey, table, id) {
17381
17409
  const url = `${supabaseUrl}/rest/v1/${table}?id=eq.${id}&select=*`;
17382
17410
  const res = await fetch(url, {
@@ -17609,7 +17637,8 @@ async function resolveFromForm(options) {
17609
17637
  let mergedSectionState = { ...sectionState };
17610
17638
  const templateDefaultData = templateRow.default_data;
17611
17639
  if (templateDefaultData && isDefaultDataV2(templateDefaultData)) {
17612
- const defaults = templateDefaultData.sectionState;
17640
+ const rawDefaults = templateDefaultData.sectionState;
17641
+ const defaults = sanitizeSectionStateAgainstSchema(rawDefaults, formSchema);
17613
17642
  for (const key of Object.keys(defaults)) {
17614
17643
  if (!(key in mergedSectionState)) {
17615
17644
  mergedSectionState[key] = defaults[key];
@@ -17618,6 +17647,7 @@ async function resolveFromForm(options) {
17618
17647
  }
17619
17648
  mergedSectionState = mergeRepeatableEntryMeta(mergedSectionState, templateDefaultMetaSectionState, inferredSections);
17620
17649
  mergedSectionState = mergeRepeatableEntryMeta(mergedSectionState, defaultFormMetaSectionState, inferredSections);
17650
+ mergedSectionState = sanitizeSectionStateAgainstSchema(mergedSectionState, formSchema);
17621
17651
  const flatFormData = flattenSectionStateToFormData(mergedSectionState, inferredSections);
17622
17652
  const dynamicFields = templateConfig.dynamicFields || [];
17623
17653
  const mappings = [];
@@ -17636,7 +17666,8 @@ async function resolveFromForm(options) {
17636
17666
  (repeatableFromSchema ?? []).map((r) => [baseId(r.nodeId), r])
17637
17667
  );
17638
17668
  const topLevelRepeatables = inferredSections.filter((s) => s.type === "repeatable" && !s.parentId).map((s) => {
17639
- const entries = mergedSectionState[s.id] ?? [];
17669
+ const rawEntries = mergedSectionState[s.id];
17670
+ const entries = Array.isArray(rawEntries) ? rawEntries : [];
17640
17671
  const nodeId = s.treeNodeId ?? s.id;
17641
17672
  const schemaRepeatable = repeatableFromSchemaByBase.get(baseId(nodeId));
17642
17673
  const entryMeta = entries.map((e) => getRepeatableEntryMeta(e, s));
@@ -17648,11 +17679,13 @@ async function resolveFromForm(options) {
17648
17679
  const parentId = s.parentId;
17649
17680
  const parentSection = inferredSections.find((ps) => ps.id === parentId);
17650
17681
  const parentNodeId = parentSection ? parentSection.treeNodeId ?? parentSection.id : parentId;
17651
- const parentEntries = mergedSectionState[parentId] ?? [];
17682
+ const rawParentEntries = mergedSectionState[parentId];
17683
+ const parentEntries = Array.isArray(rawParentEntries) ? rawParentEntries : [];
17652
17684
  const nestedEntryMeta = {};
17653
17685
  const merged = [];
17654
17686
  for (let pi = 0; pi < parentEntries.length; pi++) {
17655
- const childEntries = mergedSectionState[`${parentId}_${pi}_${s.id}`] ?? [];
17687
+ const rawChildEntries = mergedSectionState[`${parentId}_${pi}_${s.id}`];
17688
+ const childEntries = Array.isArray(rawChildEntries) ? rawChildEntries : [];
17656
17689
  const meta = childEntries.map((e) => getRepeatableEntryMeta(e, s));
17657
17690
  nestedEntryMeta[`${baseId(parentNodeId)}_${pi + 1}_${baseId(nodeId)}`] = meta;
17658
17691
  merged.push(...meta);
@@ -17692,13 +17725,15 @@ async function resolveFromForm(options) {
17692
17725
  if (s.type !== "repeatable") continue;
17693
17726
  const parentId = s.parentId;
17694
17727
  if (parentId == null) continue;
17695
- const parentEntries = mergedSectionState[parentId] ?? [];
17728
+ const rawParentEntries2 = mergedSectionState[parentId];
17729
+ const parentEntries = Array.isArray(rawParentEntries2) ? rawParentEntries2 : [];
17696
17730
  const parentSection = inferredSections.find((ps) => ps.id === parentId);
17697
17731
  const parentTreeNodeId = parentSection ? parentSection.treeNodeId ?? parentSection.id : parentId;
17698
17732
  const childTreeNodeId = s.treeNodeId ?? s.id;
17699
17733
  for (let pi = 0; pi < parentEntries.length; pi++) {
17700
17734
  const compositeKey = `${parentId}_${pi}_${s.id}`;
17701
- const entries = mergedSectionState[compositeKey] ?? [];
17735
+ const rawEntries2 = mergedSectionState[compositeKey];
17736
+ const entries = Array.isArray(rawEntries2) ? rawEntries2 : [];
17702
17737
  const nestedKey = `${baseId(parentTreeNodeId)}_${pi + 1}_${baseId(childTreeNodeId)}`;
17703
17738
  repeatableNestedEntryCounts[nestedKey] = Math.max(1, entries.length);
17704
17739
  }
@@ -19057,9 +19092,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
19057
19092
  }
19058
19093
  return svgString;
19059
19094
  }
19060
- const resolvedPackageVersion = "0.5.218";
19095
+ const resolvedPackageVersion = "0.5.220";
19061
19096
  const PACKAGE_VERSION = resolvedPackageVersion;
19062
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.218";
19097
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.220";
19063
19098
  const roundParityValue = (value) => {
19064
19099
  if (typeof value !== "number") return value;
19065
19100
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19620,6 +19655,58 @@ class PixldocsRenderer {
19620
19655
  compressionQuality
19621
19656
  });
19622
19657
  }
19658
+ /**
19659
+ * Render the template AND deliver the resulting PDF to the user — currently
19660
+ * via email (SendGrid). The PDF is rendered server-side on EC2, uploaded to
19661
+ * S3 (returned as `pdfUrl` for download fallback), and emailed with the PDF
19662
+ * attached.
19663
+ *
19664
+ * Every delivery is logged in the `delivery_log` table for audit + admin
19665
+ * resend. The same `pdfUrl` is returned to the caller so the host app can
19666
+ * also offer a "Download" button on the success screen.
19667
+ *
19668
+ * ```ts
19669
+ * const result = await renderer.renderAndDeliver({
19670
+ * templateId, formSchemaId, sectionState,
19671
+ * project: 'biomaker',
19672
+ * recipient: { email: 'user@example.com', name: 'Anya' },
19673
+ * });
19674
+ * // → { jobId, pdfUrl, deliveries: [{ channel: 'email', status: 'sent' }] }
19675
+ * ```
19676
+ *
19677
+ * Requires `RendererConfig.deliveryServerUrl` to be set.
19678
+ */
19679
+ async renderAndDeliver(options) {
19680
+ var _a;
19681
+ const base = (_a = this.config.deliveryServerUrl) == null ? void 0 : _a.replace(/\/+$/, "");
19682
+ if (!base) {
19683
+ throw new Error("renderAndDeliver: RendererConfig.deliveryServerUrl is required");
19684
+ }
19685
+ const { templateId, formSchemaId, sectionState, themeId, watermark, project, recipient, channels, scale } = options;
19686
+ if (!project) throw new Error('renderAndDeliver: project is required (e.g. "biomaker", "pixldocs")');
19687
+ if (!(recipient == null ? void 0 : recipient.email)) throw new Error("renderAndDeliver: recipient.email is required");
19688
+ const body = {
19689
+ template_id: templateId,
19690
+ form_schema_id: formSchemaId,
19691
+ data: { version: 2, sectionState },
19692
+ project,
19693
+ recipient,
19694
+ channels: channels || ["email"]
19695
+ };
19696
+ if (themeId) body.theme = themeId;
19697
+ if (typeof watermark === "boolean") body.watermark = watermark;
19698
+ if (typeof scale === "number") body.scale = scale;
19699
+ const res = await fetch(`${base}/v1/render-and-deliver`, {
19700
+ method: "POST",
19701
+ headers: { "Content-Type": "application/json" },
19702
+ body: JSON.stringify(body)
19703
+ });
19704
+ if (!res.ok) {
19705
+ const errText = await res.text().catch(() => "");
19706
+ throw new Error(`renderAndDeliver failed: ${res.status} ${errText.slice(0, 300)}`);
19707
+ }
19708
+ return await res.json();
19709
+ }
19623
19710
  async renderById(templateId, formData, options) {
19624
19711
  const resolved = await resolveTemplateData({
19625
19712
  templateId,
@@ -19751,7 +19838,7 @@ class PixldocsRenderer {
19751
19838
  await this.waitForCanvasScene(container, cloned, i);
19752
19839
  }
19753
19840
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19754
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-CoZ-RUqL.cjs"));
19841
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-UBFTDrVt.cjs"));
19755
19842
  const prepared = preparePagesForExport(
19756
19843
  cloned.pages,
19757
19844
  canvasWidth,
@@ -21941,7 +22028,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21941
22028
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21942
22029
  sanitizeSvgTreeForPdf(svgToDraw);
21943
22030
  try {
21944
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-CoZ-RUqL.cjs"));
22031
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-UBFTDrVt.cjs"));
21945
22032
  try {
21946
22033
  await logTextMeasurementDiagnostic(svgToDraw);
21947
22034
  } catch {
@@ -22338,4 +22425,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
22338
22425
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
22339
22426
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
22340
22427
  exports.warmTemplateFromForm = warmTemplateFromForm;
22341
- //# sourceMappingURL=index-D3NJNdX_.cjs.map
22428
+ //# sourceMappingURL=index-DRHaeOxK.cjs.map