@yuji-min/google-docs-parser 1.0.3 → 1.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.
package/dist/index.cjs CHANGED
@@ -367,7 +367,13 @@ var ParagraphCursor = class {
367
367
  };
368
368
  function createDocsClient() {
369
369
  try {
370
+ const credentialsEnv = process.env.GOOGLE_APPLICATION_CREDENTIALS;
371
+ let credentials;
372
+ if (credentialsEnv?.trim().startsWith("{")) {
373
+ credentials = JSON.parse(credentialsEnv);
374
+ }
370
375
  const auth = new googleAuthLibrary.GoogleAuth({
376
+ credentials,
371
377
  scopes: ["https://www.googleapis.com/auth/documents.readonly"]
372
378
  });
373
379
  return googleapis.google.docs({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/tree.ts","../src/list.ts","../src/textBlock.ts","../src/section.ts","../src/cursor.ts","../src/auth.ts","../src/parser.ts"],"names":["GoogleAuth","google"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * A constant tuple of supported Google Docs named styles that represent headings or titles.\n * These are used to identify structural nodes and section boundaries within the document tree.\n */\nexport const VALID_NAMED_STYLES = [\n \"HEADING_1\",\n \"HEADING_2\",\n \"HEADING_3\",\n \"HEADING_4\",\n \"HEADING_5\",\n \"HEADING_6\",\n \"TITLE\",\n \"SUBTITLE\",\n] as const;\n\n/**\n * A union type representing any one of the valid named styles defined in `VALID_NAMED_STYLES`.\n */\nexport type ValidNamedStyle = (typeof VALID_NAMED_STYLES)[number];\n\n/**\n * A Set containing the valid named styles for efficient O(1) validation checks.\n * This is primarily used in type guards to verify if a style string is a valid heading.\n */\nexport const VALID_NAMED_STYLES_SET = new Set<string>(VALID_NAMED_STYLES);\n","import { docs_v1 } from \"googleapis\";\nimport { Schema, NamedStyleType } from \"./types\";\nimport { VALID_NAMED_STYLES_SET } from \"./constants\";\n\n/**\n * Extracts pure text content from a Google Docs Paragraph.\n *\n * - Joins content from all `textRun` elements.\n * - Trims leading and trailing whitespace.\n * - Normalizes newline characters (`\\n`) to spaces.\n *\n * @param paragraph - The Google Docs paragraph object.\n * @returns The extracted and cleaned text string. Returns an empty string if the paragraph is empty.\n */\nexport function extractParagraphText(\n paragraph: docs_v1.Schema$Paragraph\n): string {\n const elements = paragraph.elements ?? [];\n\n const text = elements\n .map((el: docs_v1.Schema$ParagraphElement) => el.textRun?.content || \"\")\n .join(\"\")\n .trim()\n .replace(/\\n/g, \" \");\n\n return text || \"\";\n}\n\n/**\n * Checks if a paragraph has a specific `namedStyleType` (e.g., HEADING_1).\n *\n * @param paragraph - The paragraph to check.\n * @param namedStyleType - The style type to compare against.\n * @returns `true` if the paragraph's style matches the provided type; otherwise `false`.\n */\nexport function hasNamedStyle(\n paragraph: docs_v1.Schema$Paragraph,\n namedStyleType?: NamedStyleType\n): boolean {\n if (!namedStyleType) return false;\n const style = paragraph.paragraphStyle?.namedStyleType;\n return style === namedStyleType;\n}\n\n/**\n * Retrieves the named style type of a paragraph, handling default behaviors and edge cases.\n *\n * Resolution Logic:\n * 1. Returns `undefined` if the paragraph has no style and no content (completely empty).\n * 2. Returns `\"NORMAL_TEXT\"` if the paragraph has content but no explicit style (Google Docs default).\n * 3. Returns `\"NORMAL_TEXT\"` if the style object exists but `namedStyleType` is missing.\n * 4. Returns the `namedStyleType` if it exists in the valid set (`VALID_NAMED_STYLES`).\n * 5. Returns `undefined` if the style is invalid or unknown.\n *\n * @param paragraph - The paragraph to analyze.\n * @returns The valid `NamedStyleType`, `\"NORMAL_TEXT\"`, or `undefined`.\n */\nexport function getParagraphNamedStyleType(\n paragraph: docs_v1.Schema$Paragraph\n): NamedStyleType | \"NORMAL_TEXT\" | undefined {\n if (!paragraph.paragraphStyle && !paragraph.elements?.length) {\n return undefined;\n }\n if (!paragraph.paragraphStyle) {\n return \"NORMAL_TEXT\";\n }\n\n const namedStyleType = paragraph.paragraphStyle.namedStyleType;\n\n if (!namedStyleType) {\n return \"NORMAL_TEXT\";\n }\n\n if (VALID_NAMED_STYLES_SET.has(namedStyleType)) {\n return namedStyleType as NamedStyleType;\n }\n\n return undefined;\n}\n\n/**\n * Type Guard to check if a style is a valid Heading style (HEADING_1~6, TITLE, SUBTITLE).\n *\n * - Returns `false` for `\"NORMAL_TEXT\"`.\n * - Returns `false` for undefined or invalid strings.\n *\n * @param style - The style string to check.\n * @returns `true` if the style is a valid `NamedStyleType`.\n */\nexport function isNamedStyleType(\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined\n): style is NamedStyleType {\n if (typeof style !== \"string\") return false;\n if (style === \"NORMAL_TEXT\") return false;\n\n return VALID_NAMED_STYLES_SET.has(style);\n}\n\n/**\n * Splits a string by a delimiter and trims whitespace from each item.\n *\n * @param text - The string to split.\n * @param delimiter - The character to split by (e.g., \",\").\n * @param filterEmpty - If `true`, removes empty strings from the result. Defaults to `false`.\n * @returns An array of trimmed strings.\n */\nexport function splitAndTrim(\n text: string,\n delimiter: string,\n filterEmpty = false\n): string[] {\n if (text === \"\") {\n return [];\n }\n const items = text.split(delimiter).map((t) => t.trim());\n return filterEmpty ? items.filter((t) => t.length > 0) : items;\n}\n\n/**\n * Parses text into a Key-Value list structure.\n *\n * Format: \"Key: Value1, Value2\"\n *\n * @param text - The text to parse.\n * @param keyDelimiter - The separator between key and values (e.g., \":\").\n * @param listDelimiter - The separator between list items (e.g., \",\").\n * @returns An object `{ key, value[] }` if parsing succeeds, or the original text string if the key delimiter is missing.\n */\nexport function parseToKeyedList(\n text: string,\n keyDelimiter: string,\n listDelimiter: string\n): { key: string; value: string[] } | string {\n const delimiterIndex = text.indexOf(keyDelimiter);\n\n if (delimiterIndex <= 0) {\n return text;\n }\n\n const key = text.substring(0, delimiterIndex).trim();\n const valuePart = text.substring(delimiterIndex + keyDelimiter.length).trim();\n\n const value = valuePart ? splitAndTrim(valuePart, listDelimiter, true) : [];\n\n return { key, value };\n}\n\n/**\n * Maps delimited text values to a list of keys by position.\n *\n * Format: \"Value1 | Value2 | Value3\" -> { Key1: \"Value1\", Key2: \"Value2\", ... }\n *\n * @param text - The text to parse.\n * @param keys - The list of field names (keys) to map values to.\n * @param delimiter - The separator between values (e.g., \"|\").\n * @returns A record object mapping keys to values. Missing values are filled with empty strings.\n */\nexport function parseToFields(\n text: string,\n keys: readonly string[],\n delimiter: string\n): Record<string, unknown> {\n const values = splitAndTrim(text, delimiter, false);\n\n return keys.reduce((acc, key, index) => {\n const value = values[index];\n acc[key] = value !== undefined && value !== \"\" ? value : \"\";\n return acc;\n }, {} as Record<string, unknown>);\n}\n\n/**\n * Parses a simple delimited string into an array, filtering out empty items.\n *\n * Example: \"Apple, , Banana\" -> [\"Apple\", \"Banana\"]\n *\n * @param text - The text to parse.\n * @param delimiter - The separator (e.g., \",\").\n * @returns An array of non-empty strings.\n */\nexport function parseDelimitedList(text: string, delimiter: string): string[] {\n const values = text\n .split(delimiter)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n return values;\n}\n\n/**\n * Parses a single line of text into structured data based on the provided Schema.\n *\n * **Parsing Priorities:**\n * 1. **Keyed List**: If `schema.keyDelimiter` is set. (e.g., \"Team: A, B\")\n * 2. **Fixed Fields**: If `schema.keys` is set. (e.g., \"Google | Engineer | 2023\")\n * 3. **Default List**: Fallback to simple delimited list. (e.g., \"A, B, C\")\n *\n * @param text - The text content to parse.\n * @param schema - The schema object defining the parsing rules.\n * @returns The parsed result as an object, array, or string depending on the schema.\n */\nexport function parseStructuredText(\n text: string,\n schema: Schema\n): Record<string, unknown> | string[] | string {\n const delimiter = schema.delimiter || \",\";\n\n if (schema.keyDelimiter) {\n return parseToKeyedList(text, schema.keyDelimiter, delimiter);\n }\n\n if (schema.keys && schema.keys.length > 0) {\n return parseToFields(text, schema.keys, delimiter);\n }\n\n return parseDelimitedList(text, delimiter);\n}\n","import { docs_v1 } from \"googleapis\";\nimport { NamedStyleType, Node, Section, Schema } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { hasNamedStyle, parseStructuredText } from \"./utils\";\n\nconst CONTENT_KEY = \"content\";\n\n/**\n * Recursively collects all `NamedStyleType`s defined within a nested `Node` schema.\n *\n * This is used to build a set of allowed heading styles for the parser to recognize\n * valid tree nodes and detect boundaries.\n *\n * @param node - The current node schema to traverse.\n * @param set - The Set to populate with collected style types.\n */\nexport function collectNodeStylesRecursive(\n node: Node,\n set: Set<NamedStyleType>\n): void {\n if (node.title.namedStyleType) set.add(node.title.namedStyleType);\n if (node.content && node.content.kind === \"tree\") {\n collectNodeStylesRecursive(node.content.node, set);\n }\n}\n\n/**\n * Creates a normalized node object from a raw title string based on the provided schema.\n *\n * - If the schema defines parsing rules (delimiters/keys), the title is parsed into\n * structured data (Array or Object).\n * - If no parsing rules exist, the title is treated as a simple string.\n *\n * @param text - The raw text of the title/heading.\n * @param titleSchema - The schema defining how to parse the title.\n * @returns An object containing the parsed `title` and an empty `content` array.\n */\nexport function createNodeFromTitle(\n text: string,\n titleSchema: Schema\n): Record<\"title\" | \"content\", unknown> {\n const hasDelimiterSchema =\n !!titleSchema.keyDelimiter ||\n (!!titleSchema.keys && titleSchema.keys.length > 0) ||\n !!titleSchema.delimiter;\n\n if (hasDelimiterSchema) {\n const structuredText = parseStructuredText(text, titleSchema);\n if (Array.isArray(structuredText)) {\n return { title: structuredText, content: [] };\n } else if (typeof structuredText === \"object\" && structuredText !== null) {\n return { title: structuredText, content: [] };\n } else {\n return { title: text, content: [] };\n }\n } else {\n return { title: text, content: [] };\n }\n}\n\n/**\n * Represents the decision made by the parser for the current paragraph.\n */\nexport type TreeParsingAction =\n | { kind: \"exitSection\" }\n | { kind: \"createNode\" }\n | { kind: \"startChildNode\" }\n | { kind: \"finishCurrentNode\" }\n | { kind: \"appendDetail\" };\n\n/**\n * Determines the next action to take based on the current paragraph's style\n * and its relationship to the current node hierarchy.\n *\n * Decision Logic Priorities:\n * 1. New Section -> Exit.\n * 2. Matches Current Node Title -> Create Sibling Node.\n * 3. Matches Child Node Title -> Start Child Node (Recursion).\n * 4. Matches Ancestor/Same Level Heading -> Finish Current Node (Pop Stack).\n * 5. Heading outside this tree -> Exit.\n * 6. Otherwise -> Append as detail content.\n *\n * @param paragraph - The current paragraph being inspected.\n * @param cursor - The paragraph cursor.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - The stack of ancestor nodes for context.\n * @returns The determining parsing action.\n */\nexport function determineTreeParsingAction(\n paragraph: docs_v1.Schema$Paragraph,\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[]\n): TreeParsingAction {\n const nodeTitleStyle = nodeSchema.title.namedStyleType;\n const childNode =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n const isCurrentNodeTitle = hasNamedStyle(paragraph, nodeTitleStyle);\n const isChildNodeTitle =\n !!childNode && hasNamedStyle(paragraph, childNode.title.namedStyleType);\n const isAncestorNodeTitle = ancestorNodeList.some((a) =>\n hasNamedStyle(paragraph, a.title.namedStyleType)\n );\n\n const isInThisTree =\n isCurrentNodeTitle || isChildNodeTitle || isAncestorNodeTitle;\n\n const isHeading = cursor.isAtParagraphHeading();\n const isAtNewSection = cursor.isAtNewSection();\n const isHeadingOutsideThisTree = isHeading && !isInThisTree;\n const isHigherLevelHeading = isHeading && isAncestorNodeTitle;\n const isSameLevelHeading = isHeading && isCurrentNodeTitle;\n\n if (isAtNewSection) return { kind: \"exitSection\" };\n if (isCurrentNodeTitle) return { kind: \"createNode\" };\n if (isChildNodeTitle) return { kind: \"startChildNode\" };\n if (isHigherLevelHeading || isSameLevelHeading)\n return { kind: \"finishCurrentNode\" };\n if (isHeadingOutsideThisTree) return { kind: \"exitSection\" };\n return { kind: \"appendDetail\" };\n}\n\n/**\n * The entry point for parsing a document section defined as a `Tree`.\n *\n * It searches for the first occurrence of the root node style to begin parsing.\n * Text preceding the first valid root node is ignored (orphan text).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the tree schema.\n * @param allNodeTitleStyles - A set of all valid styles in this tree (for boundary detection).\n * @returns An array of parsed tree nodes.\n */\nexport function parseTreeSection(\n cursor: ParagraphCursor,\n section: Section,\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const treeContent = section.content;\n const nodeSchema =\n treeContent?.kind === \"tree\" ? treeContent.node : undefined;\n if (!treeContent || !nodeSchema) return [];\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { paragraph, style } = info;\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) break;\n\n if (hasNamedStyle(paragraph, nodeSchema.title.namedStyleType)) {\n return parseTreeNode(cursor, nodeSchema, [], allNodeTitleStyles);\n }\n\n cursor.getNextParagraph();\n }\n return [];\n}\n\n/**\n * Recursively parses a specific level of the tree structure.\n *\n * Handles creation of current nodes, delegating to child parsers for nested content,\n * and accumulating list details.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - Stack of ancestors to detect hierarchy boundaries.\n * @param allNodeTitleStyles - Set of all valid styles for boundary checks.\n * @returns An array of parsed nodes for this level.\n */\nexport function parseTreeNode(\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[],\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const result: unknown[] = [];\n let currentNode: Record<string, unknown> | null = null;\n\n const childNodeSchema =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { text, paragraph, style } = info;\n\n if (cursor.isAtNewSection()) {\n return result;\n }\n\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) {\n return result;\n }\n\n const decision = determineTreeParsingAction(\n paragraph,\n cursor,\n nodeSchema,\n ancestorNodeList\n );\n\n switch (decision.kind) {\n case \"exitSection\": {\n return result;\n }\n\n case \"createNode\": {\n currentNode = createNodeFromTitle(text, nodeSchema.title);\n result.push(currentNode);\n cursor.getNextParagraph();\n break;\n }\n\n case \"startChildNode\": {\n if (\n !currentNode ||\n !Array.isArray(currentNode[CONTENT_KEY]) ||\n !childNodeSchema\n ) {\n cursor.getNextParagraph();\n break;\n }\n const children = parseTreeNode(\n cursor,\n childNodeSchema,\n [nodeSchema, ...ancestorNodeList],\n allNodeTitleStyles\n );\n (currentNode[CONTENT_KEY] as unknown[]).push(...children);\n break;\n }\n\n case \"finishCurrentNode\": {\n if (currentNode) return result;\n cursor.getNextParagraph();\n break;\n }\n\n case \"appendDetail\": {\n if (nodeSchema.content?.kind === \"tree\") {\n cursor.getNextParagraph();\n break;\n }\n if (currentNode && Array.isArray(currentNode[CONTENT_KEY])) {\n (currentNode[CONTENT_KEY] as unknown[]).push(text.trim());\n }\n cursor.getNextParagraph();\n break;\n }\n }\n }\n\n return result;\n}\n","import { Section } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseStructuredText } from \"./utils\";\n\n/**\n * Parses a section defined as a 'List'.\n *\n * This function iterates through paragraphs, parsing each line according to the\n * section's content schema (e.g., delimiters, keys). It supports both flat lists\n * and nested structures depending on the `isFlatten` configuration.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the list schema.\n * @returns An array of parsed items (strings, objects, or arrays).\n */\nexport function parseListSection(\n cursor: ParagraphCursor,\n section: Section\n): unknown[] {\n const result: unknown[] = [];\n if (!section.content || section.content.kind !== \"list\") {\n return [];\n }\n const contentSchema = section.content;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n const parsed = parseStructuredText(info.text, contentSchema);\n\n if (contentSchema.isFlatten && Array.isArray(parsed)) {\n result.push(...parsed);\n } else {\n result.push(parsed);\n }\n cursor.getNextParagraph();\n }\n return result;\n}\n","import { ParagraphCursor } from \"./cursor\";\n\n/**\n * Parses a continuous block of text paragraphs into a single string.\n *\n * This function iterates through paragraphs starting from the current cursor position\n * and collects text until a boundary is encountered.\n *\n * **Stop Conditions:**\n * 1. **New Section:** The cursor reaches a paragraph that marks the start of a new section defined in the schema.\n * 2. **Heading:** The cursor reaches any paragraph with a named heading style (e.g., HEADING_1, HEADING_2).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @returns The combined text content of the block, joined by spaces.\n */\nexport function parseTextBlockSection(cursor: ParagraphCursor): string {\n const textPartList: string[] = [];\n\n while (!cursor.isEndOfDocument()) {\n const paragraph = cursor.getCurrentParagraph();\n if (!paragraph) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n textPartList.push(paragraph.text);\n cursor.getNextParagraph();\n }\n return textPartList.join(\" \");\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, Section, NamedStyleType } from \"./types\";\nimport { hasNamedStyle } from \"./utils\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseTreeSection, collectNodeStylesRecursive } from \"./tree\";\nimport { parseListSection } from \"./list\";\nimport { parseTextBlockSection } from \"./textBlock\";\n\n/**\n * Identifies if a paragraph corresponds to a section title defined in the schema.\n *\n * Matching Logic:\n * 1. Matches the paragraph's named style (e.g., HEADING_2) with the section's style.\n * 2. Matches the text content (case-insensitive, trimmed).\n *\n * @param paragraph - The current paragraph being inspected.\n * @param text - The extracted text content of the paragraph.\n * @param parseSchema - The global parsing schema containing section definitions.\n * @returns The canonical name of the section if found, otherwise `null`.\n */\nexport function getSectionTitle(\n paragraph: docs_v1.Schema$Paragraph,\n text: string,\n parseSchema: ParseSchema\n): string | null {\n const normalized = text.trim().toLowerCase();\n const sectionList = parseSchema.sections ?? [];\n\n for (const section of sectionList) {\n const { name, namedStyleType } = section.title;\n if (name && namedStyleType) {\n const styleMatches = hasNamedStyle(paragraph, namedStyleType);\n const textMatches = normalized === name.trim().toLowerCase();\n if (styleMatches && textMatches) {\n return name;\n }\n }\n }\n return null;\n}\n\n/**\n * Dispatches the parsing logic to the appropriate handler based on the section's content type.\n *\n * - **Tree:** Delegates to `parseTreeSection` for hierarchical data.\n * - **List:** Delegates to `parseListSection` for flat lists.\n * - **TextBlock (Default):** Delegates to `parseTextBlockSection` if no content structure is defined.\n *\n * @param cursor - The paragraph cursor.\n * @param section - The section definition containing the content schema.\n * @returns The parsed result (String, Array, or Object depending on the type).\n */\nexport function parseSectionContent(\n cursor: ParagraphCursor,\n section: Section\n): unknown {\n const content = section.content;\n\n if (!content) {\n return parseTextBlockSection(cursor);\n }\n\n switch (content.kind) {\n case \"tree\": {\n if (!content.node) {\n return [];\n }\n const allNodeTitleStyles = new Set<NamedStyleType>();\n collectNodeStylesRecursive(content.node, allNodeTitleStyles);\n return parseTreeSection(cursor, section, allNodeTitleStyles);\n }\n case \"list\":\n return parseListSection(cursor, section);\n default:\n return parseTextBlockSection(cursor);\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, NamedStyleType } from \"./types\";\nimport {\n getParagraphNamedStyleType,\n isNamedStyleType,\n extractParagraphText,\n} from \"./utils\";\nimport { getSectionTitle } from \"./section\";\n\n/**\n * Represents a processed paragraph with extracted text and normalized style.\n */\nexport interface Paragraph {\n text: string;\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined;\n paragraph: docs_v1.Schema$Paragraph;\n}\n\n/**\n * Extracts and normalizes data from a raw Google Docs paragraph.\n *\n * @param paragraph - The raw paragraph object from the API.\n * @returns A `Paragraph` object if text exists, otherwise `null` (e.g., empty lines).\n */\nexport function getParagraph(\n paragraph: docs_v1.Schema$Paragraph\n): Paragraph | null {\n const text = extractParagraphText(paragraph);\n if (!text) return null;\n const style = getParagraphNamedStyleType(paragraph);\n return { text, style, paragraph };\n}\n\n/**\n * A stateful cursor for traversing a list of Google Docs paragraphs.\n *\n * It manages the current position (index) and provides methods to inspect\n * the current paragraph's context (e.g., style, section boundaries)\n * without manually handling array indices.\n */\nexport class ParagraphCursor {\n private index = 0;\n\n constructor(\n private paragraphList: docs_v1.Schema$Paragraph[],\n private parseSchema: ParseSchema\n ) {}\n\n /**\n * Retrieves the paragraph at the current cursor position.\n *\n * @returns The current `Paragraph` object, or `null` if the cursor is at the end of the document or the line is empty.\n */\n getCurrentParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n const paragraph = this.paragraphList[this.index];\n if (!paragraph) return null;\n return getParagraph(paragraph);\n }\n\n /**\n * Advances the cursor to the next position and returns the new paragraph.\n *\n * @returns The next `Paragraph` object, or `null` if the end of the document is reached.\n */\n getNextParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n this.index++;\n return this.getCurrentParagraph();\n }\n\n /**\n * Checks if the cursor has reached the end of the paragraph list.\n */\n isEndOfDocument(): boolean {\n return this.index >= this.paragraphList.length;\n }\n\n /**\n * Determines if the current paragraph corresponds to a section title defined in the schema.\n *\n * @returns The section name if matched, otherwise `null`.\n */\n getCurrentSectionTitle(): string | null {\n const info = this.getCurrentParagraph();\n if (!info) return null;\n return getSectionTitle(info.paragraph, info.text, this.parseSchema);\n }\n\n /**\n * Checks if the current cursor position marks the start of a new section.\n */\n isAtNewSection(): boolean {\n return this.getCurrentSectionTitle() !== null;\n }\n\n /**\n * Checks if the current paragraph has a named heading style (e.g., HEADING_1).\n */\n isAtParagraphHeading(): boolean {\n const info = this.getCurrentParagraph();\n return !!info && isNamedStyleType(info.style);\n }\n}\n","import { google, docs_v1 } from \"googleapis\";\nimport { GoogleAuth } from \"google-auth-library\";\n\n/**\n * Creates and configures a Google Docs API client instance.\n *\n * This function handles authentication using `GoogleAuth` with the read-only scope\n * and initializes the `googleapis` Docs service with version 'v1'.\n *\n * @returns An initialized `docs_v1.Docs` client ready for API calls.\n * @throws {Error} If client initialization fails (e.g., missing credentials or configuration errors).\n */\nexport function createDocsClient(): docs_v1.Docs {\n try {\n const auth = new GoogleAuth({\n scopes: [\"https://www.googleapis.com/auth/documents.readonly\"],\n });\n\n return google.docs({\n version: \"v1\",\n auth,\n });\n } catch (error) {\n console.error(\"Error initializing Google Docs client:\", error);\n throw new Error(\n \"Failed to initialize Google Docs client. Check setup and credentials.\"\n );\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParsedDocument, ParseSchema, GetParsedType } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseSectionContent } from \"./section\";\nimport { createDocsClient } from \"./auth\";\n\n/**\n * Parses the raw Google Docs document content according to the provided schema.\n *\n * This function converts the document into a stream of valid paragraphs and\n * uses a cursor to navigate and parse sections based on the schema definition.\n *\n * @param doc - The raw Google Docs document object.\n * @param parseSchema - The schema defining the structure of sections to parse.\n * @returns An object representing the parsed document content.\n */\nfunction parseDocument(\n doc: docs_v1.Schema$Document,\n parseSchema: ParseSchema\n): ParsedDocument {\n const content = doc.body?.content || [];\n const result: ParsedDocument = {};\n\n const validParagraphList = content\n .map((element) => element.paragraph)\n .filter((paragraph): paragraph is docs_v1.Schema$Paragraph => !!paragraph);\n\n const cursor = new ParagraphCursor(validParagraphList, parseSchema);\n\n while (!cursor.isEndOfDocument()) {\n const currentSectionTitle = cursor.getCurrentSectionTitle();\n if (currentSectionTitle) {\n const section = parseSchema.sections.find(\n (s) => s.title.name === currentSectionTitle\n );\n if (section) {\n cursor.getNextParagraph();\n const parsedData = parseSectionContent(cursor, section);\n result[currentSectionTitle] = parsedData;\n continue;\n }\n }\n cursor.getNextParagraph();\n }\n\n return result;\n}\n\n/**\n * Public API: Fetches and parses a Google Doc by its ID.\n *\n * This function handles authentication, API communication, and error wrapping.\n * It returns a fully typed object based on the provided schema generic `T`.\n *\n * @template T - The type of the ParseSchema, allowing for type inference of the result.\n * @param documentId - The unique ID of the Google Doc to parse.\n * @param parseSchema - The schema definition used to guide the parsing process.\n * @returns A promise resolving to the parsed document data.\n * @throws Will throw a descriptive error if the API call fails or returns an empty response.\n */\nexport async function getParsedDocument<T extends ParseSchema>(\n documentId: string,\n parseSchema: T\n): Promise<GetParsedType<T>> {\n try {\n const docs = createDocsClient();\n const response = await docs.documents.get({ documentId });\n if (!response.data) {\n throw new Error(\"Empty document response from Google Docs API.\");\n }\n\n const parsedDocument = parseDocument(response.data, parseSchema);\n return parsedDocument as GetParsedType<T>;\n } catch (e) {\n throw new Error(\n `Google Docs API call failed. Check Doc ID and Service Account permissions. Original error: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/tree.ts","../src/list.ts","../src/textBlock.ts","../src/section.ts","../src/cursor.ts","../src/auth.ts","../src/parser.ts"],"names":["GoogleAuth","google"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * A constant tuple of supported Google Docs named styles that represent headings or titles.\n * These are used to identify structural nodes and section boundaries within the document tree.\n */\nexport const VALID_NAMED_STYLES = [\n \"HEADING_1\",\n \"HEADING_2\",\n \"HEADING_3\",\n \"HEADING_4\",\n \"HEADING_5\",\n \"HEADING_6\",\n \"TITLE\",\n \"SUBTITLE\",\n] as const;\n\n/**\n * A union type representing any one of the valid named styles defined in `VALID_NAMED_STYLES`.\n */\nexport type ValidNamedStyle = (typeof VALID_NAMED_STYLES)[number];\n\n/**\n * A Set containing the valid named styles for efficient O(1) validation checks.\n * This is primarily used in type guards to verify if a style string is a valid heading.\n */\nexport const VALID_NAMED_STYLES_SET = new Set<string>(VALID_NAMED_STYLES);\n","import { docs_v1 } from \"googleapis\";\nimport { Schema, NamedStyleType } from \"./types\";\nimport { VALID_NAMED_STYLES_SET } from \"./constants\";\n\n/**\n * Extracts pure text content from a Google Docs Paragraph.\n *\n * - Joins content from all `textRun` elements.\n * - Trims leading and trailing whitespace.\n * - Normalizes newline characters (`\\n`) to spaces.\n *\n * @param paragraph - The Google Docs paragraph object.\n * @returns The extracted and cleaned text string. Returns an empty string if the paragraph is empty.\n */\nexport function extractParagraphText(\n paragraph: docs_v1.Schema$Paragraph\n): string {\n const elements = paragraph.elements ?? [];\n\n const text = elements\n .map((el: docs_v1.Schema$ParagraphElement) => el.textRun?.content || \"\")\n .join(\"\")\n .trim()\n .replace(/\\n/g, \" \");\n\n return text || \"\";\n}\n\n/**\n * Checks if a paragraph has a specific `namedStyleType` (e.g., HEADING_1).\n *\n * @param paragraph - The paragraph to check.\n * @param namedStyleType - The style type to compare against.\n * @returns `true` if the paragraph's style matches the provided type; otherwise `false`.\n */\nexport function hasNamedStyle(\n paragraph: docs_v1.Schema$Paragraph,\n namedStyleType?: NamedStyleType\n): boolean {\n if (!namedStyleType) return false;\n const style = paragraph.paragraphStyle?.namedStyleType;\n return style === namedStyleType;\n}\n\n/**\n * Retrieves the named style type of a paragraph, handling default behaviors and edge cases.\n *\n * Resolution Logic:\n * 1. Returns `undefined` if the paragraph has no style and no content (completely empty).\n * 2. Returns `\"NORMAL_TEXT\"` if the paragraph has content but no explicit style (Google Docs default).\n * 3. Returns `\"NORMAL_TEXT\"` if the style object exists but `namedStyleType` is missing.\n * 4. Returns the `namedStyleType` if it exists in the valid set (`VALID_NAMED_STYLES`).\n * 5. Returns `undefined` if the style is invalid or unknown.\n *\n * @param paragraph - The paragraph to analyze.\n * @returns The valid `NamedStyleType`, `\"NORMAL_TEXT\"`, or `undefined`.\n */\nexport function getParagraphNamedStyleType(\n paragraph: docs_v1.Schema$Paragraph\n): NamedStyleType | \"NORMAL_TEXT\" | undefined {\n if (!paragraph.paragraphStyle && !paragraph.elements?.length) {\n return undefined;\n }\n if (!paragraph.paragraphStyle) {\n return \"NORMAL_TEXT\";\n }\n\n const namedStyleType = paragraph.paragraphStyle.namedStyleType;\n\n if (!namedStyleType) {\n return \"NORMAL_TEXT\";\n }\n\n if (VALID_NAMED_STYLES_SET.has(namedStyleType)) {\n return namedStyleType as NamedStyleType;\n }\n\n return undefined;\n}\n\n/**\n * Type Guard to check if a style is a valid Heading style (HEADING_1~6, TITLE, SUBTITLE).\n *\n * - Returns `false` for `\"NORMAL_TEXT\"`.\n * - Returns `false` for undefined or invalid strings.\n *\n * @param style - The style string to check.\n * @returns `true` if the style is a valid `NamedStyleType`.\n */\nexport function isNamedStyleType(\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined\n): style is NamedStyleType {\n if (typeof style !== \"string\") return false;\n if (style === \"NORMAL_TEXT\") return false;\n\n return VALID_NAMED_STYLES_SET.has(style);\n}\n\n/**\n * Splits a string by a delimiter and trims whitespace from each item.\n *\n * @param text - The string to split.\n * @param delimiter - The character to split by (e.g., \",\").\n * @param filterEmpty - If `true`, removes empty strings from the result. Defaults to `false`.\n * @returns An array of trimmed strings.\n */\nexport function splitAndTrim(\n text: string,\n delimiter: string,\n filterEmpty = false\n): string[] {\n if (text === \"\") {\n return [];\n }\n const items = text.split(delimiter).map((t) => t.trim());\n return filterEmpty ? items.filter((t) => t.length > 0) : items;\n}\n\n/**\n * Parses text into a Key-Value list structure.\n *\n * Format: \"Key: Value1, Value2\"\n *\n * @param text - The text to parse.\n * @param keyDelimiter - The separator between key and values (e.g., \":\").\n * @param listDelimiter - The separator between list items (e.g., \",\").\n * @returns An object `{ key, value[] }` if parsing succeeds, or the original text string if the key delimiter is missing.\n */\nexport function parseToKeyedList(\n text: string,\n keyDelimiter: string,\n listDelimiter: string\n): { key: string; value: string[] } | string {\n const delimiterIndex = text.indexOf(keyDelimiter);\n\n if (delimiterIndex <= 0) {\n return text;\n }\n\n const key = text.substring(0, delimiterIndex).trim();\n const valuePart = text.substring(delimiterIndex + keyDelimiter.length).trim();\n\n const value = valuePart ? splitAndTrim(valuePart, listDelimiter, true) : [];\n\n return { key, value };\n}\n\n/**\n * Maps delimited text values to a list of keys by position.\n *\n * Format: \"Value1 | Value2 | Value3\" -> { Key1: \"Value1\", Key2: \"Value2\", ... }\n *\n * @param text - The text to parse.\n * @param keys - The list of field names (keys) to map values to.\n * @param delimiter - The separator between values (e.g., \"|\").\n * @returns A record object mapping keys to values. Missing values are filled with empty strings.\n */\nexport function parseToFields(\n text: string,\n keys: readonly string[],\n delimiter: string\n): Record<string, unknown> {\n const values = splitAndTrim(text, delimiter, false);\n\n return keys.reduce((acc, key, index) => {\n const value = values[index];\n acc[key] = value !== undefined && value !== \"\" ? value : \"\";\n return acc;\n }, {} as Record<string, unknown>);\n}\n\n/**\n * Parses a simple delimited string into an array, filtering out empty items.\n *\n * Example: \"Apple, , Banana\" -> [\"Apple\", \"Banana\"]\n *\n * @param text - The text to parse.\n * @param delimiter - The separator (e.g., \",\").\n * @returns An array of non-empty strings.\n */\nexport function parseDelimitedList(text: string, delimiter: string): string[] {\n const values = text\n .split(delimiter)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n return values;\n}\n\n/**\n * Parses a single line of text into structured data based on the provided Schema.\n *\n * **Parsing Priorities:**\n * 1. **Keyed List**: If `schema.keyDelimiter` is set. (e.g., \"Team: A, B\")\n * 2. **Fixed Fields**: If `schema.keys` is set. (e.g., \"Google | Engineer | 2023\")\n * 3. **Default List**: Fallback to simple delimited list. (e.g., \"A, B, C\")\n *\n * @param text - The text content to parse.\n * @param schema - The schema object defining the parsing rules.\n * @returns The parsed result as an object, array, or string depending on the schema.\n */\nexport function parseStructuredText(\n text: string,\n schema: Schema\n): Record<string, unknown> | string[] | string {\n const delimiter = schema.delimiter || \",\";\n\n if (schema.keyDelimiter) {\n return parseToKeyedList(text, schema.keyDelimiter, delimiter);\n }\n\n if (schema.keys && schema.keys.length > 0) {\n return parseToFields(text, schema.keys, delimiter);\n }\n\n return parseDelimitedList(text, delimiter);\n}\n","import { docs_v1 } from \"googleapis\";\nimport { NamedStyleType, Node, Section, Schema } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { hasNamedStyle, parseStructuredText } from \"./utils\";\n\nconst CONTENT_KEY = \"content\";\n\n/**\n * Recursively collects all `NamedStyleType`s defined within a nested `Node` schema.\n *\n * This is used to build a set of allowed heading styles for the parser to recognize\n * valid tree nodes and detect boundaries.\n *\n * @param node - The current node schema to traverse.\n * @param set - The Set to populate with collected style types.\n */\nexport function collectNodeStylesRecursive(\n node: Node,\n set: Set<NamedStyleType>\n): void {\n if (node.title.namedStyleType) set.add(node.title.namedStyleType);\n if (node.content && node.content.kind === \"tree\") {\n collectNodeStylesRecursive(node.content.node, set);\n }\n}\n\n/**\n * Creates a normalized node object from a raw title string based on the provided schema.\n *\n * - If the schema defines parsing rules (delimiters/keys), the title is parsed into\n * structured data (Array or Object).\n * - If no parsing rules exist, the title is treated as a simple string.\n *\n * @param text - The raw text of the title/heading.\n * @param titleSchema - The schema defining how to parse the title.\n * @returns An object containing the parsed `title` and an empty `content` array.\n */\nexport function createNodeFromTitle(\n text: string,\n titleSchema: Schema\n): Record<\"title\" | \"content\", unknown> {\n const hasDelimiterSchema =\n !!titleSchema.keyDelimiter ||\n (!!titleSchema.keys && titleSchema.keys.length > 0) ||\n !!titleSchema.delimiter;\n\n if (hasDelimiterSchema) {\n const structuredText = parseStructuredText(text, titleSchema);\n if (Array.isArray(structuredText)) {\n return { title: structuredText, content: [] };\n } else if (typeof structuredText === \"object\" && structuredText !== null) {\n return { title: structuredText, content: [] };\n } else {\n return { title: text, content: [] };\n }\n } else {\n return { title: text, content: [] };\n }\n}\n\n/**\n * Represents the decision made by the parser for the current paragraph.\n */\nexport type TreeParsingAction =\n | { kind: \"exitSection\" }\n | { kind: \"createNode\" }\n | { kind: \"startChildNode\" }\n | { kind: \"finishCurrentNode\" }\n | { kind: \"appendDetail\" };\n\n/**\n * Determines the next action to take based on the current paragraph's style\n * and its relationship to the current node hierarchy.\n *\n * Decision Logic Priorities:\n * 1. New Section -> Exit.\n * 2. Matches Current Node Title -> Create Sibling Node.\n * 3. Matches Child Node Title -> Start Child Node (Recursion).\n * 4. Matches Ancestor/Same Level Heading -> Finish Current Node (Pop Stack).\n * 5. Heading outside this tree -> Exit.\n * 6. Otherwise -> Append as detail content.\n *\n * @param paragraph - The current paragraph being inspected.\n * @param cursor - The paragraph cursor.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - The stack of ancestor nodes for context.\n * @returns The determining parsing action.\n */\nexport function determineTreeParsingAction(\n paragraph: docs_v1.Schema$Paragraph,\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[]\n): TreeParsingAction {\n const nodeTitleStyle = nodeSchema.title.namedStyleType;\n const childNode =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n const isCurrentNodeTitle = hasNamedStyle(paragraph, nodeTitleStyle);\n const isChildNodeTitle =\n !!childNode && hasNamedStyle(paragraph, childNode.title.namedStyleType);\n const isAncestorNodeTitle = ancestorNodeList.some((a) =>\n hasNamedStyle(paragraph, a.title.namedStyleType)\n );\n\n const isInThisTree =\n isCurrentNodeTitle || isChildNodeTitle || isAncestorNodeTitle;\n\n const isHeading = cursor.isAtParagraphHeading();\n const isAtNewSection = cursor.isAtNewSection();\n const isHeadingOutsideThisTree = isHeading && !isInThisTree;\n const isHigherLevelHeading = isHeading && isAncestorNodeTitle;\n const isSameLevelHeading = isHeading && isCurrentNodeTitle;\n\n if (isAtNewSection) return { kind: \"exitSection\" };\n if (isCurrentNodeTitle) return { kind: \"createNode\" };\n if (isChildNodeTitle) return { kind: \"startChildNode\" };\n if (isHigherLevelHeading || isSameLevelHeading)\n return { kind: \"finishCurrentNode\" };\n if (isHeadingOutsideThisTree) return { kind: \"exitSection\" };\n return { kind: \"appendDetail\" };\n}\n\n/**\n * The entry point for parsing a document section defined as a `Tree`.\n *\n * It searches for the first occurrence of the root node style to begin parsing.\n * Text preceding the first valid root node is ignored (orphan text).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the tree schema.\n * @param allNodeTitleStyles - A set of all valid styles in this tree (for boundary detection).\n * @returns An array of parsed tree nodes.\n */\nexport function parseTreeSection(\n cursor: ParagraphCursor,\n section: Section,\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const treeContent = section.content;\n const nodeSchema =\n treeContent?.kind === \"tree\" ? treeContent.node : undefined;\n if (!treeContent || !nodeSchema) return [];\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { paragraph, style } = info;\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) break;\n\n if (hasNamedStyle(paragraph, nodeSchema.title.namedStyleType)) {\n return parseTreeNode(cursor, nodeSchema, [], allNodeTitleStyles);\n }\n\n cursor.getNextParagraph();\n }\n return [];\n}\n\n/**\n * Recursively parses a specific level of the tree structure.\n *\n * Handles creation of current nodes, delegating to child parsers for nested content,\n * and accumulating list details.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - Stack of ancestors to detect hierarchy boundaries.\n * @param allNodeTitleStyles - Set of all valid styles for boundary checks.\n * @returns An array of parsed nodes for this level.\n */\nexport function parseTreeNode(\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[],\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const result: unknown[] = [];\n let currentNode: Record<string, unknown> | null = null;\n\n const childNodeSchema =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { text, paragraph, style } = info;\n\n if (cursor.isAtNewSection()) {\n return result;\n }\n\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) {\n return result;\n }\n\n const decision = determineTreeParsingAction(\n paragraph,\n cursor,\n nodeSchema,\n ancestorNodeList\n );\n\n switch (decision.kind) {\n case \"exitSection\": {\n return result;\n }\n\n case \"createNode\": {\n currentNode = createNodeFromTitle(text, nodeSchema.title);\n result.push(currentNode);\n cursor.getNextParagraph();\n break;\n }\n\n case \"startChildNode\": {\n if (\n !currentNode ||\n !Array.isArray(currentNode[CONTENT_KEY]) ||\n !childNodeSchema\n ) {\n cursor.getNextParagraph();\n break;\n }\n const children = parseTreeNode(\n cursor,\n childNodeSchema,\n [nodeSchema, ...ancestorNodeList],\n allNodeTitleStyles\n );\n (currentNode[CONTENT_KEY] as unknown[]).push(...children);\n break;\n }\n\n case \"finishCurrentNode\": {\n if (currentNode) return result;\n cursor.getNextParagraph();\n break;\n }\n\n case \"appendDetail\": {\n if (nodeSchema.content?.kind === \"tree\") {\n cursor.getNextParagraph();\n break;\n }\n if (currentNode && Array.isArray(currentNode[CONTENT_KEY])) {\n (currentNode[CONTENT_KEY] as unknown[]).push(text.trim());\n }\n cursor.getNextParagraph();\n break;\n }\n }\n }\n\n return result;\n}\n","import { Section } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseStructuredText } from \"./utils\";\n\n/**\n * Parses a section defined as a 'List'.\n *\n * This function iterates through paragraphs, parsing each line according to the\n * section's content schema (e.g., delimiters, keys). It supports both flat lists\n * and nested structures depending on the `isFlatten` configuration.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the list schema.\n * @returns An array of parsed items (strings, objects, or arrays).\n */\nexport function parseListSection(\n cursor: ParagraphCursor,\n section: Section\n): unknown[] {\n const result: unknown[] = [];\n if (!section.content || section.content.kind !== \"list\") {\n return [];\n }\n const contentSchema = section.content;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n const parsed = parseStructuredText(info.text, contentSchema);\n\n if (contentSchema.isFlatten && Array.isArray(parsed)) {\n result.push(...parsed);\n } else {\n result.push(parsed);\n }\n cursor.getNextParagraph();\n }\n return result;\n}\n","import { ParagraphCursor } from \"./cursor\";\n\n/**\n * Parses a continuous block of text paragraphs into a single string.\n *\n * This function iterates through paragraphs starting from the current cursor position\n * and collects text until a boundary is encountered.\n *\n * **Stop Conditions:**\n * 1. **New Section:** The cursor reaches a paragraph that marks the start of a new section defined in the schema.\n * 2. **Heading:** The cursor reaches any paragraph with a named heading style (e.g., HEADING_1, HEADING_2).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @returns The combined text content of the block, joined by spaces.\n */\nexport function parseTextBlockSection(cursor: ParagraphCursor): string {\n const textPartList: string[] = [];\n\n while (!cursor.isEndOfDocument()) {\n const paragraph = cursor.getCurrentParagraph();\n if (!paragraph) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n textPartList.push(paragraph.text);\n cursor.getNextParagraph();\n }\n return textPartList.join(\" \");\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, Section, NamedStyleType } from \"./types\";\nimport { hasNamedStyle } from \"./utils\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseTreeSection, collectNodeStylesRecursive } from \"./tree\";\nimport { parseListSection } from \"./list\";\nimport { parseTextBlockSection } from \"./textBlock\";\n\n/**\n * Identifies if a paragraph corresponds to a section title defined in the schema.\n *\n * Matching Logic:\n * 1. Matches the paragraph's named style (e.g., HEADING_2) with the section's style.\n * 2. Matches the text content (case-insensitive, trimmed).\n *\n * @param paragraph - The current paragraph being inspected.\n * @param text - The extracted text content of the paragraph.\n * @param parseSchema - The global parsing schema containing section definitions.\n * @returns The canonical name of the section if found, otherwise `null`.\n */\nexport function getSectionTitle(\n paragraph: docs_v1.Schema$Paragraph,\n text: string,\n parseSchema: ParseSchema\n): string | null {\n const normalized = text.trim().toLowerCase();\n const sectionList = parseSchema.sections ?? [];\n\n for (const section of sectionList) {\n const { name, namedStyleType } = section.title;\n if (name && namedStyleType) {\n const styleMatches = hasNamedStyle(paragraph, namedStyleType);\n const textMatches = normalized === name.trim().toLowerCase();\n if (styleMatches && textMatches) {\n return name;\n }\n }\n }\n return null;\n}\n\n/**\n * Dispatches the parsing logic to the appropriate handler based on the section's content type.\n *\n * - **Tree:** Delegates to `parseTreeSection` for hierarchical data.\n * - **List:** Delegates to `parseListSection` for flat lists.\n * - **TextBlock (Default):** Delegates to `parseTextBlockSection` if no content structure is defined.\n *\n * @param cursor - The paragraph cursor.\n * @param section - The section definition containing the content schema.\n * @returns The parsed result (String, Array, or Object depending on the type).\n */\nexport function parseSectionContent(\n cursor: ParagraphCursor,\n section: Section\n): unknown {\n const content = section.content;\n\n if (!content) {\n return parseTextBlockSection(cursor);\n }\n\n switch (content.kind) {\n case \"tree\": {\n if (!content.node) {\n return [];\n }\n const allNodeTitleStyles = new Set<NamedStyleType>();\n collectNodeStylesRecursive(content.node, allNodeTitleStyles);\n return parseTreeSection(cursor, section, allNodeTitleStyles);\n }\n case \"list\":\n return parseListSection(cursor, section);\n default:\n return parseTextBlockSection(cursor);\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, NamedStyleType } from \"./types\";\nimport {\n getParagraphNamedStyleType,\n isNamedStyleType,\n extractParagraphText,\n} from \"./utils\";\nimport { getSectionTitle } from \"./section\";\n\n/**\n * Represents a processed paragraph with extracted text and normalized style.\n */\nexport interface Paragraph {\n text: string;\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined;\n paragraph: docs_v1.Schema$Paragraph;\n}\n\n/**\n * Extracts and normalizes data from a raw Google Docs paragraph.\n *\n * @param paragraph - The raw paragraph object from the API.\n * @returns A `Paragraph` object if text exists, otherwise `null` (e.g., empty lines).\n */\nexport function getParagraph(\n paragraph: docs_v1.Schema$Paragraph\n): Paragraph | null {\n const text = extractParagraphText(paragraph);\n if (!text) return null;\n const style = getParagraphNamedStyleType(paragraph);\n return { text, style, paragraph };\n}\n\n/**\n * A stateful cursor for traversing a list of Google Docs paragraphs.\n *\n * It manages the current position (index) and provides methods to inspect\n * the current paragraph's context (e.g., style, section boundaries)\n * without manually handling array indices.\n */\nexport class ParagraphCursor {\n private index = 0;\n\n constructor(\n private paragraphList: docs_v1.Schema$Paragraph[],\n private parseSchema: ParseSchema\n ) {}\n\n /**\n * Retrieves the paragraph at the current cursor position.\n *\n * @returns The current `Paragraph` object, or `null` if the cursor is at the end of the document or the line is empty.\n */\n getCurrentParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n const paragraph = this.paragraphList[this.index];\n if (!paragraph) return null;\n return getParagraph(paragraph);\n }\n\n /**\n * Advances the cursor to the next position and returns the new paragraph.\n *\n * @returns The next `Paragraph` object, or `null` if the end of the document is reached.\n */\n getNextParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n this.index++;\n return this.getCurrentParagraph();\n }\n\n /**\n * Checks if the cursor has reached the end of the paragraph list.\n */\n isEndOfDocument(): boolean {\n return this.index >= this.paragraphList.length;\n }\n\n /**\n * Determines if the current paragraph corresponds to a section title defined in the schema.\n *\n * @returns The section name if matched, otherwise `null`.\n */\n getCurrentSectionTitle(): string | null {\n const info = this.getCurrentParagraph();\n if (!info) return null;\n return getSectionTitle(info.paragraph, info.text, this.parseSchema);\n }\n\n /**\n * Checks if the current cursor position marks the start of a new section.\n */\n isAtNewSection(): boolean {\n return this.getCurrentSectionTitle() !== null;\n }\n\n /**\n * Checks if the current paragraph has a named heading style (e.g., HEADING_1).\n */\n isAtParagraphHeading(): boolean {\n const info = this.getCurrentParagraph();\n return !!info && isNamedStyleType(info.style);\n }\n}\n","import { google, docs_v1 } from \"googleapis\";\nimport { GoogleAuth } from \"google-auth-library\";\n\n/**\n * Creates and configures a Google Docs API client instance.\n *\n * This function handles authentication using `GoogleAuth` with the read-only scope\n * and initializes the `googleapis` Docs service with version 'v1'.\n *\n * The function automatically detects the credential format from GOOGLE_APPLICATION_CREDENTIALS:\n * - If it's a JSON string (starts with '{'): Parses and uses it directly\n * - If it's a file path: Lets GoogleAuth handle it automatically by passing undefined\n *\n * @returns An initialized `docs_v1.Docs` client ready for API calls.\n * @throws {Error} If client initialization fails (e.g., missing credentials or configuration errors).\n */\nexport function createDocsClient(): docs_v1.Docs {\n try {\n const credentialsEnv = process.env.GOOGLE_APPLICATION_CREDENTIALS;\n let credentials;\n\n if (credentialsEnv?.trim().startsWith(\"{\")) {\n credentials = JSON.parse(credentialsEnv);\n }\n\n const auth = new GoogleAuth({\n credentials,\n scopes: [\"https://www.googleapis.com/auth/documents.readonly\"],\n });\n\n return google.docs({\n version: \"v1\",\n auth,\n });\n } catch (error) {\n console.error(\"Error initializing Google Docs client:\", error);\n throw new Error(\n \"Failed to initialize Google Docs client. Check setup and credentials.\"\n );\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParsedDocument, ParseSchema, GetParsedType } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseSectionContent } from \"./section\";\nimport { createDocsClient } from \"./auth\";\n\n/**\n * Parses the raw Google Docs document content according to the provided schema.\n *\n * This function converts the document into a stream of valid paragraphs and\n * uses a cursor to navigate and parse sections based on the schema definition.\n *\n * @param doc - The raw Google Docs document object.\n * @param parseSchema - The schema defining the structure of sections to parse.\n * @returns An object representing the parsed document content.\n */\nfunction parseDocument(\n doc: docs_v1.Schema$Document,\n parseSchema: ParseSchema\n): ParsedDocument {\n const content = doc.body?.content || [];\n const result: ParsedDocument = {};\n\n const validParagraphList = content\n .map((element) => element.paragraph)\n .filter((paragraph): paragraph is docs_v1.Schema$Paragraph => !!paragraph);\n\n const cursor = new ParagraphCursor(validParagraphList, parseSchema);\n\n while (!cursor.isEndOfDocument()) {\n const currentSectionTitle = cursor.getCurrentSectionTitle();\n if (currentSectionTitle) {\n const section = parseSchema.sections.find(\n (s) => s.title.name === currentSectionTitle\n );\n if (section) {\n cursor.getNextParagraph();\n const parsedData = parseSectionContent(cursor, section);\n result[currentSectionTitle] = parsedData;\n continue;\n }\n }\n cursor.getNextParagraph();\n }\n\n return result;\n}\n\n/**\n * Public API: Fetches and parses a Google Doc by its ID.\n *\n * This function handles authentication, API communication, and error wrapping.\n * It returns a fully typed object based on the provided schema generic `T`.\n *\n * @template T - The type of the ParseSchema, allowing for type inference of the result.\n * @param documentId - The unique ID of the Google Doc to parse.\n * @param parseSchema - The schema definition used to guide the parsing process.\n * @returns A promise resolving to the parsed document data.\n * @throws Will throw a descriptive error if the API call fails or returns an empty response.\n */\nexport async function getParsedDocument<T extends ParseSchema>(\n documentId: string,\n parseSchema: T\n): Promise<GetParsedType<T>> {\n try {\n const docs = createDocsClient();\n const response = await docs.documents.get({ documentId });\n if (!response.data) {\n throw new Error(\"Empty document response from Google Docs API.\");\n }\n\n const parsedDocument = parseDocument(response.data, parseSchema);\n return parsedDocument as GetParsedType<T>;\n } catch (e) {\n throw new Error(\n `Google Docs API call failed. Check Doc ID and Service Account permissions. Original error: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n}\n"]}
package/dist/index.js CHANGED
@@ -365,7 +365,13 @@ var ParagraphCursor = class {
365
365
  };
366
366
  function createDocsClient() {
367
367
  try {
368
+ const credentialsEnv = process.env.GOOGLE_APPLICATION_CREDENTIALS;
369
+ let credentials;
370
+ if (credentialsEnv?.trim().startsWith("{")) {
371
+ credentials = JSON.parse(credentialsEnv);
372
+ }
368
373
  const auth = new GoogleAuth({
374
+ credentials,
369
375
  scopes: ["https://www.googleapis.com/auth/documents.readonly"]
370
376
  });
371
377
  return google.docs({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/tree.ts","../src/list.ts","../src/textBlock.ts","../src/section.ts","../src/cursor.ts","../src/auth.ts","../src/parser.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * A constant tuple of supported Google Docs named styles that represent headings or titles.\n * These are used to identify structural nodes and section boundaries within the document tree.\n */\nexport const VALID_NAMED_STYLES = [\n \"HEADING_1\",\n \"HEADING_2\",\n \"HEADING_3\",\n \"HEADING_4\",\n \"HEADING_5\",\n \"HEADING_6\",\n \"TITLE\",\n \"SUBTITLE\",\n] as const;\n\n/**\n * A union type representing any one of the valid named styles defined in `VALID_NAMED_STYLES`.\n */\nexport type ValidNamedStyle = (typeof VALID_NAMED_STYLES)[number];\n\n/**\n * A Set containing the valid named styles for efficient O(1) validation checks.\n * This is primarily used in type guards to verify if a style string is a valid heading.\n */\nexport const VALID_NAMED_STYLES_SET = new Set<string>(VALID_NAMED_STYLES);\n","import { docs_v1 } from \"googleapis\";\nimport { Schema, NamedStyleType } from \"./types\";\nimport { VALID_NAMED_STYLES_SET } from \"./constants\";\n\n/**\n * Extracts pure text content from a Google Docs Paragraph.\n *\n * - Joins content from all `textRun` elements.\n * - Trims leading and trailing whitespace.\n * - Normalizes newline characters (`\\n`) to spaces.\n *\n * @param paragraph - The Google Docs paragraph object.\n * @returns The extracted and cleaned text string. Returns an empty string if the paragraph is empty.\n */\nexport function extractParagraphText(\n paragraph: docs_v1.Schema$Paragraph\n): string {\n const elements = paragraph.elements ?? [];\n\n const text = elements\n .map((el: docs_v1.Schema$ParagraphElement) => el.textRun?.content || \"\")\n .join(\"\")\n .trim()\n .replace(/\\n/g, \" \");\n\n return text || \"\";\n}\n\n/**\n * Checks if a paragraph has a specific `namedStyleType` (e.g., HEADING_1).\n *\n * @param paragraph - The paragraph to check.\n * @param namedStyleType - The style type to compare against.\n * @returns `true` if the paragraph's style matches the provided type; otherwise `false`.\n */\nexport function hasNamedStyle(\n paragraph: docs_v1.Schema$Paragraph,\n namedStyleType?: NamedStyleType\n): boolean {\n if (!namedStyleType) return false;\n const style = paragraph.paragraphStyle?.namedStyleType;\n return style === namedStyleType;\n}\n\n/**\n * Retrieves the named style type of a paragraph, handling default behaviors and edge cases.\n *\n * Resolution Logic:\n * 1. Returns `undefined` if the paragraph has no style and no content (completely empty).\n * 2. Returns `\"NORMAL_TEXT\"` if the paragraph has content but no explicit style (Google Docs default).\n * 3. Returns `\"NORMAL_TEXT\"` if the style object exists but `namedStyleType` is missing.\n * 4. Returns the `namedStyleType` if it exists in the valid set (`VALID_NAMED_STYLES`).\n * 5. Returns `undefined` if the style is invalid or unknown.\n *\n * @param paragraph - The paragraph to analyze.\n * @returns The valid `NamedStyleType`, `\"NORMAL_TEXT\"`, or `undefined`.\n */\nexport function getParagraphNamedStyleType(\n paragraph: docs_v1.Schema$Paragraph\n): NamedStyleType | \"NORMAL_TEXT\" | undefined {\n if (!paragraph.paragraphStyle && !paragraph.elements?.length) {\n return undefined;\n }\n if (!paragraph.paragraphStyle) {\n return \"NORMAL_TEXT\";\n }\n\n const namedStyleType = paragraph.paragraphStyle.namedStyleType;\n\n if (!namedStyleType) {\n return \"NORMAL_TEXT\";\n }\n\n if (VALID_NAMED_STYLES_SET.has(namedStyleType)) {\n return namedStyleType as NamedStyleType;\n }\n\n return undefined;\n}\n\n/**\n * Type Guard to check if a style is a valid Heading style (HEADING_1~6, TITLE, SUBTITLE).\n *\n * - Returns `false` for `\"NORMAL_TEXT\"`.\n * - Returns `false` for undefined or invalid strings.\n *\n * @param style - The style string to check.\n * @returns `true` if the style is a valid `NamedStyleType`.\n */\nexport function isNamedStyleType(\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined\n): style is NamedStyleType {\n if (typeof style !== \"string\") return false;\n if (style === \"NORMAL_TEXT\") return false;\n\n return VALID_NAMED_STYLES_SET.has(style);\n}\n\n/**\n * Splits a string by a delimiter and trims whitespace from each item.\n *\n * @param text - The string to split.\n * @param delimiter - The character to split by (e.g., \",\").\n * @param filterEmpty - If `true`, removes empty strings from the result. Defaults to `false`.\n * @returns An array of trimmed strings.\n */\nexport function splitAndTrim(\n text: string,\n delimiter: string,\n filterEmpty = false\n): string[] {\n if (text === \"\") {\n return [];\n }\n const items = text.split(delimiter).map((t) => t.trim());\n return filterEmpty ? items.filter((t) => t.length > 0) : items;\n}\n\n/**\n * Parses text into a Key-Value list structure.\n *\n * Format: \"Key: Value1, Value2\"\n *\n * @param text - The text to parse.\n * @param keyDelimiter - The separator between key and values (e.g., \":\").\n * @param listDelimiter - The separator between list items (e.g., \",\").\n * @returns An object `{ key, value[] }` if parsing succeeds, or the original text string if the key delimiter is missing.\n */\nexport function parseToKeyedList(\n text: string,\n keyDelimiter: string,\n listDelimiter: string\n): { key: string; value: string[] } | string {\n const delimiterIndex = text.indexOf(keyDelimiter);\n\n if (delimiterIndex <= 0) {\n return text;\n }\n\n const key = text.substring(0, delimiterIndex).trim();\n const valuePart = text.substring(delimiterIndex + keyDelimiter.length).trim();\n\n const value = valuePart ? splitAndTrim(valuePart, listDelimiter, true) : [];\n\n return { key, value };\n}\n\n/**\n * Maps delimited text values to a list of keys by position.\n *\n * Format: \"Value1 | Value2 | Value3\" -> { Key1: \"Value1\", Key2: \"Value2\", ... }\n *\n * @param text - The text to parse.\n * @param keys - The list of field names (keys) to map values to.\n * @param delimiter - The separator between values (e.g., \"|\").\n * @returns A record object mapping keys to values. Missing values are filled with empty strings.\n */\nexport function parseToFields(\n text: string,\n keys: readonly string[],\n delimiter: string\n): Record<string, unknown> {\n const values = splitAndTrim(text, delimiter, false);\n\n return keys.reduce((acc, key, index) => {\n const value = values[index];\n acc[key] = value !== undefined && value !== \"\" ? value : \"\";\n return acc;\n }, {} as Record<string, unknown>);\n}\n\n/**\n * Parses a simple delimited string into an array, filtering out empty items.\n *\n * Example: \"Apple, , Banana\" -> [\"Apple\", \"Banana\"]\n *\n * @param text - The text to parse.\n * @param delimiter - The separator (e.g., \",\").\n * @returns An array of non-empty strings.\n */\nexport function parseDelimitedList(text: string, delimiter: string): string[] {\n const values = text\n .split(delimiter)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n return values;\n}\n\n/**\n * Parses a single line of text into structured data based on the provided Schema.\n *\n * **Parsing Priorities:**\n * 1. **Keyed List**: If `schema.keyDelimiter` is set. (e.g., \"Team: A, B\")\n * 2. **Fixed Fields**: If `schema.keys` is set. (e.g., \"Google | Engineer | 2023\")\n * 3. **Default List**: Fallback to simple delimited list. (e.g., \"A, B, C\")\n *\n * @param text - The text content to parse.\n * @param schema - The schema object defining the parsing rules.\n * @returns The parsed result as an object, array, or string depending on the schema.\n */\nexport function parseStructuredText(\n text: string,\n schema: Schema\n): Record<string, unknown> | string[] | string {\n const delimiter = schema.delimiter || \",\";\n\n if (schema.keyDelimiter) {\n return parseToKeyedList(text, schema.keyDelimiter, delimiter);\n }\n\n if (schema.keys && schema.keys.length > 0) {\n return parseToFields(text, schema.keys, delimiter);\n }\n\n return parseDelimitedList(text, delimiter);\n}\n","import { docs_v1 } from \"googleapis\";\nimport { NamedStyleType, Node, Section, Schema } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { hasNamedStyle, parseStructuredText } from \"./utils\";\n\nconst CONTENT_KEY = \"content\";\n\n/**\n * Recursively collects all `NamedStyleType`s defined within a nested `Node` schema.\n *\n * This is used to build a set of allowed heading styles for the parser to recognize\n * valid tree nodes and detect boundaries.\n *\n * @param node - The current node schema to traverse.\n * @param set - The Set to populate with collected style types.\n */\nexport function collectNodeStylesRecursive(\n node: Node,\n set: Set<NamedStyleType>\n): void {\n if (node.title.namedStyleType) set.add(node.title.namedStyleType);\n if (node.content && node.content.kind === \"tree\") {\n collectNodeStylesRecursive(node.content.node, set);\n }\n}\n\n/**\n * Creates a normalized node object from a raw title string based on the provided schema.\n *\n * - If the schema defines parsing rules (delimiters/keys), the title is parsed into\n * structured data (Array or Object).\n * - If no parsing rules exist, the title is treated as a simple string.\n *\n * @param text - The raw text of the title/heading.\n * @param titleSchema - The schema defining how to parse the title.\n * @returns An object containing the parsed `title` and an empty `content` array.\n */\nexport function createNodeFromTitle(\n text: string,\n titleSchema: Schema\n): Record<\"title\" | \"content\", unknown> {\n const hasDelimiterSchema =\n !!titleSchema.keyDelimiter ||\n (!!titleSchema.keys && titleSchema.keys.length > 0) ||\n !!titleSchema.delimiter;\n\n if (hasDelimiterSchema) {\n const structuredText = parseStructuredText(text, titleSchema);\n if (Array.isArray(structuredText)) {\n return { title: structuredText, content: [] };\n } else if (typeof structuredText === \"object\" && structuredText !== null) {\n return { title: structuredText, content: [] };\n } else {\n return { title: text, content: [] };\n }\n } else {\n return { title: text, content: [] };\n }\n}\n\n/**\n * Represents the decision made by the parser for the current paragraph.\n */\nexport type TreeParsingAction =\n | { kind: \"exitSection\" }\n | { kind: \"createNode\" }\n | { kind: \"startChildNode\" }\n | { kind: \"finishCurrentNode\" }\n | { kind: \"appendDetail\" };\n\n/**\n * Determines the next action to take based on the current paragraph's style\n * and its relationship to the current node hierarchy.\n *\n * Decision Logic Priorities:\n * 1. New Section -> Exit.\n * 2. Matches Current Node Title -> Create Sibling Node.\n * 3. Matches Child Node Title -> Start Child Node (Recursion).\n * 4. Matches Ancestor/Same Level Heading -> Finish Current Node (Pop Stack).\n * 5. Heading outside this tree -> Exit.\n * 6. Otherwise -> Append as detail content.\n *\n * @param paragraph - The current paragraph being inspected.\n * @param cursor - The paragraph cursor.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - The stack of ancestor nodes for context.\n * @returns The determining parsing action.\n */\nexport function determineTreeParsingAction(\n paragraph: docs_v1.Schema$Paragraph,\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[]\n): TreeParsingAction {\n const nodeTitleStyle = nodeSchema.title.namedStyleType;\n const childNode =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n const isCurrentNodeTitle = hasNamedStyle(paragraph, nodeTitleStyle);\n const isChildNodeTitle =\n !!childNode && hasNamedStyle(paragraph, childNode.title.namedStyleType);\n const isAncestorNodeTitle = ancestorNodeList.some((a) =>\n hasNamedStyle(paragraph, a.title.namedStyleType)\n );\n\n const isInThisTree =\n isCurrentNodeTitle || isChildNodeTitle || isAncestorNodeTitle;\n\n const isHeading = cursor.isAtParagraphHeading();\n const isAtNewSection = cursor.isAtNewSection();\n const isHeadingOutsideThisTree = isHeading && !isInThisTree;\n const isHigherLevelHeading = isHeading && isAncestorNodeTitle;\n const isSameLevelHeading = isHeading && isCurrentNodeTitle;\n\n if (isAtNewSection) return { kind: \"exitSection\" };\n if (isCurrentNodeTitle) return { kind: \"createNode\" };\n if (isChildNodeTitle) return { kind: \"startChildNode\" };\n if (isHigherLevelHeading || isSameLevelHeading)\n return { kind: \"finishCurrentNode\" };\n if (isHeadingOutsideThisTree) return { kind: \"exitSection\" };\n return { kind: \"appendDetail\" };\n}\n\n/**\n * The entry point for parsing a document section defined as a `Tree`.\n *\n * It searches for the first occurrence of the root node style to begin parsing.\n * Text preceding the first valid root node is ignored (orphan text).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the tree schema.\n * @param allNodeTitleStyles - A set of all valid styles in this tree (for boundary detection).\n * @returns An array of parsed tree nodes.\n */\nexport function parseTreeSection(\n cursor: ParagraphCursor,\n section: Section,\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const treeContent = section.content;\n const nodeSchema =\n treeContent?.kind === \"tree\" ? treeContent.node : undefined;\n if (!treeContent || !nodeSchema) return [];\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { paragraph, style } = info;\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) break;\n\n if (hasNamedStyle(paragraph, nodeSchema.title.namedStyleType)) {\n return parseTreeNode(cursor, nodeSchema, [], allNodeTitleStyles);\n }\n\n cursor.getNextParagraph();\n }\n return [];\n}\n\n/**\n * Recursively parses a specific level of the tree structure.\n *\n * Handles creation of current nodes, delegating to child parsers for nested content,\n * and accumulating list details.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - Stack of ancestors to detect hierarchy boundaries.\n * @param allNodeTitleStyles - Set of all valid styles for boundary checks.\n * @returns An array of parsed nodes for this level.\n */\nexport function parseTreeNode(\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[],\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const result: unknown[] = [];\n let currentNode: Record<string, unknown> | null = null;\n\n const childNodeSchema =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { text, paragraph, style } = info;\n\n if (cursor.isAtNewSection()) {\n return result;\n }\n\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) {\n return result;\n }\n\n const decision = determineTreeParsingAction(\n paragraph,\n cursor,\n nodeSchema,\n ancestorNodeList\n );\n\n switch (decision.kind) {\n case \"exitSection\": {\n return result;\n }\n\n case \"createNode\": {\n currentNode = createNodeFromTitle(text, nodeSchema.title);\n result.push(currentNode);\n cursor.getNextParagraph();\n break;\n }\n\n case \"startChildNode\": {\n if (\n !currentNode ||\n !Array.isArray(currentNode[CONTENT_KEY]) ||\n !childNodeSchema\n ) {\n cursor.getNextParagraph();\n break;\n }\n const children = parseTreeNode(\n cursor,\n childNodeSchema,\n [nodeSchema, ...ancestorNodeList],\n allNodeTitleStyles\n );\n (currentNode[CONTENT_KEY] as unknown[]).push(...children);\n break;\n }\n\n case \"finishCurrentNode\": {\n if (currentNode) return result;\n cursor.getNextParagraph();\n break;\n }\n\n case \"appendDetail\": {\n if (nodeSchema.content?.kind === \"tree\") {\n cursor.getNextParagraph();\n break;\n }\n if (currentNode && Array.isArray(currentNode[CONTENT_KEY])) {\n (currentNode[CONTENT_KEY] as unknown[]).push(text.trim());\n }\n cursor.getNextParagraph();\n break;\n }\n }\n }\n\n return result;\n}\n","import { Section } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseStructuredText } from \"./utils\";\n\n/**\n * Parses a section defined as a 'List'.\n *\n * This function iterates through paragraphs, parsing each line according to the\n * section's content schema (e.g., delimiters, keys). It supports both flat lists\n * and nested structures depending on the `isFlatten` configuration.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the list schema.\n * @returns An array of parsed items (strings, objects, or arrays).\n */\nexport function parseListSection(\n cursor: ParagraphCursor,\n section: Section\n): unknown[] {\n const result: unknown[] = [];\n if (!section.content || section.content.kind !== \"list\") {\n return [];\n }\n const contentSchema = section.content;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n const parsed = parseStructuredText(info.text, contentSchema);\n\n if (contentSchema.isFlatten && Array.isArray(parsed)) {\n result.push(...parsed);\n } else {\n result.push(parsed);\n }\n cursor.getNextParagraph();\n }\n return result;\n}\n","import { ParagraphCursor } from \"./cursor\";\n\n/**\n * Parses a continuous block of text paragraphs into a single string.\n *\n * This function iterates through paragraphs starting from the current cursor position\n * and collects text until a boundary is encountered.\n *\n * **Stop Conditions:**\n * 1. **New Section:** The cursor reaches a paragraph that marks the start of a new section defined in the schema.\n * 2. **Heading:** The cursor reaches any paragraph with a named heading style (e.g., HEADING_1, HEADING_2).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @returns The combined text content of the block, joined by spaces.\n */\nexport function parseTextBlockSection(cursor: ParagraphCursor): string {\n const textPartList: string[] = [];\n\n while (!cursor.isEndOfDocument()) {\n const paragraph = cursor.getCurrentParagraph();\n if (!paragraph) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n textPartList.push(paragraph.text);\n cursor.getNextParagraph();\n }\n return textPartList.join(\" \");\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, Section, NamedStyleType } from \"./types\";\nimport { hasNamedStyle } from \"./utils\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseTreeSection, collectNodeStylesRecursive } from \"./tree\";\nimport { parseListSection } from \"./list\";\nimport { parseTextBlockSection } from \"./textBlock\";\n\n/**\n * Identifies if a paragraph corresponds to a section title defined in the schema.\n *\n * Matching Logic:\n * 1. Matches the paragraph's named style (e.g., HEADING_2) with the section's style.\n * 2. Matches the text content (case-insensitive, trimmed).\n *\n * @param paragraph - The current paragraph being inspected.\n * @param text - The extracted text content of the paragraph.\n * @param parseSchema - The global parsing schema containing section definitions.\n * @returns The canonical name of the section if found, otherwise `null`.\n */\nexport function getSectionTitle(\n paragraph: docs_v1.Schema$Paragraph,\n text: string,\n parseSchema: ParseSchema\n): string | null {\n const normalized = text.trim().toLowerCase();\n const sectionList = parseSchema.sections ?? [];\n\n for (const section of sectionList) {\n const { name, namedStyleType } = section.title;\n if (name && namedStyleType) {\n const styleMatches = hasNamedStyle(paragraph, namedStyleType);\n const textMatches = normalized === name.trim().toLowerCase();\n if (styleMatches && textMatches) {\n return name;\n }\n }\n }\n return null;\n}\n\n/**\n * Dispatches the parsing logic to the appropriate handler based on the section's content type.\n *\n * - **Tree:** Delegates to `parseTreeSection` for hierarchical data.\n * - **List:** Delegates to `parseListSection` for flat lists.\n * - **TextBlock (Default):** Delegates to `parseTextBlockSection` if no content structure is defined.\n *\n * @param cursor - The paragraph cursor.\n * @param section - The section definition containing the content schema.\n * @returns The parsed result (String, Array, or Object depending on the type).\n */\nexport function parseSectionContent(\n cursor: ParagraphCursor,\n section: Section\n): unknown {\n const content = section.content;\n\n if (!content) {\n return parseTextBlockSection(cursor);\n }\n\n switch (content.kind) {\n case \"tree\": {\n if (!content.node) {\n return [];\n }\n const allNodeTitleStyles = new Set<NamedStyleType>();\n collectNodeStylesRecursive(content.node, allNodeTitleStyles);\n return parseTreeSection(cursor, section, allNodeTitleStyles);\n }\n case \"list\":\n return parseListSection(cursor, section);\n default:\n return parseTextBlockSection(cursor);\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, NamedStyleType } from \"./types\";\nimport {\n getParagraphNamedStyleType,\n isNamedStyleType,\n extractParagraphText,\n} from \"./utils\";\nimport { getSectionTitle } from \"./section\";\n\n/**\n * Represents a processed paragraph with extracted text and normalized style.\n */\nexport interface Paragraph {\n text: string;\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined;\n paragraph: docs_v1.Schema$Paragraph;\n}\n\n/**\n * Extracts and normalizes data from a raw Google Docs paragraph.\n *\n * @param paragraph - The raw paragraph object from the API.\n * @returns A `Paragraph` object if text exists, otherwise `null` (e.g., empty lines).\n */\nexport function getParagraph(\n paragraph: docs_v1.Schema$Paragraph\n): Paragraph | null {\n const text = extractParagraphText(paragraph);\n if (!text) return null;\n const style = getParagraphNamedStyleType(paragraph);\n return { text, style, paragraph };\n}\n\n/**\n * A stateful cursor for traversing a list of Google Docs paragraphs.\n *\n * It manages the current position (index) and provides methods to inspect\n * the current paragraph's context (e.g., style, section boundaries)\n * without manually handling array indices.\n */\nexport class ParagraphCursor {\n private index = 0;\n\n constructor(\n private paragraphList: docs_v1.Schema$Paragraph[],\n private parseSchema: ParseSchema\n ) {}\n\n /**\n * Retrieves the paragraph at the current cursor position.\n *\n * @returns The current `Paragraph` object, or `null` if the cursor is at the end of the document or the line is empty.\n */\n getCurrentParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n const paragraph = this.paragraphList[this.index];\n if (!paragraph) return null;\n return getParagraph(paragraph);\n }\n\n /**\n * Advances the cursor to the next position and returns the new paragraph.\n *\n * @returns The next `Paragraph` object, or `null` if the end of the document is reached.\n */\n getNextParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n this.index++;\n return this.getCurrentParagraph();\n }\n\n /**\n * Checks if the cursor has reached the end of the paragraph list.\n */\n isEndOfDocument(): boolean {\n return this.index >= this.paragraphList.length;\n }\n\n /**\n * Determines if the current paragraph corresponds to a section title defined in the schema.\n *\n * @returns The section name if matched, otherwise `null`.\n */\n getCurrentSectionTitle(): string | null {\n const info = this.getCurrentParagraph();\n if (!info) return null;\n return getSectionTitle(info.paragraph, info.text, this.parseSchema);\n }\n\n /**\n * Checks if the current cursor position marks the start of a new section.\n */\n isAtNewSection(): boolean {\n return this.getCurrentSectionTitle() !== null;\n }\n\n /**\n * Checks if the current paragraph has a named heading style (e.g., HEADING_1).\n */\n isAtParagraphHeading(): boolean {\n const info = this.getCurrentParagraph();\n return !!info && isNamedStyleType(info.style);\n }\n}\n","import { google, docs_v1 } from \"googleapis\";\nimport { GoogleAuth } from \"google-auth-library\";\n\n/**\n * Creates and configures a Google Docs API client instance.\n *\n * This function handles authentication using `GoogleAuth` with the read-only scope\n * and initializes the `googleapis` Docs service with version 'v1'.\n *\n * @returns An initialized `docs_v1.Docs` client ready for API calls.\n * @throws {Error} If client initialization fails (e.g., missing credentials or configuration errors).\n */\nexport function createDocsClient(): docs_v1.Docs {\n try {\n const auth = new GoogleAuth({\n scopes: [\"https://www.googleapis.com/auth/documents.readonly\"],\n });\n\n return google.docs({\n version: \"v1\",\n auth,\n });\n } catch (error) {\n console.error(\"Error initializing Google Docs client:\", error);\n throw new Error(\n \"Failed to initialize Google Docs client. Check setup and credentials.\"\n );\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParsedDocument, ParseSchema, GetParsedType } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseSectionContent } from \"./section\";\nimport { createDocsClient } from \"./auth\";\n\n/**\n * Parses the raw Google Docs document content according to the provided schema.\n *\n * This function converts the document into a stream of valid paragraphs and\n * uses a cursor to navigate and parse sections based on the schema definition.\n *\n * @param doc - The raw Google Docs document object.\n * @param parseSchema - The schema defining the structure of sections to parse.\n * @returns An object representing the parsed document content.\n */\nfunction parseDocument(\n doc: docs_v1.Schema$Document,\n parseSchema: ParseSchema\n): ParsedDocument {\n const content = doc.body?.content || [];\n const result: ParsedDocument = {};\n\n const validParagraphList = content\n .map((element) => element.paragraph)\n .filter((paragraph): paragraph is docs_v1.Schema$Paragraph => !!paragraph);\n\n const cursor = new ParagraphCursor(validParagraphList, parseSchema);\n\n while (!cursor.isEndOfDocument()) {\n const currentSectionTitle = cursor.getCurrentSectionTitle();\n if (currentSectionTitle) {\n const section = parseSchema.sections.find(\n (s) => s.title.name === currentSectionTitle\n );\n if (section) {\n cursor.getNextParagraph();\n const parsedData = parseSectionContent(cursor, section);\n result[currentSectionTitle] = parsedData;\n continue;\n }\n }\n cursor.getNextParagraph();\n }\n\n return result;\n}\n\n/**\n * Public API: Fetches and parses a Google Doc by its ID.\n *\n * This function handles authentication, API communication, and error wrapping.\n * It returns a fully typed object based on the provided schema generic `T`.\n *\n * @template T - The type of the ParseSchema, allowing for type inference of the result.\n * @param documentId - The unique ID of the Google Doc to parse.\n * @param parseSchema - The schema definition used to guide the parsing process.\n * @returns A promise resolving to the parsed document data.\n * @throws Will throw a descriptive error if the API call fails or returns an empty response.\n */\nexport async function getParsedDocument<T extends ParseSchema>(\n documentId: string,\n parseSchema: T\n): Promise<GetParsedType<T>> {\n try {\n const docs = createDocsClient();\n const response = await docs.documents.get({ documentId });\n if (!response.data) {\n throw new Error(\"Empty document response from Google Docs API.\");\n }\n\n const parsedDocument = parseDocument(response.data, parseSchema);\n return parsedDocument as GetParsedType<T>;\n } catch (e) {\n throw new Error(\n `Google Docs API call failed. Check Doc ID and Service Account permissions. Original error: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/tree.ts","../src/list.ts","../src/textBlock.ts","../src/section.ts","../src/cursor.ts","../src/auth.ts","../src/parser.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * A constant tuple of supported Google Docs named styles that represent headings or titles.\n * These are used to identify structural nodes and section boundaries within the document tree.\n */\nexport const VALID_NAMED_STYLES = [\n \"HEADING_1\",\n \"HEADING_2\",\n \"HEADING_3\",\n \"HEADING_4\",\n \"HEADING_5\",\n \"HEADING_6\",\n \"TITLE\",\n \"SUBTITLE\",\n] as const;\n\n/**\n * A union type representing any one of the valid named styles defined in `VALID_NAMED_STYLES`.\n */\nexport type ValidNamedStyle = (typeof VALID_NAMED_STYLES)[number];\n\n/**\n * A Set containing the valid named styles for efficient O(1) validation checks.\n * This is primarily used in type guards to verify if a style string is a valid heading.\n */\nexport const VALID_NAMED_STYLES_SET = new Set<string>(VALID_NAMED_STYLES);\n","import { docs_v1 } from \"googleapis\";\nimport { Schema, NamedStyleType } from \"./types\";\nimport { VALID_NAMED_STYLES_SET } from \"./constants\";\n\n/**\n * Extracts pure text content from a Google Docs Paragraph.\n *\n * - Joins content from all `textRun` elements.\n * - Trims leading and trailing whitespace.\n * - Normalizes newline characters (`\\n`) to spaces.\n *\n * @param paragraph - The Google Docs paragraph object.\n * @returns The extracted and cleaned text string. Returns an empty string if the paragraph is empty.\n */\nexport function extractParagraphText(\n paragraph: docs_v1.Schema$Paragraph\n): string {\n const elements = paragraph.elements ?? [];\n\n const text = elements\n .map((el: docs_v1.Schema$ParagraphElement) => el.textRun?.content || \"\")\n .join(\"\")\n .trim()\n .replace(/\\n/g, \" \");\n\n return text || \"\";\n}\n\n/**\n * Checks if a paragraph has a specific `namedStyleType` (e.g., HEADING_1).\n *\n * @param paragraph - The paragraph to check.\n * @param namedStyleType - The style type to compare against.\n * @returns `true` if the paragraph's style matches the provided type; otherwise `false`.\n */\nexport function hasNamedStyle(\n paragraph: docs_v1.Schema$Paragraph,\n namedStyleType?: NamedStyleType\n): boolean {\n if (!namedStyleType) return false;\n const style = paragraph.paragraphStyle?.namedStyleType;\n return style === namedStyleType;\n}\n\n/**\n * Retrieves the named style type of a paragraph, handling default behaviors and edge cases.\n *\n * Resolution Logic:\n * 1. Returns `undefined` if the paragraph has no style and no content (completely empty).\n * 2. Returns `\"NORMAL_TEXT\"` if the paragraph has content but no explicit style (Google Docs default).\n * 3. Returns `\"NORMAL_TEXT\"` if the style object exists but `namedStyleType` is missing.\n * 4. Returns the `namedStyleType` if it exists in the valid set (`VALID_NAMED_STYLES`).\n * 5. Returns `undefined` if the style is invalid or unknown.\n *\n * @param paragraph - The paragraph to analyze.\n * @returns The valid `NamedStyleType`, `\"NORMAL_TEXT\"`, or `undefined`.\n */\nexport function getParagraphNamedStyleType(\n paragraph: docs_v1.Schema$Paragraph\n): NamedStyleType | \"NORMAL_TEXT\" | undefined {\n if (!paragraph.paragraphStyle && !paragraph.elements?.length) {\n return undefined;\n }\n if (!paragraph.paragraphStyle) {\n return \"NORMAL_TEXT\";\n }\n\n const namedStyleType = paragraph.paragraphStyle.namedStyleType;\n\n if (!namedStyleType) {\n return \"NORMAL_TEXT\";\n }\n\n if (VALID_NAMED_STYLES_SET.has(namedStyleType)) {\n return namedStyleType as NamedStyleType;\n }\n\n return undefined;\n}\n\n/**\n * Type Guard to check if a style is a valid Heading style (HEADING_1~6, TITLE, SUBTITLE).\n *\n * - Returns `false` for `\"NORMAL_TEXT\"`.\n * - Returns `false` for undefined or invalid strings.\n *\n * @param style - The style string to check.\n * @returns `true` if the style is a valid `NamedStyleType`.\n */\nexport function isNamedStyleType(\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined\n): style is NamedStyleType {\n if (typeof style !== \"string\") return false;\n if (style === \"NORMAL_TEXT\") return false;\n\n return VALID_NAMED_STYLES_SET.has(style);\n}\n\n/**\n * Splits a string by a delimiter and trims whitespace from each item.\n *\n * @param text - The string to split.\n * @param delimiter - The character to split by (e.g., \",\").\n * @param filterEmpty - If `true`, removes empty strings from the result. Defaults to `false`.\n * @returns An array of trimmed strings.\n */\nexport function splitAndTrim(\n text: string,\n delimiter: string,\n filterEmpty = false\n): string[] {\n if (text === \"\") {\n return [];\n }\n const items = text.split(delimiter).map((t) => t.trim());\n return filterEmpty ? items.filter((t) => t.length > 0) : items;\n}\n\n/**\n * Parses text into a Key-Value list structure.\n *\n * Format: \"Key: Value1, Value2\"\n *\n * @param text - The text to parse.\n * @param keyDelimiter - The separator between key and values (e.g., \":\").\n * @param listDelimiter - The separator between list items (e.g., \",\").\n * @returns An object `{ key, value[] }` if parsing succeeds, or the original text string if the key delimiter is missing.\n */\nexport function parseToKeyedList(\n text: string,\n keyDelimiter: string,\n listDelimiter: string\n): { key: string; value: string[] } | string {\n const delimiterIndex = text.indexOf(keyDelimiter);\n\n if (delimiterIndex <= 0) {\n return text;\n }\n\n const key = text.substring(0, delimiterIndex).trim();\n const valuePart = text.substring(delimiterIndex + keyDelimiter.length).trim();\n\n const value = valuePart ? splitAndTrim(valuePart, listDelimiter, true) : [];\n\n return { key, value };\n}\n\n/**\n * Maps delimited text values to a list of keys by position.\n *\n * Format: \"Value1 | Value2 | Value3\" -> { Key1: \"Value1\", Key2: \"Value2\", ... }\n *\n * @param text - The text to parse.\n * @param keys - The list of field names (keys) to map values to.\n * @param delimiter - The separator between values (e.g., \"|\").\n * @returns A record object mapping keys to values. Missing values are filled with empty strings.\n */\nexport function parseToFields(\n text: string,\n keys: readonly string[],\n delimiter: string\n): Record<string, unknown> {\n const values = splitAndTrim(text, delimiter, false);\n\n return keys.reduce((acc, key, index) => {\n const value = values[index];\n acc[key] = value !== undefined && value !== \"\" ? value : \"\";\n return acc;\n }, {} as Record<string, unknown>);\n}\n\n/**\n * Parses a simple delimited string into an array, filtering out empty items.\n *\n * Example: \"Apple, , Banana\" -> [\"Apple\", \"Banana\"]\n *\n * @param text - The text to parse.\n * @param delimiter - The separator (e.g., \",\").\n * @returns An array of non-empty strings.\n */\nexport function parseDelimitedList(text: string, delimiter: string): string[] {\n const values = text\n .split(delimiter)\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n return values;\n}\n\n/**\n * Parses a single line of text into structured data based on the provided Schema.\n *\n * **Parsing Priorities:**\n * 1. **Keyed List**: If `schema.keyDelimiter` is set. (e.g., \"Team: A, B\")\n * 2. **Fixed Fields**: If `schema.keys` is set. (e.g., \"Google | Engineer | 2023\")\n * 3. **Default List**: Fallback to simple delimited list. (e.g., \"A, B, C\")\n *\n * @param text - The text content to parse.\n * @param schema - The schema object defining the parsing rules.\n * @returns The parsed result as an object, array, or string depending on the schema.\n */\nexport function parseStructuredText(\n text: string,\n schema: Schema\n): Record<string, unknown> | string[] | string {\n const delimiter = schema.delimiter || \",\";\n\n if (schema.keyDelimiter) {\n return parseToKeyedList(text, schema.keyDelimiter, delimiter);\n }\n\n if (schema.keys && schema.keys.length > 0) {\n return parseToFields(text, schema.keys, delimiter);\n }\n\n return parseDelimitedList(text, delimiter);\n}\n","import { docs_v1 } from \"googleapis\";\nimport { NamedStyleType, Node, Section, Schema } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { hasNamedStyle, parseStructuredText } from \"./utils\";\n\nconst CONTENT_KEY = \"content\";\n\n/**\n * Recursively collects all `NamedStyleType`s defined within a nested `Node` schema.\n *\n * This is used to build a set of allowed heading styles for the parser to recognize\n * valid tree nodes and detect boundaries.\n *\n * @param node - The current node schema to traverse.\n * @param set - The Set to populate with collected style types.\n */\nexport function collectNodeStylesRecursive(\n node: Node,\n set: Set<NamedStyleType>\n): void {\n if (node.title.namedStyleType) set.add(node.title.namedStyleType);\n if (node.content && node.content.kind === \"tree\") {\n collectNodeStylesRecursive(node.content.node, set);\n }\n}\n\n/**\n * Creates a normalized node object from a raw title string based on the provided schema.\n *\n * - If the schema defines parsing rules (delimiters/keys), the title is parsed into\n * structured data (Array or Object).\n * - If no parsing rules exist, the title is treated as a simple string.\n *\n * @param text - The raw text of the title/heading.\n * @param titleSchema - The schema defining how to parse the title.\n * @returns An object containing the parsed `title` and an empty `content` array.\n */\nexport function createNodeFromTitle(\n text: string,\n titleSchema: Schema\n): Record<\"title\" | \"content\", unknown> {\n const hasDelimiterSchema =\n !!titleSchema.keyDelimiter ||\n (!!titleSchema.keys && titleSchema.keys.length > 0) ||\n !!titleSchema.delimiter;\n\n if (hasDelimiterSchema) {\n const structuredText = parseStructuredText(text, titleSchema);\n if (Array.isArray(structuredText)) {\n return { title: structuredText, content: [] };\n } else if (typeof structuredText === \"object\" && structuredText !== null) {\n return { title: structuredText, content: [] };\n } else {\n return { title: text, content: [] };\n }\n } else {\n return { title: text, content: [] };\n }\n}\n\n/**\n * Represents the decision made by the parser for the current paragraph.\n */\nexport type TreeParsingAction =\n | { kind: \"exitSection\" }\n | { kind: \"createNode\" }\n | { kind: \"startChildNode\" }\n | { kind: \"finishCurrentNode\" }\n | { kind: \"appendDetail\" };\n\n/**\n * Determines the next action to take based on the current paragraph's style\n * and its relationship to the current node hierarchy.\n *\n * Decision Logic Priorities:\n * 1. New Section -> Exit.\n * 2. Matches Current Node Title -> Create Sibling Node.\n * 3. Matches Child Node Title -> Start Child Node (Recursion).\n * 4. Matches Ancestor/Same Level Heading -> Finish Current Node (Pop Stack).\n * 5. Heading outside this tree -> Exit.\n * 6. Otherwise -> Append as detail content.\n *\n * @param paragraph - The current paragraph being inspected.\n * @param cursor - The paragraph cursor.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - The stack of ancestor nodes for context.\n * @returns The determining parsing action.\n */\nexport function determineTreeParsingAction(\n paragraph: docs_v1.Schema$Paragraph,\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[]\n): TreeParsingAction {\n const nodeTitleStyle = nodeSchema.title.namedStyleType;\n const childNode =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n const isCurrentNodeTitle = hasNamedStyle(paragraph, nodeTitleStyle);\n const isChildNodeTitle =\n !!childNode && hasNamedStyle(paragraph, childNode.title.namedStyleType);\n const isAncestorNodeTitle = ancestorNodeList.some((a) =>\n hasNamedStyle(paragraph, a.title.namedStyleType)\n );\n\n const isInThisTree =\n isCurrentNodeTitle || isChildNodeTitle || isAncestorNodeTitle;\n\n const isHeading = cursor.isAtParagraphHeading();\n const isAtNewSection = cursor.isAtNewSection();\n const isHeadingOutsideThisTree = isHeading && !isInThisTree;\n const isHigherLevelHeading = isHeading && isAncestorNodeTitle;\n const isSameLevelHeading = isHeading && isCurrentNodeTitle;\n\n if (isAtNewSection) return { kind: \"exitSection\" };\n if (isCurrentNodeTitle) return { kind: \"createNode\" };\n if (isChildNodeTitle) return { kind: \"startChildNode\" };\n if (isHigherLevelHeading || isSameLevelHeading)\n return { kind: \"finishCurrentNode\" };\n if (isHeadingOutsideThisTree) return { kind: \"exitSection\" };\n return { kind: \"appendDetail\" };\n}\n\n/**\n * The entry point for parsing a document section defined as a `Tree`.\n *\n * It searches for the first occurrence of the root node style to begin parsing.\n * Text preceding the first valid root node is ignored (orphan text).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the tree schema.\n * @param allNodeTitleStyles - A set of all valid styles in this tree (for boundary detection).\n * @returns An array of parsed tree nodes.\n */\nexport function parseTreeSection(\n cursor: ParagraphCursor,\n section: Section,\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const treeContent = section.content;\n const nodeSchema =\n treeContent?.kind === \"tree\" ? treeContent.node : undefined;\n if (!treeContent || !nodeSchema) return [];\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { paragraph, style } = info;\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) break;\n\n if (hasNamedStyle(paragraph, nodeSchema.title.namedStyleType)) {\n return parseTreeNode(cursor, nodeSchema, [], allNodeTitleStyles);\n }\n\n cursor.getNextParagraph();\n }\n return [];\n}\n\n/**\n * Recursively parses a specific level of the tree structure.\n *\n * Handles creation of current nodes, delegating to child parsers for nested content,\n * and accumulating list details.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param nodeSchema - The schema for the current node level.\n * @param ancestorNodeList - Stack of ancestors to detect hierarchy boundaries.\n * @param allNodeTitleStyles - Set of all valid styles for boundary checks.\n * @returns An array of parsed nodes for this level.\n */\nexport function parseTreeNode(\n cursor: ParagraphCursor,\n nodeSchema: Node,\n ancestorNodeList: Node[],\n allNodeTitleStyles: Set<NamedStyleType>\n): unknown[] {\n const result: unknown[] = [];\n let currentNode: Record<string, unknown> | null = null;\n\n const childNodeSchema =\n nodeSchema.content?.kind === \"tree\" ? nodeSchema.content.node : undefined;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n const { text, paragraph, style } = info;\n\n if (cursor.isAtNewSection()) {\n return result;\n }\n\n if (cursor.isAtParagraphHeading() && !allNodeTitleStyles.has(style)) {\n return result;\n }\n\n const decision = determineTreeParsingAction(\n paragraph,\n cursor,\n nodeSchema,\n ancestorNodeList\n );\n\n switch (decision.kind) {\n case \"exitSection\": {\n return result;\n }\n\n case \"createNode\": {\n currentNode = createNodeFromTitle(text, nodeSchema.title);\n result.push(currentNode);\n cursor.getNextParagraph();\n break;\n }\n\n case \"startChildNode\": {\n if (\n !currentNode ||\n !Array.isArray(currentNode[CONTENT_KEY]) ||\n !childNodeSchema\n ) {\n cursor.getNextParagraph();\n break;\n }\n const children = parseTreeNode(\n cursor,\n childNodeSchema,\n [nodeSchema, ...ancestorNodeList],\n allNodeTitleStyles\n );\n (currentNode[CONTENT_KEY] as unknown[]).push(...children);\n break;\n }\n\n case \"finishCurrentNode\": {\n if (currentNode) return result;\n cursor.getNextParagraph();\n break;\n }\n\n case \"appendDetail\": {\n if (nodeSchema.content?.kind === \"tree\") {\n cursor.getNextParagraph();\n break;\n }\n if (currentNode && Array.isArray(currentNode[CONTENT_KEY])) {\n (currentNode[CONTENT_KEY] as unknown[]).push(text.trim());\n }\n cursor.getNextParagraph();\n break;\n }\n }\n }\n\n return result;\n}\n","import { Section } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseStructuredText } from \"./utils\";\n\n/**\n * Parses a section defined as a 'List'.\n *\n * This function iterates through paragraphs, parsing each line according to the\n * section's content schema (e.g., delimiters, keys). It supports both flat lists\n * and nested structures depending on the `isFlatten` configuration.\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @param section - The section definition containing the list schema.\n * @returns An array of parsed items (strings, objects, or arrays).\n */\nexport function parseListSection(\n cursor: ParagraphCursor,\n section: Section\n): unknown[] {\n const result: unknown[] = [];\n if (!section.content || section.content.kind !== \"list\") {\n return [];\n }\n const contentSchema = section.content;\n\n while (!cursor.isEndOfDocument()) {\n const info = cursor.getCurrentParagraph();\n if (!info) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n const parsed = parseStructuredText(info.text, contentSchema);\n\n if (contentSchema.isFlatten && Array.isArray(parsed)) {\n result.push(...parsed);\n } else {\n result.push(parsed);\n }\n cursor.getNextParagraph();\n }\n return result;\n}\n","import { ParagraphCursor } from \"./cursor\";\n\n/**\n * Parses a continuous block of text paragraphs into a single string.\n *\n * This function iterates through paragraphs starting from the current cursor position\n * and collects text until a boundary is encountered.\n *\n * **Stop Conditions:**\n * 1. **New Section:** The cursor reaches a paragraph that marks the start of a new section defined in the schema.\n * 2. **Heading:** The cursor reaches any paragraph with a named heading style (e.g., HEADING_1, HEADING_2).\n *\n * @param cursor - The cursor traversing the document paragraphs.\n * @returns The combined text content of the block, joined by spaces.\n */\nexport function parseTextBlockSection(cursor: ParagraphCursor): string {\n const textPartList: string[] = [];\n\n while (!cursor.isEndOfDocument()) {\n const paragraph = cursor.getCurrentParagraph();\n if (!paragraph) {\n cursor.getNextParagraph();\n continue;\n }\n\n if (cursor.isAtNewSection()) break;\n if (cursor.isAtParagraphHeading()) break;\n\n textPartList.push(paragraph.text);\n cursor.getNextParagraph();\n }\n return textPartList.join(\" \");\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, Section, NamedStyleType } from \"./types\";\nimport { hasNamedStyle } from \"./utils\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseTreeSection, collectNodeStylesRecursive } from \"./tree\";\nimport { parseListSection } from \"./list\";\nimport { parseTextBlockSection } from \"./textBlock\";\n\n/**\n * Identifies if a paragraph corresponds to a section title defined in the schema.\n *\n * Matching Logic:\n * 1. Matches the paragraph's named style (e.g., HEADING_2) with the section's style.\n * 2. Matches the text content (case-insensitive, trimmed).\n *\n * @param paragraph - The current paragraph being inspected.\n * @param text - The extracted text content of the paragraph.\n * @param parseSchema - The global parsing schema containing section definitions.\n * @returns The canonical name of the section if found, otherwise `null`.\n */\nexport function getSectionTitle(\n paragraph: docs_v1.Schema$Paragraph,\n text: string,\n parseSchema: ParseSchema\n): string | null {\n const normalized = text.trim().toLowerCase();\n const sectionList = parseSchema.sections ?? [];\n\n for (const section of sectionList) {\n const { name, namedStyleType } = section.title;\n if (name && namedStyleType) {\n const styleMatches = hasNamedStyle(paragraph, namedStyleType);\n const textMatches = normalized === name.trim().toLowerCase();\n if (styleMatches && textMatches) {\n return name;\n }\n }\n }\n return null;\n}\n\n/**\n * Dispatches the parsing logic to the appropriate handler based on the section's content type.\n *\n * - **Tree:** Delegates to `parseTreeSection` for hierarchical data.\n * - **List:** Delegates to `parseListSection` for flat lists.\n * - **TextBlock (Default):** Delegates to `parseTextBlockSection` if no content structure is defined.\n *\n * @param cursor - The paragraph cursor.\n * @param section - The section definition containing the content schema.\n * @returns The parsed result (String, Array, or Object depending on the type).\n */\nexport function parseSectionContent(\n cursor: ParagraphCursor,\n section: Section\n): unknown {\n const content = section.content;\n\n if (!content) {\n return parseTextBlockSection(cursor);\n }\n\n switch (content.kind) {\n case \"tree\": {\n if (!content.node) {\n return [];\n }\n const allNodeTitleStyles = new Set<NamedStyleType>();\n collectNodeStylesRecursive(content.node, allNodeTitleStyles);\n return parseTreeSection(cursor, section, allNodeTitleStyles);\n }\n case \"list\":\n return parseListSection(cursor, section);\n default:\n return parseTextBlockSection(cursor);\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParseSchema, NamedStyleType } from \"./types\";\nimport {\n getParagraphNamedStyleType,\n isNamedStyleType,\n extractParagraphText,\n} from \"./utils\";\nimport { getSectionTitle } from \"./section\";\n\n/**\n * Represents a processed paragraph with extracted text and normalized style.\n */\nexport interface Paragraph {\n text: string;\n style: NamedStyleType | \"NORMAL_TEXT\" | undefined;\n paragraph: docs_v1.Schema$Paragraph;\n}\n\n/**\n * Extracts and normalizes data from a raw Google Docs paragraph.\n *\n * @param paragraph - The raw paragraph object from the API.\n * @returns A `Paragraph` object if text exists, otherwise `null` (e.g., empty lines).\n */\nexport function getParagraph(\n paragraph: docs_v1.Schema$Paragraph\n): Paragraph | null {\n const text = extractParagraphText(paragraph);\n if (!text) return null;\n const style = getParagraphNamedStyleType(paragraph);\n return { text, style, paragraph };\n}\n\n/**\n * A stateful cursor for traversing a list of Google Docs paragraphs.\n *\n * It manages the current position (index) and provides methods to inspect\n * the current paragraph's context (e.g., style, section boundaries)\n * without manually handling array indices.\n */\nexport class ParagraphCursor {\n private index = 0;\n\n constructor(\n private paragraphList: docs_v1.Schema$Paragraph[],\n private parseSchema: ParseSchema\n ) {}\n\n /**\n * Retrieves the paragraph at the current cursor position.\n *\n * @returns The current `Paragraph` object, or `null` if the cursor is at the end of the document or the line is empty.\n */\n getCurrentParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n const paragraph = this.paragraphList[this.index];\n if (!paragraph) return null;\n return getParagraph(paragraph);\n }\n\n /**\n * Advances the cursor to the next position and returns the new paragraph.\n *\n * @returns The next `Paragraph` object, or `null` if the end of the document is reached.\n */\n getNextParagraph(): Paragraph | null {\n if (this.isEndOfDocument()) return null;\n this.index++;\n return this.getCurrentParagraph();\n }\n\n /**\n * Checks if the cursor has reached the end of the paragraph list.\n */\n isEndOfDocument(): boolean {\n return this.index >= this.paragraphList.length;\n }\n\n /**\n * Determines if the current paragraph corresponds to a section title defined in the schema.\n *\n * @returns The section name if matched, otherwise `null`.\n */\n getCurrentSectionTitle(): string | null {\n const info = this.getCurrentParagraph();\n if (!info) return null;\n return getSectionTitle(info.paragraph, info.text, this.parseSchema);\n }\n\n /**\n * Checks if the current cursor position marks the start of a new section.\n */\n isAtNewSection(): boolean {\n return this.getCurrentSectionTitle() !== null;\n }\n\n /**\n * Checks if the current paragraph has a named heading style (e.g., HEADING_1).\n */\n isAtParagraphHeading(): boolean {\n const info = this.getCurrentParagraph();\n return !!info && isNamedStyleType(info.style);\n }\n}\n","import { google, docs_v1 } from \"googleapis\";\nimport { GoogleAuth } from \"google-auth-library\";\n\n/**\n * Creates and configures a Google Docs API client instance.\n *\n * This function handles authentication using `GoogleAuth` with the read-only scope\n * and initializes the `googleapis` Docs service with version 'v1'.\n *\n * The function automatically detects the credential format from GOOGLE_APPLICATION_CREDENTIALS:\n * - If it's a JSON string (starts with '{'): Parses and uses it directly\n * - If it's a file path: Lets GoogleAuth handle it automatically by passing undefined\n *\n * @returns An initialized `docs_v1.Docs` client ready for API calls.\n * @throws {Error} If client initialization fails (e.g., missing credentials or configuration errors).\n */\nexport function createDocsClient(): docs_v1.Docs {\n try {\n const credentialsEnv = process.env.GOOGLE_APPLICATION_CREDENTIALS;\n let credentials;\n\n if (credentialsEnv?.trim().startsWith(\"{\")) {\n credentials = JSON.parse(credentialsEnv);\n }\n\n const auth = new GoogleAuth({\n credentials,\n scopes: [\"https://www.googleapis.com/auth/documents.readonly\"],\n });\n\n return google.docs({\n version: \"v1\",\n auth,\n });\n } catch (error) {\n console.error(\"Error initializing Google Docs client:\", error);\n throw new Error(\n \"Failed to initialize Google Docs client. Check setup and credentials.\"\n );\n }\n}\n","import { docs_v1 } from \"googleapis\";\nimport { ParsedDocument, ParseSchema, GetParsedType } from \"./types\";\nimport { ParagraphCursor } from \"./cursor\";\nimport { parseSectionContent } from \"./section\";\nimport { createDocsClient } from \"./auth\";\n\n/**\n * Parses the raw Google Docs document content according to the provided schema.\n *\n * This function converts the document into a stream of valid paragraphs and\n * uses a cursor to navigate and parse sections based on the schema definition.\n *\n * @param doc - The raw Google Docs document object.\n * @param parseSchema - The schema defining the structure of sections to parse.\n * @returns An object representing the parsed document content.\n */\nfunction parseDocument(\n doc: docs_v1.Schema$Document,\n parseSchema: ParseSchema\n): ParsedDocument {\n const content = doc.body?.content || [];\n const result: ParsedDocument = {};\n\n const validParagraphList = content\n .map((element) => element.paragraph)\n .filter((paragraph): paragraph is docs_v1.Schema$Paragraph => !!paragraph);\n\n const cursor = new ParagraphCursor(validParagraphList, parseSchema);\n\n while (!cursor.isEndOfDocument()) {\n const currentSectionTitle = cursor.getCurrentSectionTitle();\n if (currentSectionTitle) {\n const section = parseSchema.sections.find(\n (s) => s.title.name === currentSectionTitle\n );\n if (section) {\n cursor.getNextParagraph();\n const parsedData = parseSectionContent(cursor, section);\n result[currentSectionTitle] = parsedData;\n continue;\n }\n }\n cursor.getNextParagraph();\n }\n\n return result;\n}\n\n/**\n * Public API: Fetches and parses a Google Doc by its ID.\n *\n * This function handles authentication, API communication, and error wrapping.\n * It returns a fully typed object based on the provided schema generic `T`.\n *\n * @template T - The type of the ParseSchema, allowing for type inference of the result.\n * @param documentId - The unique ID of the Google Doc to parse.\n * @param parseSchema - The schema definition used to guide the parsing process.\n * @returns A promise resolving to the parsed document data.\n * @throws Will throw a descriptive error if the API call fails or returns an empty response.\n */\nexport async function getParsedDocument<T extends ParseSchema>(\n documentId: string,\n parseSchema: T\n): Promise<GetParsedType<T>> {\n try {\n const docs = createDocsClient();\n const response = await docs.documents.get({ documentId });\n if (!response.data) {\n throw new Error(\"Empty document response from Google Docs API.\");\n }\n\n const parsedDocument = parseDocument(response.data, parseSchema);\n return parsedDocument as GetParsedType<T>;\n } catch (e) {\n throw new Error(\n `Google Docs API call failed. Check Doc ID and Service Account permissions. Original error: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuji-min/google-docs-parser",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Turn your Google Docs into a Headless CMS. A strictly typed, schema-based parser for Google Docs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",