@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.
@@ -13626,7 +13626,9 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
13626
13626
  function convert(defs, parentId) {
13627
13627
  var _a, _b;
13628
13628
  for (const def of defs) {
13629
- if (def.repeatable) {
13629
+ const isRepeatable = def.repeatable === true || def.type === "repeatable";
13630
+ const defFields = def.fields ?? def.entryFields ?? [];
13631
+ if (isRepeatable) {
13630
13632
  const rawPrefix = def.templateKeyPrefix || def.label.toLowerCase().replace(/\s+/g, "_");
13631
13633
  const labelPrefix = rawPrefix.startsWith("field_") ? rawPrefix : `field_${rawPrefix}`;
13632
13634
  const treeNodeId = repeatableNodeMap == null ? void 0 : repeatableNodeMap.get(def.label.trim().toLowerCase());
@@ -13640,7 +13642,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
13640
13642
  templateKeyPrefix: prefix,
13641
13643
  minEntries,
13642
13644
  maxEntries: def.maxEntries,
13643
- entryFields: def.fields.map((f, i) => ({
13645
+ entryFields: defFields.map((f, i) => ({
13644
13646
  key: f.key,
13645
13647
  label: f.label,
13646
13648
  type: mapFormDefFieldType(f.type),
@@ -13665,7 +13667,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
13665
13667
  label: def.label,
13666
13668
  order: def.order ?? 0,
13667
13669
  type: "single",
13668
- fields: def.fields.map((f, i) => ({
13670
+ fields: defFields.map((f, i) => ({
13669
13671
  key: f.key,
13670
13672
  label: f.label,
13671
13673
  type: mapFormDefFieldType(f.type),
@@ -17276,7 +17278,7 @@ function repeatablePageToSection(page) {
17276
17278
  label: page.label,
17277
17279
  description: page.description,
17278
17280
  order: typeof page.order === "number" ? page.order + 1e4 : 1e4,
17279
- fields: page.fields,
17281
+ fields: page.fields ?? page.entryFields ?? [],
17280
17282
  repeatable: true,
17281
17283
  minEntries: page.minEntries,
17282
17284
  maxEntries: page.maxEntries,
@@ -17348,7 +17350,14 @@ function buildRepeatablePagesInputForApply(schema, sectionState) {
17348
17350
  const minEntries = page.minEntries != null ? Math.max(0, page.minEntries) : 1;
17349
17351
  let entryCount;
17350
17352
  if (Array.isArray(entries)) {
17351
- entryCount = minEntries === 0 ? entries.length : Math.max(1, entries.length);
17353
+ const realEntries = entries.filter((entry) => {
17354
+ if (!isRecord(entry)) return false;
17355
+ return Object.keys(entry).some(
17356
+ (k) => k !== ENTRY_ID_KEY && k !== ENTRY_NAME_KEY
17357
+ );
17358
+ });
17359
+ const count = minEntries === 0 ? realEntries.length : Math.max(1, realEntries.length);
17360
+ entryCount = count;
17352
17361
  } else {
17353
17362
  entryCount = minEntries === 0 ? 0 : minEntries;
17354
17363
  }
@@ -17359,6 +17368,25 @@ function buildRepeatablePagesInputForApply(schema, sectionState) {
17359
17368
  };
17360
17369
  });
17361
17370
  }
17371
+ function sanitizeSectionStateAgainstSchema(state, schema) {
17372
+ if (!state) return {};
17373
+ if (!schema) return { ...state };
17374
+ const known = /* @__PURE__ */ new Set();
17375
+ for (const s of schema.sections ?? []) known.add(s.id);
17376
+ for (const p of schema.repeatablePages ?? []) known.add(p.id);
17377
+ const out = {};
17378
+ for (const [key, value] of Object.entries(state)) {
17379
+ if (known.has(key)) {
17380
+ out[key] = value;
17381
+ continue;
17382
+ }
17383
+ const compositeMatch = /^(.+?)_(\d+)_(.+)$/.exec(key);
17384
+ if (compositeMatch && known.has(compositeMatch[1]) && known.has(compositeMatch[3])) {
17385
+ out[key] = value;
17386
+ }
17387
+ }
17388
+ return out;
17389
+ }
17362
17390
  async function fetchRow(supabaseUrl, anonKey, table, id) {
17363
17391
  const url = `${supabaseUrl}/rest/v1/${table}?id=eq.${id}&select=*`;
17364
17392
  const res = await fetch(url, {
@@ -17591,7 +17619,8 @@ async function resolveFromForm(options) {
17591
17619
  let mergedSectionState = { ...sectionState };
17592
17620
  const templateDefaultData = templateRow.default_data;
17593
17621
  if (templateDefaultData && isDefaultDataV2(templateDefaultData)) {
17594
- const defaults = templateDefaultData.sectionState;
17622
+ const rawDefaults = templateDefaultData.sectionState;
17623
+ const defaults = sanitizeSectionStateAgainstSchema(rawDefaults, formSchema);
17595
17624
  for (const key of Object.keys(defaults)) {
17596
17625
  if (!(key in mergedSectionState)) {
17597
17626
  mergedSectionState[key] = defaults[key];
@@ -17600,6 +17629,7 @@ async function resolveFromForm(options) {
17600
17629
  }
17601
17630
  mergedSectionState = mergeRepeatableEntryMeta(mergedSectionState, templateDefaultMetaSectionState, inferredSections);
17602
17631
  mergedSectionState = mergeRepeatableEntryMeta(mergedSectionState, defaultFormMetaSectionState, inferredSections);
17632
+ mergedSectionState = sanitizeSectionStateAgainstSchema(mergedSectionState, formSchema);
17603
17633
  const flatFormData = flattenSectionStateToFormData(mergedSectionState, inferredSections);
17604
17634
  const dynamicFields = templateConfig.dynamicFields || [];
17605
17635
  const mappings = [];
@@ -17618,7 +17648,8 @@ async function resolveFromForm(options) {
17618
17648
  (repeatableFromSchema ?? []).map((r) => [baseId(r.nodeId), r])
17619
17649
  );
17620
17650
  const topLevelRepeatables = inferredSections.filter((s) => s.type === "repeatable" && !s.parentId).map((s) => {
17621
- const entries = mergedSectionState[s.id] ?? [];
17651
+ const rawEntries = mergedSectionState[s.id];
17652
+ const entries = Array.isArray(rawEntries) ? rawEntries : [];
17622
17653
  const nodeId = s.treeNodeId ?? s.id;
17623
17654
  const schemaRepeatable = repeatableFromSchemaByBase.get(baseId(nodeId));
17624
17655
  const entryMeta = entries.map((e) => getRepeatableEntryMeta(e, s));
@@ -17630,11 +17661,13 @@ async function resolveFromForm(options) {
17630
17661
  const parentId = s.parentId;
17631
17662
  const parentSection = inferredSections.find((ps) => ps.id === parentId);
17632
17663
  const parentNodeId = parentSection ? parentSection.treeNodeId ?? parentSection.id : parentId;
17633
- const parentEntries = mergedSectionState[parentId] ?? [];
17664
+ const rawParentEntries = mergedSectionState[parentId];
17665
+ const parentEntries = Array.isArray(rawParentEntries) ? rawParentEntries : [];
17634
17666
  const nestedEntryMeta = {};
17635
17667
  const merged = [];
17636
17668
  for (let pi = 0; pi < parentEntries.length; pi++) {
17637
- const childEntries = mergedSectionState[`${parentId}_${pi}_${s.id}`] ?? [];
17669
+ const rawChildEntries = mergedSectionState[`${parentId}_${pi}_${s.id}`];
17670
+ const childEntries = Array.isArray(rawChildEntries) ? rawChildEntries : [];
17638
17671
  const meta = childEntries.map((e) => getRepeatableEntryMeta(e, s));
17639
17672
  nestedEntryMeta[`${baseId(parentNodeId)}_${pi + 1}_${baseId(nodeId)}`] = meta;
17640
17673
  merged.push(...meta);
@@ -17674,13 +17707,15 @@ async function resolveFromForm(options) {
17674
17707
  if (s.type !== "repeatable") continue;
17675
17708
  const parentId = s.parentId;
17676
17709
  if (parentId == null) continue;
17677
- const parentEntries = mergedSectionState[parentId] ?? [];
17710
+ const rawParentEntries2 = mergedSectionState[parentId];
17711
+ const parentEntries = Array.isArray(rawParentEntries2) ? rawParentEntries2 : [];
17678
17712
  const parentSection = inferredSections.find((ps) => ps.id === parentId);
17679
17713
  const parentTreeNodeId = parentSection ? parentSection.treeNodeId ?? parentSection.id : parentId;
17680
17714
  const childTreeNodeId = s.treeNodeId ?? s.id;
17681
17715
  for (let pi = 0; pi < parentEntries.length; pi++) {
17682
17716
  const compositeKey = `${parentId}_${pi}_${s.id}`;
17683
- const entries = mergedSectionState[compositeKey] ?? [];
17717
+ const rawEntries2 = mergedSectionState[compositeKey];
17718
+ const entries = Array.isArray(rawEntries2) ? rawEntries2 : [];
17684
17719
  const nestedKey = `${baseId(parentTreeNodeId)}_${pi + 1}_${baseId(childTreeNodeId)}`;
17685
17720
  repeatableNestedEntryCounts[nestedKey] = Math.max(1, entries.length);
17686
17721
  }
@@ -19039,9 +19074,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
19039
19074
  }
19040
19075
  return svgString;
19041
19076
  }
19042
- const resolvedPackageVersion = "0.5.218";
19077
+ const resolvedPackageVersion = "0.5.220";
19043
19078
  const PACKAGE_VERSION = resolvedPackageVersion;
19044
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.218";
19079
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.220";
19045
19080
  const roundParityValue = (value) => {
19046
19081
  if (typeof value !== "number") return value;
19047
19082
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19602,6 +19637,58 @@ class PixldocsRenderer {
19602
19637
  compressionQuality
19603
19638
  });
19604
19639
  }
19640
+ /**
19641
+ * Render the template AND deliver the resulting PDF to the user — currently
19642
+ * via email (SendGrid). The PDF is rendered server-side on EC2, uploaded to
19643
+ * S3 (returned as `pdfUrl` for download fallback), and emailed with the PDF
19644
+ * attached.
19645
+ *
19646
+ * Every delivery is logged in the `delivery_log` table for audit + admin
19647
+ * resend. The same `pdfUrl` is returned to the caller so the host app can
19648
+ * also offer a "Download" button on the success screen.
19649
+ *
19650
+ * ```ts
19651
+ * const result = await renderer.renderAndDeliver({
19652
+ * templateId, formSchemaId, sectionState,
19653
+ * project: 'biomaker',
19654
+ * recipient: { email: 'user@example.com', name: 'Anya' },
19655
+ * });
19656
+ * // → { jobId, pdfUrl, deliveries: [{ channel: 'email', status: 'sent' }] }
19657
+ * ```
19658
+ *
19659
+ * Requires `RendererConfig.deliveryServerUrl` to be set.
19660
+ */
19661
+ async renderAndDeliver(options) {
19662
+ var _a;
19663
+ const base = (_a = this.config.deliveryServerUrl) == null ? void 0 : _a.replace(/\/+$/, "");
19664
+ if (!base) {
19665
+ throw new Error("renderAndDeliver: RendererConfig.deliveryServerUrl is required");
19666
+ }
19667
+ const { templateId, formSchemaId, sectionState, themeId, watermark, project, recipient, channels, scale } = options;
19668
+ if (!project) throw new Error('renderAndDeliver: project is required (e.g. "biomaker", "pixldocs")');
19669
+ if (!(recipient == null ? void 0 : recipient.email)) throw new Error("renderAndDeliver: recipient.email is required");
19670
+ const body = {
19671
+ template_id: templateId,
19672
+ form_schema_id: formSchemaId,
19673
+ data: { version: 2, sectionState },
19674
+ project,
19675
+ recipient,
19676
+ channels: channels || ["email"]
19677
+ };
19678
+ if (themeId) body.theme = themeId;
19679
+ if (typeof watermark === "boolean") body.watermark = watermark;
19680
+ if (typeof scale === "number") body.scale = scale;
19681
+ const res = await fetch(`${base}/v1/render-and-deliver`, {
19682
+ method: "POST",
19683
+ headers: { "Content-Type": "application/json" },
19684
+ body: JSON.stringify(body)
19685
+ });
19686
+ if (!res.ok) {
19687
+ const errText = await res.text().catch(() => "");
19688
+ throw new Error(`renderAndDeliver failed: ${res.status} ${errText.slice(0, 300)}`);
19689
+ }
19690
+ return await res.json();
19691
+ }
19605
19692
  async renderById(templateId, formData, options) {
19606
19693
  const resolved = await resolveTemplateData({
19607
19694
  templateId,
@@ -19733,7 +19820,7 @@ class PixldocsRenderer {
19733
19820
  await this.waitForCanvasScene(container, cloned, i);
19734
19821
  }
19735
19822
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19736
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-som_tmDY.js");
19823
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-D5bhT-57.js");
19737
19824
  const prepared = preparePagesForExport(
19738
19825
  cloned.pages,
19739
19826
  canvasWidth,
@@ -21923,7 +22010,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21923
22010
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21924
22011
  sanitizeSvgTreeForPdf(svgToDraw);
21925
22012
  try {
21926
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-som_tmDY.js");
22013
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-D5bhT-57.js");
21927
22014
  try {
21928
22015
  await logTextMeasurementDiagnostic(svgToDraw);
21929
22016
  } catch {
@@ -22323,4 +22410,4 @@ export {
22323
22410
  buildTeaserBlurFlatKeys as y,
22324
22411
  collectFontDescriptorsFromConfig as z
22325
22412
  };
22326
- //# sourceMappingURL=index-CZk_GpIL.js.map
22413
+ //# sourceMappingURL=index-KtRZhEPi.js.map