← Files Google DriveARCHIVED FILE

skills/google-docs/runtime/docs-read-normalizer.mjs

10.5 KB · Sep 30, 2026 · 23:18 UTC

↓ Download file

/* Dependency-free normalization for file-backed Google Docs trusted reads. */

function isRecord(value) {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

function unwrap(value) {
  if (isRecord(value?.structuredContent)) return value.structuredContent;
  if (isRecord(value?.result?.structuredContent)) return value.result.structuredContent;
  if (isRecord(value?.result)) return value.result;
  return value;
}

function findDocument(value) {
  const root = unwrap(value);
  const seen = new Set();
  const visit = (candidate) => {
    if (!candidate || typeof candidate !== "object" || seen.has(candidate)) return null;
    seen.add(candidate);
    if (isRecord(candidate) && typeof (candidate.documentId ?? candidate.document_id) === "string" &&
        (candidate.tabs || candidate.body || candidate.revisionId || candidate.revision_id)) return candidate;
    for (const child of Array.isArray(candidate) ? candidate : Object.values(candidate)) {
      const found = visit(child);
      if (found) return found;
    }
    return null;
  };
  return visit(root);
}

function isPrivateUse(codePoint) {
  return (codePoint >= 0xE000 && codePoint <= 0xF8FF) ||
    (codePoint >= 0xF0000 && codePoint <= 0xFFFFD) ||
    (codePoint >= 0x100000 && codePoint <= 0x10FFFD);
}

function visibleText(value) {
  let output = "";
  for (const character of String(value ?? "")) {
    const codePoint = character.codePointAt(0);
    output += isPrivateUse(codePoint)
      ? `⟦PROTECTED_CONTROL U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}⟧`
      : character;
  }
  return output;
}

function markdownLink(label, url) {
  const text = String(label ?? "").replace(/\n/g, " ").trim() || url;
  return `[${text.replace(/([\\\[\]])/g, "\\$1")}](${url})`;
}

function elementText(element) {
  if (element.textRun) {
    const content = visibleText(element.textRun.content);
    const url = element.textRun.textStyle?.link?.url;
    return url ? markdownLink(content, url) : content;
  }
  if (element.person) {
    const properties = element.person.personProperties ?? {};
    const label = properties.name ?? properties.email ?? "person";
    return properties.email ? `@${label} <${properties.email}>` : `@${label}`;
  }
  if (element.dateElement) {
    return element.dateElement.dateElementProperties?.displayText ?? "[DATE]";
  }
  if (element.richLink) {
    const properties = element.richLink.richLinkProperties ?? {};
    return properties.uri ? markdownLink(properties.title ?? properties.uri, properties.uri) : `[RICH_LINK ${properties.title ?? ""}]`;
  }
  if (element.inlineObjectElement) return `[INLINE_OBJECT ${element.inlineObjectElement.inlineObjectId ?? ""}]`;
  if (element.footnoteReference) return `[FOOTNOTE ${element.footnoteReference.footnoteId ?? ""}]`;
  if (element.horizontalRule) return "[HORIZONTAL_RULE]";
  if (element.equation) return "[EQUATION]";
  return "";
}

function tabRecords(document) {
  if (Array.isArray(document.tabs) && document.tabs.length > 0) {
    return document.tabs.map((tab, index) => ({
      tabId: tab.tabId ?? tab.tab_id ?? tab.tabProperties?.tabId ?? tab.tab_properties?.tab_id ?? null,
      title: tab.title ?? tab.tabProperties?.title ?? tab.tab_properties?.title ?? `Tab ${index + 1}`,
      body: tab.body ?? tab.documentTab?.body ?? tab.document_tab?.body ?? null,
    }));
  }
  return [{ tabId: null, title: "Document", body: document.body ?? null }];
}

function controlsForParagraph(controls, paragraph) {
  return controls.filter((control) =>
    (control.tabId ?? null) === (paragraph.tabId ?? null) &&
    (control.segmentId ?? null) === (paragraph.segmentId ?? null) &&
    Number.isInteger(control.startIndex) && Number.isInteger(control.endIndex) &&
    Number.isInteger(paragraph.startIndex) && Number.isInteger(paragraph.endIndex) &&
    control.startIndex >= paragraph.startIndex && control.endIndex <= paragraph.endIndex,
  ).map((control) => ({
    classification: control.classification,
    fidelityClass: control.fidelityClass,
    startIndex: control.startIndex,
    endIndex: control.endIndex,
    codePoints: control.codePoints ?? [],
    dropdownId: control.dropdownId ?? null,
    selectedValue: control.selectedValue ?? null,
  }));
}

function normalizeParagraph(structuralElement, context, path, controls, ordinal) {
  const paragraph = structuralElement.paragraph;
  const bullet = paragraph.bullet ?? null;
  const record = {
    paragraphId: `P${String(ordinal).padStart(5, "0")}`,
    path,
    tabId: context.tabId,
    tabTitle: context.tabTitle,
    segmentId: context.segmentId,
    startIndex: structuralElement.startIndex ?? structuralElement.start_index ?? null,
    endIndex: structuralElement.endIndex ?? structuralElement.end_index ?? null,
    namedStyleType: paragraph.paragraphStyle?.namedStyleType ?? paragraph.paragraph_style?.named_style_type ?? null,
    isListItem: Boolean(bullet),
    listId: bullet?.listId ?? bullet?.list_id ?? null,
    nestingLevel: bullet?.nestingLevel ?? bullet?.nesting_level ?? null,
    table: context.table,
    text: (paragraph.elements ?? []).map(elementText).join("").replace(/\n$/, ""),
  };
  return { ...record, protectedControls: controlsForParagraph(controls, record) };
}

function collectParagraphs(document, controls, selectedTabId) {
  const paragraphs = [];
  const visitContent = (content, context, path) => {
    for (let index = 0; index < (content ?? []).length; index += 1) {
      const structuralElement = content[index];
      const elementPath = `${path}[${index}]`;
      if (structuralElement.paragraph?.elements) {
        paragraphs.push(normalizeParagraph(structuralElement, context, elementPath, controls, paragraphs.length + 1));
      } else if (structuralElement.table?.tableRows) {
        const tableStartIndex = structuralElement.startIndex ?? structuralElement.start_index ?? null;
        structuralElement.table.tableRows.forEach((row, rowIndex) => {
          (row.tableCells ?? []).forEach((cell, columnIndex) => {
            visitContent(cell.content ?? [], {
              ...context,
              table: { tableStartIndex, rowIndex, columnIndex },
            }, `${elementPath}.table.rows[${rowIndex}].cells[${columnIndex}].content`);
          });
        });
      } else if (structuralElement.tableOfContents?.content) {
        visitContent(structuralElement.tableOfContents.content, context, `${elementPath}.tableOfContents.content`);
      }
    }
  };

  for (const tab of tabRecords(document)) {
    if (selectedTabId !== null && tab.tabId !== selectedTabId) continue;
    visitContent(tab.body?.content ?? [], {
      tabId: tab.tabId,
      tabTitle: tab.title,
      segmentId: null,
      table: null,
    }, `tabs.${tab.tabId ?? "body"}.body.content`);
  }

  for (const [collectionName, values] of [["headers", document.headers], ["footers", document.footers], ["footnotes", document.footnotes]]) {
    if (!isRecord(values)) continue;
    for (const [segmentId, segment] of Object.entries(values)) {
      visitContent(segment.content ?? [], {
        tabId: null,
        tabTitle: collectionName,
        segmentId,
        table: null,
      }, `${collectionName}.${segmentId}.content`);
    }
  }
  return paragraphs;
}

function controlLabel(control) {
  const codePoints = control.codePoints?.length ? ` ${control.codePoints.join(",")}` : "";
  const selected = control.selectedValue !== null && control.selectedValue !== undefined ? ` selected=${JSON.stringify(control.selectedValue)}` : "";
  return `${control.classification}/${control.fidelityClass}@${control.startIndex}:${control.endIndex}${codePoints}${selected}`;
}

export function renderNormalizedDocumentText(normalized) {
  const lines = [
    `# ${normalized.document.title ?? "Google Doc"}`,
    "",
    `- Document ID: ${normalized.document.documentId}`,
    `- Revision ID: ${normalized.document.revisionId ?? "unavailable"}`,
    `- Selected tab: ${normalized.selectedTabId ?? "all"}`,
    `- Protected controls: ${normalized.controlSummary.total}`,
    `- Opaque controls: ${normalized.controlSummary.opaque}`,
    `- Authoritative dropdowns: ${normalized.controlSummary.authoritativeDropdowns}`,
    "",
    "Protected-control annotations are preservation instructions. Do not insert their displayed placeholder text to recreate a native control.",
    "",
  ];
  let currentSection = Symbol("none");
  for (const paragraph of normalized.paragraphs) {
    const section = `${paragraph.tabTitle ?? "Document"}\u0000${paragraph.tabId ?? "body"}\u0000${paragraph.segmentId ?? ""}`;
    if (section !== currentSection) {
      currentSection = section;
      lines.push(`## ${paragraph.tabTitle ?? "Document"} (${paragraph.tabId ?? "body"})`, "");
    }
    const metadata = [
      paragraph.paragraphId,
      `${paragraph.startIndex ?? "?"}:${paragraph.endIndex ?? "?"}`,
      paragraph.namedStyleType ?? "NORMAL_TEXT",
      paragraph.isListItem ? `LIST id=${paragraph.listId ?? "?"} level=${paragraph.nestingLevel ?? 0}` : null,
      paragraph.table ? `TABLE row=${paragraph.table.rowIndex} col=${paragraph.table.columnIndex}` : null,
    ].filter(Boolean).join(" | ");
    lines.push(`[${metadata}]`);
    if (paragraph.protectedControls.length > 0) {
      lines.push(`⟦PROTECTED: ${paragraph.protectedControls.map(controlLabel).join("; ")} — preserve this control and its containing structure⟧`);
    }
    lines.push(paragraph.text || "⟦EMPTY PARAGRAPH⟧", "");
  }
  return `${lines.join("\n")}\n`;
}

export function normalizeGoogleDocumentRead({ documentResult, trustedReadContext, selectedTabId = null } = {}) {
  const document = findDocument(documentResult);
  if (!document) throw new Error("Could not locate the Google Docs document resource");
  const controls = trustedReadContext?.derivedControlInventory?.controls ?? [];
  const paragraphs = collectParagraphs(document, controls, selectedTabId);
  const normalized = {
    version: 1,
    kind: "google-docs-normalized-read",
    document: {
      documentId: document.documentId ?? document.document_id,
      title: document.title ?? null,
      revisionId: document.revisionId ?? document.revision_id ?? null,
      documentUrl: document.documentUrl ?? document.document_url ?? document.url ?? null,
    },
    selectedTabId,
    controlSummary: {
      total: controls.length,
      opaque: trustedReadContext?.derivedControlInventory?.opaqueControlCount ?? 0,
      authoritativeDropdowns: trustedReadContext?.derivedControlInventory?.authoritativeDropdownCount ?? 0,
      affectedParagraphs: paragraphs.filter((paragraph) => paragraph.protectedControls.length > 0).length,
    },
    paragraphs,
  };
  return { normalized, markdown: renderNormalizedDocumentText(normalized) };
}

SHA-256: 324de06dedc0476c91f7f3d2fe4b15c4cb992ace2afb9e57df498a994020878a