@payloadcms/plugin-import-export 4.0.0-internal.1f9ae9a → 4.0.0-internal.203916b

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.
Files changed (76) hide show
  1. package/dist/components/ExportPreview/index.css +55 -0
  2. package/dist/components/ExportPreview/index.d.ts +1 -1
  3. package/dist/components/ExportPreview/index.d.ts.map +1 -1
  4. package/dist/components/ExportPreview/index.js +1 -1
  5. package/dist/components/ExportPreview/index.js.map +1 -1
  6. package/dist/components/FormatField/index.d.ts.map +1 -1
  7. package/dist/components/FormatField/index.js +6 -3
  8. package/dist/components/FormatField/index.js.map +1 -1
  9. package/dist/components/ImportPreview/index.css +48 -0
  10. package/dist/components/ImportPreview/index.d.ts +1 -1
  11. package/dist/components/ImportPreview/index.d.ts.map +1 -1
  12. package/dist/components/ImportPreview/index.js +17 -3
  13. package/dist/components/ImportPreview/index.js.map +1 -1
  14. package/dist/components/Page/index.d.ts +1 -1
  15. package/dist/components/Page/index.d.ts.map +1 -1
  16. package/dist/components/Page/index.js +1 -1
  17. package/dist/components/Page/index.js.map +1 -1
  18. package/dist/components/SortBy/index.css +9 -0
  19. package/dist/components/SortBy/index.d.ts +1 -1
  20. package/dist/components/SortBy/index.d.ts.map +1 -1
  21. package/dist/components/SortBy/index.js +1 -2
  22. package/dist/components/SortBy/index.js.map +1 -1
  23. package/dist/components/SortOrder/index.css +9 -0
  24. package/dist/components/SortOrder/index.d.ts +1 -1
  25. package/dist/components/SortOrder/index.d.ts.map +1 -1
  26. package/dist/components/SortOrder/index.js +1 -2
  27. package/dist/components/SortOrder/index.js.map +1 -1
  28. package/dist/export/batchProcessor.d.ts +2 -2
  29. package/dist/export/batchProcessor.d.ts.map +1 -1
  30. package/dist/export/batchProcessor.js.map +1 -1
  31. package/dist/export/createExport.d.ts +2 -2
  32. package/dist/export/createExport.d.ts.map +1 -1
  33. package/dist/export/createExport.js +1 -1
  34. package/dist/export/createExport.js.map +1 -1
  35. package/dist/export/getExportCollection.js +4 -4
  36. package/dist/export/getExportCollection.js.map +1 -1
  37. package/dist/export/handleDownload.js +2 -2
  38. package/dist/export/handleDownload.js.map +1 -1
  39. package/dist/import/batchProcessor.d.ts +2 -2
  40. package/dist/import/batchProcessor.d.ts.map +1 -1
  41. package/dist/import/batchProcessor.js.map +1 -1
  42. package/dist/import/createImport.d.ts.map +1 -1
  43. package/dist/import/createImport.js.map +1 -1
  44. package/dist/import/getImportCollection.js +4 -4
  45. package/dist/import/getImportCollection.js.map +1 -1
  46. package/dist/index.d.ts +1 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +6 -3
  49. package/dist/index.js.map +1 -1
  50. package/dist/utilities/applyFieldHooks.d.ts.map +1 -1
  51. package/dist/utilities/applyFieldHooks.js +1 -1
  52. package/dist/utilities/applyFieldHooks.js.map +1 -1
  53. package/dist/utilities/flattenedFields.d.ts.map +1 -1
  54. package/dist/utilities/flattenedFields.js +3 -0
  55. package/dist/utilities/flattenedFields.js.map +1 -1
  56. package/dist/utilities/processRichTextField.d.ts.map +1 -1
  57. package/dist/utilities/processRichTextField.js +7 -0
  58. package/dist/utilities/processRichTextField.js.map +1 -1
  59. package/dist/utilities/processRichTextField.spec.js +308 -0
  60. package/dist/utilities/processRichTextField.spec.js.map +1 -0
  61. package/dist/utilities/unflattenObject.d.ts.map +1 -1
  62. package/dist/utilities/unflattenObject.js +21 -2
  63. package/dist/utilities/unflattenObject.js.map +1 -1
  64. package/dist/utilities/unflattenObject.spec.js +106 -0
  65. package/dist/utilities/unflattenObject.spec.js.map +1 -1
  66. package/dist/utilities/unflattenPostProcess.d.ts.map +1 -1
  67. package/dist/utilities/unflattenPostProcess.js +39 -13
  68. package/dist/utilities/unflattenPostProcess.js.map +1 -1
  69. package/license.md +1 -1
  70. package/package.json +7 -7
  71. package/dist/components/ExportPreview/index.scss +0 -48
  72. package/dist/components/FieldsToExport/index.scss +0 -0
  73. package/dist/components/ImportPreview/index.scss +0 -41
  74. package/dist/components/SortBy/index.scss +0 -3
  75. package/dist/components/SortOrder/index.scss +0 -3
  76. /package/dist/components/Page/{index.scss → index.css} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/applyFieldHooks.ts"],"sourcesContent":["import type { FlattenedField, PayloadRequest } from 'payload'\n\nimport type {\n ExportFieldHookEntry,\n FieldBeforeExportHook,\n FieldBeforeImportHook,\n ImportFieldHookEntry,\n} from '../types.js'\n\nimport { isPlainObject } from './isPlainObject.js'\n\nexport type FieldHook = FieldBeforeExportHook | FieldBeforeImportHook\n\nexport type FieldHookEntry = ExportFieldHookEntry | ImportFieldHookEntry\n\nconst joinPath = (parent: string | undefined, segment: string): string =>\n parent ? `${parent}_${segment}` : segment\n\nexport type Args = {\n data: Record<string, unknown>\n fieldHooks: Record<string, FieldHookEntry>\n fields: FlattenedField[]\n format: 'csv' | 'json' | ({} & string)\n operation: 'export' | 'import'\n req: PayloadRequest\n type: 'beforeExport' | 'beforeImport'\n}\n\nconst operationLabel = { export: 'Export', import: 'Import' } as const\n\ntype TraverseArgs = {\n path: string | undefined\n schemaPath: string | undefined\n siblingData: Record<string, unknown>\n} & Args\n\nconst traverseFields = ({\n type,\n data,\n fieldHooks,\n fields,\n format,\n operation,\n path,\n req,\n schemaPath,\n siblingData,\n}: TraverseArgs): Record<string, unknown> => {\n const result: Record<string, unknown> = { ...siblingData }\n\n for (const field of fields) {\n if (!('name' in field) || !field.name) {\n continue\n }\n\n const fieldPath = joinPath(path, field.name)\n const fieldSchemaPath = joinPath(schemaPath, field.name)\n const entry = fieldHooks[fieldSchemaPath]\n const hook = entry?.type === type ? (entry.fn as FieldHook) : undefined\n\n if (typeof hook === 'function' && field.name in result) {\n try {\n const transformed = hook({\n columnName: fieldPath,\n data,\n format,\n siblingData: result,\n siblingDoc: siblingData,\n value: result[field.name],\n })\n\n if (typeof transformed !== 'undefined') {\n result[field.name] = transformed\n }\n } catch (error) {\n req.payload.logger.error({\n err: error,\n msg: `[plugin-import-export] Field-level before${operationLabel[operation]} hook for \"${fieldPath}\" threw — falling back to original value`,\n })\n }\n }\n\n if (!(field.name in result)) {\n continue\n }\n\n const value = result[field.name]\n\n if ((field.type === 'group' || field.type === 'tab') && isPlainObject(value)) {\n result[field.name] = traverseFields({\n type,\n data,\n fieldHooks,\n fields: field.flattenedFields,\n format,\n operation,\n path: fieldPath,\n req,\n schemaPath: fieldSchemaPath,\n siblingData: value,\n })\n } else if (field.type === 'array' && Array.isArray(value)) {\n result[field.name] = value.map((item, index) => {\n if (!isPlainObject(item)) {\n return item\n }\n return traverseFields({\n type,\n data,\n fieldHooks,\n fields: field.flattenedFields,\n format,\n operation,\n path: `${fieldPath}_${index}`,\n req,\n schemaPath: fieldSchemaPath,\n siblingData: item,\n })\n })\n } else if (field.type === 'blocks' && Array.isArray(value)) {\n result[field.name] = value.map((item, index) => {\n if (!isPlainObject(item)) {\n return item\n }\n const blockType = typeof item.blockType === 'string' ? item.blockType : undefined\n const block = blockType ? field.blocks.find((b) => b.slug === blockType) : undefined\n return traverseFields({\n type,\n data,\n fieldHooks,\n fields: block?.flattenedFields ?? [],\n format,\n operation,\n path: blockType ? `${fieldPath}_${index}_${blockType}` : `${fieldPath}_${index}`,\n req,\n schemaPath: blockType ? `${fieldSchemaPath}_${blockType}` : fieldSchemaPath,\n siblingData: item,\n })\n })\n }\n }\n\n return result\n}\n\n/**\n * Walks a nested document and applies each field's `beforeExport` or\n * `beforeImport` hook.\n *\n * Field-level hook errors are logged and the field falls back to its\n * original value so a single bad doc does not abort the batch.\n */\nexport const applyFieldHooks = (args: Args): Record<string, unknown> => {\n const { data, fieldHooks } = args\n if (!data || typeof data !== 'object' || Object.keys(fieldHooks).length === 0) {\n return data\n }\n\n return traverseFields({\n ...args,\n path: undefined,\n schemaPath: undefined,\n siblingData: data,\n })\n}\n"],"names":["isPlainObject","joinPath","parent","segment","operationLabel","export","import","traverseFields","type","data","fieldHooks","fields","format","operation","path","req","schemaPath","siblingData","result","field","name","fieldPath","fieldSchemaPath","entry","hook","fn","undefined","transformed","columnName","siblingDoc","value","error","payload","logger","err","msg","flattenedFields","Array","isArray","map","item","index","blockType","block","blocks","find","b","slug","applyFieldHooks","args","Object","keys","length"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utilities/applyFieldHooks.ts"],"sourcesContent":["import type { FlattenedBlock, FlattenedField, PayloadRequest } from 'payload'\n\nimport type {\n ExportFieldHookEntry,\n FieldBeforeExportHook,\n FieldBeforeImportHook,\n ImportFieldHookEntry,\n} from '../types.js'\n\nimport { isPlainObject } from './isPlainObject.js'\n\nexport type FieldHook = FieldBeforeExportHook | FieldBeforeImportHook\n\nexport type FieldHookEntry = ExportFieldHookEntry | ImportFieldHookEntry\n\nconst joinPath = (parent: string | undefined, segment: string): string =>\n parent ? `${parent}_${segment}` : segment\n\nexport type Args = {\n data: Record<string, unknown>\n fieldHooks: Record<string, FieldHookEntry>\n fields: FlattenedField[]\n format: 'csv' | 'json' | ({} & string)\n operation: 'export' | 'import'\n req: PayloadRequest\n type: 'beforeExport' | 'beforeImport'\n}\n\nconst operationLabel = { export: 'Export', import: 'Import' } as const\n\ntype TraverseArgs = {\n path: string | undefined\n schemaPath: string | undefined\n siblingData: Record<string, unknown>\n} & Args\n\nconst traverseFields = ({\n type,\n data,\n fieldHooks,\n fields,\n format,\n operation,\n path,\n req,\n schemaPath,\n siblingData,\n}: TraverseArgs): Record<string, unknown> => {\n const result: Record<string, unknown> = { ...siblingData }\n\n for (const field of fields) {\n if (!('name' in field) || !field.name) {\n continue\n }\n\n const fieldPath = joinPath(path, field.name)\n const fieldSchemaPath = joinPath(schemaPath, field.name)\n const entry = fieldHooks[fieldSchemaPath]\n const hook = entry?.type === type ? (entry.fn as FieldHook) : undefined\n\n if (typeof hook === 'function' && field.name in result) {\n try {\n const transformed = hook({\n columnName: fieldPath,\n data,\n format,\n siblingData: result,\n siblingDoc: siblingData,\n value: result[field.name],\n })\n\n if (typeof transformed !== 'undefined') {\n result[field.name] = transformed\n }\n } catch (error) {\n req.payload.logger.error({\n err: error,\n msg: `[plugin-import-export] Field-level before${operationLabel[operation]} hook for \"${fieldPath}\" threw — falling back to original value`,\n })\n }\n }\n\n if (!(field.name in result)) {\n continue\n }\n\n const value = result[field.name]\n\n if ((field.type === 'group' || field.type === 'tab') && isPlainObject(value)) {\n result[field.name] = traverseFields({\n type,\n data,\n fieldHooks,\n fields: field.flattenedFields,\n format,\n operation,\n path: fieldPath,\n req,\n schemaPath: fieldSchemaPath,\n siblingData: value,\n })\n } else if (field.type === 'array' && Array.isArray(value)) {\n result[field.name] = value.map((item, index) => {\n if (!isPlainObject(item)) {\n return item\n }\n return traverseFields({\n type,\n data,\n fieldHooks,\n fields: field.flattenedFields,\n format,\n operation,\n path: `${fieldPath}_${index}`,\n req,\n schemaPath: fieldSchemaPath,\n siblingData: item,\n })\n })\n } else if (field.type === 'blocks' && Array.isArray(value)) {\n result[field.name] = value.map((item, index) => {\n if (!isPlainObject(item)) {\n return item\n }\n const blockType = typeof item.blockType === 'string' ? item.blockType : undefined\n const block = blockType\n ? (req.payload.blocks[blockType] ??\n field.blocks.find(\n (b): b is FlattenedBlock => typeof b !== 'string' && b.slug === blockType,\n ))\n : undefined\n return traverseFields({\n type,\n data,\n fieldHooks,\n fields: block?.flattenedFields ?? [],\n format,\n operation,\n path: blockType ? `${fieldPath}_${index}_${blockType}` : `${fieldPath}_${index}`,\n req,\n schemaPath: blockType ? `${fieldSchemaPath}_${blockType}` : fieldSchemaPath,\n siblingData: item,\n })\n })\n }\n }\n\n return result\n}\n\n/**\n * Walks a nested document and applies each field's `beforeExport` or\n * `beforeImport` hook.\n *\n * Field-level hook errors are logged and the field falls back to its\n * original value so a single bad doc does not abort the batch.\n */\nexport const applyFieldHooks = (args: Args): Record<string, unknown> => {\n const { data, fieldHooks } = args\n if (!data || typeof data !== 'object' || Object.keys(fieldHooks).length === 0) {\n return data\n }\n\n return traverseFields({\n ...args,\n path: undefined,\n schemaPath: undefined,\n siblingData: data,\n })\n}\n"],"names":["isPlainObject","joinPath","parent","segment","operationLabel","export","import","traverseFields","type","data","fieldHooks","fields","format","operation","path","req","schemaPath","siblingData","result","field","name","fieldPath","fieldSchemaPath","entry","hook","fn","undefined","transformed","columnName","siblingDoc","value","error","payload","logger","err","msg","flattenedFields","Array","isArray","map","item","index","blockType","block","blocks","find","b","slug","applyFieldHooks","args","Object","keys","length"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"flattenedFields.d.ts","sourceRoot":"","sources":["../../src/utilities/flattenedFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,cAAc,EAAE,GAAG,SACjB,CAAA;AAEnE;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO;IAC7C,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;CACnC,KAAG,cAAc,EAAiC,CAAA;AAEnD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,EACvC,QAAQ,cAAc,EAAE,EACxB,YAAY,MAAM,EAClB,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,KAChG,IA0BF,CAAA"}
1
+ {"version":3,"file":"flattenedFields.d.ts","sourceRoot":"","sources":["../../src/utilities/flattenedFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,cAAc,EAAE,GAAG,SACjB,CAAA;AAEnE;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO;IAC7C,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;CACnC,KAAG,cAAc,EAAiC,CAAA;AAEnD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,EACvC,QAAQ,cAAc,EAAE,EACxB,YAAY,MAAM,EAClB,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,KAChG,IA6BF,CAAA"}
@@ -19,6 +19,9 @@
19
19
  if (field.type === 'blocks') {
20
20
  const base = parentPath ? `${parentPath}_${field.name}` : field.name;
21
21
  for (const block of field.blocks ?? []){
22
+ if (typeof block === 'string') {
23
+ continue;
24
+ }
22
25
  registerFieldHooks(getBlockFlattenedFields(block), `${base}_${block.slug}`, result, registerHandler);
23
26
  }
24
27
  continue;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/flattenedFields.ts"],"sourcesContent":["import type { FlattenedField } from 'payload'\n\n/**\n * Returns the `flattenedFields` of a group/tab/array field, or undefined for\n * field types that don't carry nested fields. Concentrates the cast for the\n * Payload core typing oversight (`FlattenedField` doesn't expose\n * `flattenedFields` on every variant).\n */\nexport const getNestedFlattenedFields = (field: FlattenedField): FlattenedField[] | undefined =>\n (field as { flattenedFields?: FlattenedField[] }).flattenedFields\n\n/**\n * Returns the `flattenedFields` of a `BlocksField` block (always an array).\n */\nexport const getBlockFlattenedFields = (block: {\n flattenedFields?: FlattenedField[]\n}): FlattenedField[] => block.flattenedFields ?? []\n\n/**\n * Traverses a flattened field schema and calls `registerHandler` for each\n * named field. Handles blocks (keyed by block slug), groups, tabs, and arrays.\n * Shared by `getExportFieldFunctions` and `getImportFieldFunctions`.\n */\nexport const registerFieldHooks = <TEntry>(\n fields: FlattenedField[],\n parentPath: string,\n result: Record<string, TEntry>,\n registerHandler: (field: FlattenedField, fullKey: string, result: Record<string, TEntry>) => void,\n): void => {\n for (const field of fields) {\n if (!('name' in field) || !field.name) {\n continue\n }\n\n if (field.type === 'blocks') {\n const base = parentPath ? `${parentPath}_${field.name}` : field.name\n for (const block of field.blocks ?? []) {\n registerFieldHooks(\n getBlockFlattenedFields(block),\n `${base}_${block.slug}`,\n result,\n registerHandler,\n )\n }\n continue\n }\n\n const fullKey = parentPath ? `${parentPath}_${field.name}` : field.name\n registerHandler(field, fullKey, result)\n\n if (field.type === 'group' || field.type === 'tab' || field.type === 'array') {\n registerFieldHooks(getNestedFlattenedFields(field) ?? [], fullKey, result, registerHandler)\n }\n }\n}\n"],"names":["getNestedFlattenedFields","field","flattenedFields","getBlockFlattenedFields","block","registerFieldHooks","fields","parentPath","result","registerHandler","name","type","base","blocks","slug","fullKey"],"mappings":"AAEA;;;;;CAKC,GACD,OAAO,MAAMA,2BAA2B,CAACC,QACvC,AAACA,MAAiDC,eAAe,CAAA;AAEnE;;CAEC,GACD,OAAO,MAAMC,0BAA0B,CAACC,QAEhBA,MAAMF,eAAe,IAAI,EAAE,CAAA;AAEnD;;;;CAIC,GACD,OAAO,MAAMG,qBAAqB,CAChCC,QACAC,YACAC,QACAC;IAEA,KAAK,MAAMR,SAASK,OAAQ;QAC1B,IAAI,CAAE,CAAA,UAAUL,KAAI,KAAM,CAACA,MAAMS,IAAI,EAAE;YACrC;QACF;QAEA,IAAIT,MAAMU,IAAI,KAAK,UAAU;YAC3B,MAAMC,OAAOL,aAAa,GAAGA,WAAW,CAAC,EAAEN,MAAMS,IAAI,EAAE,GAAGT,MAAMS,IAAI;YACpE,KAAK,MAAMN,SAASH,MAAMY,MAAM,IAAI,EAAE,CAAE;gBACtCR,mBACEF,wBAAwBC,QACxB,GAAGQ,KAAK,CAAC,EAAER,MAAMU,IAAI,EAAE,EACvBN,QACAC;YAEJ;YACA;QACF;QAEA,MAAMM,UAAUR,aAAa,GAAGA,WAAW,CAAC,EAAEN,MAAMS,IAAI,EAAE,GAAGT,MAAMS,IAAI;QACvED,gBAAgBR,OAAOc,SAASP;QAEhC,IAAIP,MAAMU,IAAI,KAAK,WAAWV,MAAMU,IAAI,KAAK,SAASV,MAAMU,IAAI,KAAK,SAAS;YAC5EN,mBAAmBL,yBAAyBC,UAAU,EAAE,EAAEc,SAASP,QAAQC;QAC7E;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/flattenedFields.ts"],"sourcesContent":["import type { FlattenedField } from 'payload'\n\n/**\n * Returns the `flattenedFields` of a group/tab/array field, or undefined for\n * field types that don't carry nested fields. Concentrates the cast for the\n * Payload core typing oversight (`FlattenedField` doesn't expose\n * `flattenedFields` on every variant).\n */\nexport const getNestedFlattenedFields = (field: FlattenedField): FlattenedField[] | undefined =>\n (field as { flattenedFields?: FlattenedField[] }).flattenedFields\n\n/**\n * Returns the `flattenedFields` of a `BlocksField` block (always an array).\n */\nexport const getBlockFlattenedFields = (block: {\n flattenedFields?: FlattenedField[]\n}): FlattenedField[] => block.flattenedFields ?? []\n\n/**\n * Traverses a flattened field schema and calls `registerHandler` for each\n * named field. Handles blocks (keyed by block slug), groups, tabs, and arrays.\n * Shared by `getExportFieldFunctions` and `getImportFieldFunctions`.\n */\nexport const registerFieldHooks = <TEntry>(\n fields: FlattenedField[],\n parentPath: string,\n result: Record<string, TEntry>,\n registerHandler: (field: FlattenedField, fullKey: string, result: Record<string, TEntry>) => void,\n): void => {\n for (const field of fields) {\n if (!('name' in field) || !field.name) {\n continue\n }\n\n if (field.type === 'blocks') {\n const base = parentPath ? `${parentPath}_${field.name}` : field.name\n for (const block of field.blocks ?? []) {\n if (typeof block === 'string') {\n continue\n }\n registerFieldHooks(\n getBlockFlattenedFields(block),\n `${base}_${block.slug}`,\n result,\n registerHandler,\n )\n }\n continue\n }\n\n const fullKey = parentPath ? `${parentPath}_${field.name}` : field.name\n registerHandler(field, fullKey, result)\n\n if (field.type === 'group' || field.type === 'tab' || field.type === 'array') {\n registerFieldHooks(getNestedFlattenedFields(field) ?? [], fullKey, result, registerHandler)\n }\n }\n}\n"],"names":["getNestedFlattenedFields","field","flattenedFields","getBlockFlattenedFields","block","registerFieldHooks","fields","parentPath","result","registerHandler","name","type","base","blocks","slug","fullKey"],"mappings":"AAEA;;;;;CAKC,GACD,OAAO,MAAMA,2BAA2B,CAACC,QACvC,AAACA,MAAiDC,eAAe,CAAA;AAEnE;;CAEC,GACD,OAAO,MAAMC,0BAA0B,CAACC,QAEhBA,MAAMF,eAAe,IAAI,EAAE,CAAA;AAEnD;;;;CAIC,GACD,OAAO,MAAMG,qBAAqB,CAChCC,QACAC,YACAC,QACAC;IAEA,KAAK,MAAMR,SAASK,OAAQ;QAC1B,IAAI,CAAE,CAAA,UAAUL,KAAI,KAAM,CAACA,MAAMS,IAAI,EAAE;YACrC;QACF;QAEA,IAAIT,MAAMU,IAAI,KAAK,UAAU;YAC3B,MAAMC,OAAOL,aAAa,GAAGA,WAAW,CAAC,EAAEN,MAAMS,IAAI,EAAE,GAAGT,MAAMS,IAAI;YACpE,KAAK,MAAMN,SAASH,MAAMY,MAAM,IAAI,EAAE,CAAE;gBACtC,IAAI,OAAOT,UAAU,UAAU;oBAC7B;gBACF;gBACAC,mBACEF,wBAAwBC,QACxB,GAAGQ,KAAK,CAAC,EAAER,MAAMU,IAAI,EAAE,EACvBN,QACAC;YAEJ;YACA;QACF;QAEA,MAAMM,UAAUR,aAAa,GAAGA,WAAW,CAAC,EAAEN,MAAMS,IAAI,EAAE,GAAGT,MAAMS,IAAI;QACvED,gBAAgBR,OAAOc,SAASP;QAEhC,IAAIP,MAAMU,IAAI,KAAK,WAAWV,MAAMU,IAAI,KAAK,SAASV,MAAMU,IAAI,KAAK,SAAS;YAC5EN,mBAAmBL,yBAAyBC,UAAU,EAAE,EAAEc,SAASP,QAAQC;QAC7E;IACF;AACF,EAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"processRichTextField.d.ts","sourceRoot":"","sources":["../../src/utilities/processRichTextField.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,OA4CrD,CAAA"}
1
+ {"version":3,"file":"processRichTextField.d.ts","sourceRoot":"","sources":["../../src/utilities/processRichTextField.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,OAoDrD,CAAA"}
@@ -2,6 +2,13 @@
2
2
  * Process rich text fields to ensure proper data types for Lexical editor.
3
3
  * Lexical expects certain properties to be numbers, not strings.
4
4
  */ export const processRichTextField = (value)=>{
5
+ if (typeof value === 'string') {
6
+ try {
7
+ value = JSON.parse(value);
8
+ } catch {
9
+ return value;
10
+ }
11
+ }
5
12
  if (!value || typeof value !== 'object') {
6
13
  return value;
7
14
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/processRichTextField.ts"],"sourcesContent":["/**\n * Process rich text fields to ensure proper data types for Lexical editor.\n * Lexical expects certain properties to be numbers, not strings.\n */\nexport const processRichTextField = (value: unknown): unknown => {\n if (!value || typeof value !== 'object') {\n return value\n }\n\n // Properties that should be numbers in Lexical\n const numericProperties = [\n 'detail',\n 'format',\n 'indent',\n 'version',\n 'value',\n 'start',\n 'textFormat',\n 'textStyle',\n ]\n\n const processNode = (node: unknown): unknown => {\n if (!node || typeof node !== 'object') {\n return node\n }\n\n // Process current node's properties\n const processed: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(node as Record<string, unknown>)) {\n if (numericProperties.includes(key) && typeof val === 'string') {\n // Convert string numbers to actual numbers\n const num = parseFloat(val)\n processed[key] = isNaN(num) ? val : num\n } else if (key === 'children' && Array.isArray(val)) {\n // Recursively process children\n processed[key] = val.map((child) => processNode(child))\n } else if (typeof val === 'object' && val !== null) {\n // Recursively process nested objects\n processed[key] = processNode(val)\n } else {\n processed[key] = val\n }\n }\n\n return processed\n }\n\n return processNode(value)\n}\n"],"names":["processRichTextField","value","numericProperties","processNode","node","processed","key","val","Object","entries","includes","num","parseFloat","isNaN","Array","isArray","map","child"],"mappings":"AAAA;;;CAGC,GACD,OAAO,MAAMA,uBAAuB,CAACC;IACnC,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU;QACvC,OAAOA;IACT;IAEA,+CAA+C;IAC/C,MAAMC,oBAAoB;QACxB;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD;IAED,MAAMC,cAAc,CAACC;QACnB,IAAI,CAACA,QAAQ,OAAOA,SAAS,UAAU;YACrC,OAAOA;QACT;QAEA,oCAAoC;QACpC,MAAMC,YAAqC,CAAC;QAC5C,KAAK,MAAM,CAACC,KAAKC,IAAI,IAAIC,OAAOC,OAAO,CAACL,MAAkC;YACxE,IAAIF,kBAAkBQ,QAAQ,CAACJ,QAAQ,OAAOC,QAAQ,UAAU;gBAC9D,2CAA2C;gBAC3C,MAAMI,MAAMC,WAAWL;gBACvBF,SAAS,CAACC,IAAI,GAAGO,MAAMF,OAAOJ,MAAMI;YACtC,OAAO,IAAIL,QAAQ,cAAcQ,MAAMC,OAAO,CAACR,MAAM;gBACnD,+BAA+B;gBAC/BF,SAAS,CAACC,IAAI,GAAGC,IAAIS,GAAG,CAAC,CAACC,QAAUd,YAAYc;YAClD,OAAO,IAAI,OAAOV,QAAQ,YAAYA,QAAQ,MAAM;gBAClD,qCAAqC;gBACrCF,SAAS,CAACC,IAAI,GAAGH,YAAYI;YAC/B,OAAO;gBACLF,SAAS,CAACC,IAAI,GAAGC;YACnB;QACF;QAEA,OAAOF;IACT;IAEA,OAAOF,YAAYF;AACrB,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/processRichTextField.ts"],"sourcesContent":["/**\n * Process rich text fields to ensure proper data types for Lexical editor.\n * Lexical expects certain properties to be numbers, not strings.\n */\nexport const processRichTextField = (value: unknown): unknown => {\n if (typeof value === 'string') {\n try {\n value = JSON.parse(value)\n } catch {\n return value\n }\n }\n\n if (!value || typeof value !== 'object') {\n return value\n }\n\n // Properties that should be numbers in Lexical\n const numericProperties = [\n 'detail',\n 'format',\n 'indent',\n 'version',\n 'value',\n 'start',\n 'textFormat',\n 'textStyle',\n ]\n\n const processNode = (node: unknown): unknown => {\n if (!node || typeof node !== 'object') {\n return node\n }\n\n // Process current node's properties\n const processed: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(node as Record<string, unknown>)) {\n if (numericProperties.includes(key) && typeof val === 'string') {\n // Convert string numbers to actual numbers\n const num = parseFloat(val)\n processed[key] = isNaN(num) ? val : num\n } else if (key === 'children' && Array.isArray(val)) {\n // Recursively process children\n processed[key] = val.map((child) => processNode(child))\n } else if (typeof val === 'object' && val !== null) {\n // Recursively process nested objects\n processed[key] = processNode(val)\n } else {\n processed[key] = val\n }\n }\n\n return processed\n }\n\n return processNode(value)\n}\n"],"names":["processRichTextField","value","JSON","parse","numericProperties","processNode","node","processed","key","val","Object","entries","includes","num","parseFloat","isNaN","Array","isArray","map","child"],"mappings":"AAAA;;;CAGC,GACD,OAAO,MAAMA,uBAAuB,CAACC;IACnC,IAAI,OAAOA,UAAU,UAAU;QAC7B,IAAI;YACFA,QAAQC,KAAKC,KAAK,CAACF;QACrB,EAAE,OAAM;YACN,OAAOA;QACT;IACF;IAEA,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU;QACvC,OAAOA;IACT;IAEA,+CAA+C;IAC/C,MAAMG,oBAAoB;QACxB;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD;IAED,MAAMC,cAAc,CAACC;QACnB,IAAI,CAACA,QAAQ,OAAOA,SAAS,UAAU;YACrC,OAAOA;QACT;QAEA,oCAAoC;QACpC,MAAMC,YAAqC,CAAC;QAC5C,KAAK,MAAM,CAACC,KAAKC,IAAI,IAAIC,OAAOC,OAAO,CAACL,MAAkC;YACxE,IAAIF,kBAAkBQ,QAAQ,CAACJ,QAAQ,OAAOC,QAAQ,UAAU;gBAC9D,2CAA2C;gBAC3C,MAAMI,MAAMC,WAAWL;gBACvBF,SAAS,CAACC,IAAI,GAAGO,MAAMF,OAAOJ,MAAMI;YACtC,OAAO,IAAIL,QAAQ,cAAcQ,MAAMC,OAAO,CAACR,MAAM;gBACnD,+BAA+B;gBAC/BF,SAAS,CAACC,IAAI,GAAGC,IAAIS,GAAG,CAAC,CAACC,QAAUd,YAAYc;YAClD,OAAO,IAAI,OAAOV,QAAQ,YAAYA,QAAQ,MAAM;gBAClD,qCAAqC;gBACrCF,SAAS,CAACC,IAAI,GAAGH,YAAYI;YAC/B,OAAO;gBACLF,SAAS,CAACC,IAAI,GAAGC;YACnB;QACF;QAEA,OAAOF;IACT;IAEA,OAAOF,YAAYJ;AACrB,EAAC"}
@@ -0,0 +1,308 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { processRichTextField } from './processRichTextField.js';
3
+ // Minimal helpers to keep assertions readable
4
+ const asRecord = (v)=>v;
5
+ const asArray = (v)=>v;
6
+ // ─── Fixtures ────────────────────────────────────────────────────────────────
7
+ const textNode = (overrides = {})=>({
8
+ detail: 0,
9
+ format: 0,
10
+ mode: 'normal',
11
+ text: 'Hello',
12
+ type: 'text',
13
+ version: 1,
14
+ ...overrides
15
+ });
16
+ const paragraphNode = (children = [
17
+ textNode()
18
+ ])=>({
19
+ children,
20
+ direction: 'ltr',
21
+ format: '',
22
+ indent: 0,
23
+ type: 'paragraph',
24
+ version: 1
25
+ });
26
+ const lexicalDoc = (children = [
27
+ paragraphNode()
28
+ ])=>({
29
+ children,
30
+ direction: 'ltr',
31
+ format: '',
32
+ indent: 0,
33
+ type: 'root',
34
+ version: 1
35
+ });
36
+ // ─── Primitive pass-through ──────────────────────────────────────────────────
37
+ describe('processRichTextField — primitive pass-through', ()=>{
38
+ it('should return null unchanged', ()=>{
39
+ expect(processRichTextField(null)).toBeNull();
40
+ });
41
+ it('should return undefined unchanged', ()=>{
42
+ expect(processRichTextField(undefined)).toBeUndefined();
43
+ });
44
+ it('should return a number unchanged', ()=>{
45
+ expect(processRichTextField(42)).toBe(42);
46
+ });
47
+ it('should return false unchanged', ()=>{
48
+ expect(processRichTextField(false)).toBe(false);
49
+ });
50
+ });
51
+ // ─── String input (the recovered-from-missed-hook path) ─────────────────────
52
+ describe('processRichTextField — JSON string input', ()=>{
53
+ it('should parse a valid JSON string into an object', ()=>{
54
+ const doc = lexicalDoc();
55
+ expect(processRichTextField(JSON.stringify(doc))).toEqual(doc);
56
+ });
57
+ it('should return an unparseable string unchanged', ()=>{
58
+ expect(processRichTextField('not json')).toBe('not json');
59
+ });
60
+ it('should return an empty string unchanged', ()=>{
61
+ expect(processRichTextField('')).toBe('');
62
+ });
63
+ it('should parse a string and still convert numeric string properties', ()=>{
64
+ const node = {
65
+ type: 'text',
66
+ detail: '0',
67
+ format: '3',
68
+ indent: '0',
69
+ version: '1',
70
+ text: 'hi'
71
+ };
72
+ const result = asRecord(processRichTextField(JSON.stringify(node)));
73
+ expect(result.detail).toBe(0);
74
+ expect(result.format).toBe(3);
75
+ expect(result.indent).toBe(0);
76
+ expect(result.version).toBe(1);
77
+ });
78
+ });
79
+ // ─── Numeric property coercion ───────────────────────────────────────────────
80
+ describe('processRichTextField — numeric property coercion', ()=>{
81
+ it('should convert detail, format, indent, version from strings to numbers', ()=>{
82
+ const input = textNode({
83
+ detail: '0',
84
+ format: '0',
85
+ indent: '0',
86
+ version: '1'
87
+ });
88
+ const result = asRecord(processRichTextField(input));
89
+ expect(result.detail).toBe(0);
90
+ expect(result.format).toBe(0);
91
+ expect(result.indent).toBe(0);
92
+ expect(result.version).toBe(1);
93
+ });
94
+ it('should convert start and value from strings to numbers (list/listitem nodes)', ()=>{
95
+ const listItemNode = {
96
+ type: 'listitem',
97
+ start: '1',
98
+ value: '2',
99
+ version: '1'
100
+ };
101
+ const result = asRecord(processRichTextField(listItemNode));
102
+ expect(result.start).toBe(1);
103
+ expect(result.value).toBe(2);
104
+ });
105
+ it('should convert textFormat and textStyle from strings to numbers', ()=>{
106
+ const input = {
107
+ type: 'text',
108
+ textFormat: '4',
109
+ textStyle: '0',
110
+ version: '1',
111
+ text: 'x'
112
+ };
113
+ const result = asRecord(processRichTextField(input));
114
+ expect(result.textFormat).toBe(4);
115
+ expect(result.textStyle).toBe(0);
116
+ });
117
+ it('should leave a non-numeric string in a numeric-property slot unchanged', ()=>{
118
+ // Lexical paragraph format can be "" (empty) or alignment strings
119
+ const input = paragraphNode();
120
+ const result = asRecord(processRichTextField(input));
121
+ expect(result.format).toBe('');
122
+ });
123
+ it('should leave already-numeric values unchanged', ()=>{
124
+ const input = textNode({
125
+ detail: 0,
126
+ format: 3,
127
+ version: 1
128
+ });
129
+ const result = asRecord(processRichTextField(input));
130
+ expect(result.detail).toBe(0);
131
+ expect(result.format).toBe(3);
132
+ expect(result.version).toBe(1);
133
+ });
134
+ it('should not convert boolean values on numeric-property keys', ()=>{
135
+ const input = {
136
+ type: 'custom',
137
+ format: true,
138
+ version: '1'
139
+ };
140
+ const result = asRecord(processRichTextField(input));
141
+ expect(result.format).toBe(true);
142
+ expect(result.version).toBe(1);
143
+ });
144
+ });
145
+ // ─── Recursive children processing ──────────────────────────────────────────
146
+ describe('processRichTextField — recursive children', ()=>{
147
+ it('should recursively convert numeric strings inside children arrays', ()=>{
148
+ const input = paragraphNode([
149
+ textNode({
150
+ detail: '0',
151
+ format: '0',
152
+ version: '1'
153
+ })
154
+ ]);
155
+ const result = asRecord(processRichTextField(input));
156
+ const child = asRecord(asArray(result.children)[0]);
157
+ expect(child.detail).toBe(0);
158
+ expect(child.format).toBe(0);
159
+ expect(child.version).toBe(1);
160
+ });
161
+ it('should handle three levels of nesting (root → paragraph → text)', ()=>{
162
+ const input = lexicalDoc([
163
+ paragraphNode([
164
+ textNode({
165
+ version: '1',
166
+ format: '2'
167
+ })
168
+ ])
169
+ ]);
170
+ const result = asRecord(processRichTextField(input));
171
+ const paragraph = asRecord(asArray(result.children)[0]);
172
+ const text = asRecord(asArray(paragraph.children)[0]);
173
+ expect(text.version).toBe(1);
174
+ expect(text.format).toBe(2);
175
+ });
176
+ it('should handle multiple children at the same level', ()=>{
177
+ const input = lexicalDoc([
178
+ paragraphNode([
179
+ textNode({
180
+ text: 'Bold',
181
+ format: '1'
182
+ }),
183
+ textNode({
184
+ text: ' normal',
185
+ format: '0'
186
+ })
187
+ ]),
188
+ paragraphNode([
189
+ textNode({
190
+ text: 'Second para',
191
+ version: '1'
192
+ })
193
+ ])
194
+ ]);
195
+ const result = asRecord(processRichTextField(input));
196
+ const firstPara = asRecord(asArray(result.children)[0]);
197
+ const secondPara = asRecord(asArray(result.children)[1]);
198
+ const bold = asRecord(asArray(firstPara.children)[0]);
199
+ const normal = asRecord(asArray(firstPara.children)[1]);
200
+ const secondText = asRecord(asArray(secondPara.children)[0]);
201
+ expect(bold.format).toBe(1);
202
+ expect(normal.format).toBe(0);
203
+ expect(secondText.version).toBe(1);
204
+ });
205
+ it('should skip null and non-object entries in children without throwing', ()=>{
206
+ const input = {
207
+ type: 'root',
208
+ version: 1,
209
+ children: [
210
+ null,
211
+ undefined,
212
+ 'stray',
213
+ textNode()
214
+ ]
215
+ };
216
+ const result = asRecord(processRichTextField(input));
217
+ const children = asArray(result.children);
218
+ expect(children[0]).toBeNull();
219
+ expect(children[1]).toBeUndefined();
220
+ expect(children[2]).toBe('stray');
221
+ expect(asRecord(children[3]).type).toBe('text');
222
+ });
223
+ });
224
+ // ─── Nested non-children objects ─────────────────────────────────────────────
225
+ describe('processRichTextField — nested non-children objects', ()=>{
226
+ it('should recurse into plain nested objects (e.g. decorator node data)', ()=>{
227
+ const input = {
228
+ type: 'decorator',
229
+ version: '1',
230
+ data: {
231
+ detail: '2',
232
+ label: 'keep me'
233
+ }
234
+ };
235
+ const result = asRecord(processRichTextField(input));
236
+ const data = asRecord(result.data);
237
+ expect(result.version).toBe(1);
238
+ expect(data.detail).toBe(2);
239
+ expect(data.label).toBe('keep me');
240
+ });
241
+ });
242
+ // ─── Full Lexical document round-trips ───────────────────────────────────────
243
+ describe('processRichTextField — full document', ()=>{
244
+ it('should produce a stable result when called on an already-processed object', ()=>{
245
+ const doc = lexicalDoc();
246
+ const firstPass = processRichTextField(doc);
247
+ const secondPass = processRichTextField(firstPass);
248
+ expect(secondPass).toEqual(firstPass);
249
+ });
250
+ it('should fully round-trip a complex Lexical document serialised to JSON', ()=>{
251
+ const doc = lexicalDoc([
252
+ paragraphNode([
253
+ textNode({
254
+ text: 'Bold',
255
+ format: '1',
256
+ detail: '0',
257
+ indent: '0',
258
+ version: '1'
259
+ }),
260
+ textNode({
261
+ text: ' normal',
262
+ format: '0',
263
+ version: '1'
264
+ })
265
+ ]),
266
+ {
267
+ type: 'list',
268
+ listType: 'number',
269
+ start: '1',
270
+ version: '1',
271
+ indent: '0',
272
+ direction: 'ltr',
273
+ children: [
274
+ {
275
+ type: 'listitem',
276
+ value: '1',
277
+ detail: '0',
278
+ format: '0',
279
+ version: '1',
280
+ text: 'Item A'
281
+ },
282
+ {
283
+ type: 'listitem',
284
+ value: '2',
285
+ detail: '0',
286
+ format: '0',
287
+ version: '1',
288
+ text: 'Item B'
289
+ }
290
+ ]
291
+ }
292
+ ]);
293
+ const result = asRecord(processRichTextField(JSON.stringify(doc)));
294
+ const para = asRecord(asArray(result.children)[0]);
295
+ const boldText = asRecord(asArray(para.children)[0]);
296
+ expect(boldText.format).toBe(1);
297
+ expect(boldText.detail).toBe(0);
298
+ const list = asRecord(asArray(result.children)[1]);
299
+ expect(list.start).toBe(1);
300
+ expect(list.indent).toBe(0);
301
+ const itemA = asRecord(asArray(list.children)[0]);
302
+ const itemB = asRecord(asArray(list.children)[1]);
303
+ expect(itemA.value).toBe(1);
304
+ expect(itemB.value).toBe(2);
305
+ });
306
+ });
307
+
308
+ //# sourceMappingURL=processRichTextField.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/processRichTextField.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { processRichTextField } from './processRichTextField.js'\n\n// Minimal helpers to keep assertions readable\nconst asRecord = (v: unknown) => v as Record<string, unknown>\nconst asArray = (v: unknown) => v as unknown[]\n\n// ─── Fixtures ────────────────────────────────────────────────────────────────\n\nconst textNode = (overrides: Record<string, unknown> = {}) => ({\n detail: 0,\n format: 0,\n mode: 'normal',\n text: 'Hello',\n type: 'text',\n version: 1,\n ...overrides,\n})\n\nconst paragraphNode = (children: unknown[] = [textNode()]) => ({\n children,\n direction: 'ltr',\n format: '',\n indent: 0,\n type: 'paragraph',\n version: 1,\n})\n\nconst lexicalDoc = (children: unknown[] = [paragraphNode()]) => ({\n children,\n direction: 'ltr',\n format: '',\n indent: 0,\n type: 'root',\n version: 1,\n})\n\n// ─── Primitive pass-through ──────────────────────────────────────────────────\n\ndescribe('processRichTextField — primitive pass-through', () => {\n it('should return null unchanged', () => {\n expect(processRichTextField(null)).toBeNull()\n })\n\n it('should return undefined unchanged', () => {\n expect(processRichTextField(undefined)).toBeUndefined()\n })\n\n it('should return a number unchanged', () => {\n expect(processRichTextField(42)).toBe(42)\n })\n\n it('should return false unchanged', () => {\n expect(processRichTextField(false)).toBe(false)\n })\n})\n\n// ─── String input (the recovered-from-missed-hook path) ─────────────────────\n\ndescribe('processRichTextField — JSON string input', () => {\n it('should parse a valid JSON string into an object', () => {\n const doc = lexicalDoc()\n expect(processRichTextField(JSON.stringify(doc))).toEqual(doc)\n })\n\n it('should return an unparseable string unchanged', () => {\n expect(processRichTextField('not json')).toBe('not json')\n })\n\n it('should return an empty string unchanged', () => {\n expect(processRichTextField('')).toBe('')\n })\n\n it('should parse a string and still convert numeric string properties', () => {\n const node = { type: 'text', detail: '0', format: '3', indent: '0', version: '1', text: 'hi' }\n const result = asRecord(processRichTextField(JSON.stringify(node)))\n expect(result.detail).toBe(0)\n expect(result.format).toBe(3)\n expect(result.indent).toBe(0)\n expect(result.version).toBe(1)\n })\n})\n\n// ─── Numeric property coercion ───────────────────────────────────────────────\n\ndescribe('processRichTextField — numeric property coercion', () => {\n it('should convert detail, format, indent, version from strings to numbers', () => {\n const input = textNode({ detail: '0', format: '0', indent: '0', version: '1' })\n const result = asRecord(processRichTextField(input))\n expect(result.detail).toBe(0)\n expect(result.format).toBe(0)\n expect(result.indent).toBe(0)\n expect(result.version).toBe(1)\n })\n\n it('should convert start and value from strings to numbers (list/listitem nodes)', () => {\n const listItemNode = { type: 'listitem', start: '1', value: '2', version: '1' }\n const result = asRecord(processRichTextField(listItemNode))\n expect(result.start).toBe(1)\n expect(result.value).toBe(2)\n })\n\n it('should convert textFormat and textStyle from strings to numbers', () => {\n const input = { type: 'text', textFormat: '4', textStyle: '0', version: '1', text: 'x' }\n const result = asRecord(processRichTextField(input))\n expect(result.textFormat).toBe(4)\n expect(result.textStyle).toBe(0)\n })\n\n it('should leave a non-numeric string in a numeric-property slot unchanged', () => {\n // Lexical paragraph format can be \"\" (empty) or alignment strings\n const input = paragraphNode()\n const result = asRecord(processRichTextField(input))\n expect(result.format).toBe('')\n })\n\n it('should leave already-numeric values unchanged', () => {\n const input = textNode({ detail: 0, format: 3, version: 1 })\n const result = asRecord(processRichTextField(input))\n expect(result.detail).toBe(0)\n expect(result.format).toBe(3)\n expect(result.version).toBe(1)\n })\n\n it('should not convert boolean values on numeric-property keys', () => {\n const input = { type: 'custom', format: true, version: '1' }\n const result = asRecord(processRichTextField(input))\n expect(result.format).toBe(true)\n expect(result.version).toBe(1)\n })\n})\n\n// ─── Recursive children processing ──────────────────────────────────────────\n\ndescribe('processRichTextField — recursive children', () => {\n it('should recursively convert numeric strings inside children arrays', () => {\n const input = paragraphNode([textNode({ detail: '0', format: '0', version: '1' })])\n const result = asRecord(processRichTextField(input))\n const child = asRecord(asArray(result.children)[0])\n expect(child.detail).toBe(0)\n expect(child.format).toBe(0)\n expect(child.version).toBe(1)\n })\n\n it('should handle three levels of nesting (root → paragraph → text)', () => {\n const input = lexicalDoc([paragraphNode([textNode({ version: '1', format: '2' })])])\n const result = asRecord(processRichTextField(input))\n const paragraph = asRecord(asArray(result.children)[0])\n const text = asRecord(asArray(paragraph.children)[0])\n expect(text.version).toBe(1)\n expect(text.format).toBe(2)\n })\n\n it('should handle multiple children at the same level', () => {\n const input = lexicalDoc([\n paragraphNode([\n textNode({ text: 'Bold', format: '1' }),\n textNode({ text: ' normal', format: '0' }),\n ]),\n paragraphNode([textNode({ text: 'Second para', version: '1' })]),\n ])\n const result = asRecord(processRichTextField(input))\n const firstPara = asRecord(asArray(result.children)[0])\n const secondPara = asRecord(asArray(result.children)[1])\n const bold = asRecord(asArray(firstPara.children)[0])\n const normal = asRecord(asArray(firstPara.children)[1])\n const secondText = asRecord(asArray(secondPara.children)[0])\n expect(bold.format).toBe(1)\n expect(normal.format).toBe(0)\n expect(secondText.version).toBe(1)\n })\n\n it('should skip null and non-object entries in children without throwing', () => {\n const input = { type: 'root', version: 1, children: [null, undefined, 'stray', textNode()] }\n const result = asRecord(processRichTextField(input))\n const children = asArray(result.children)\n expect(children[0]).toBeNull()\n expect(children[1]).toBeUndefined()\n expect(children[2]).toBe('stray')\n expect(asRecord(children[3]).type).toBe('text')\n })\n})\n\n// ─── Nested non-children objects ─────────────────────────────────────────────\n\ndescribe('processRichTextField — nested non-children objects', () => {\n it('should recurse into plain nested objects (e.g. decorator node data)', () => {\n const input = {\n type: 'decorator',\n version: '1',\n data: { detail: '2', label: 'keep me' },\n }\n const result = asRecord(processRichTextField(input))\n const data = asRecord(result.data)\n expect(result.version).toBe(1)\n expect(data.detail).toBe(2)\n expect(data.label).toBe('keep me')\n })\n})\n\n// ─── Full Lexical document round-trips ───────────────────────────────────────\n\ndescribe('processRichTextField — full document', () => {\n it('should produce a stable result when called on an already-processed object', () => {\n const doc = lexicalDoc()\n const firstPass = processRichTextField(doc)\n const secondPass = processRichTextField(firstPass)\n expect(secondPass).toEqual(firstPass)\n })\n\n it('should fully round-trip a complex Lexical document serialised to JSON', () => {\n const doc = lexicalDoc([\n paragraphNode([\n textNode({ text: 'Bold', format: '1', detail: '0', indent: '0', version: '1' }),\n textNode({ text: ' normal', format: '0', version: '1' }),\n ]),\n {\n type: 'list',\n listType: 'number',\n start: '1',\n version: '1',\n indent: '0',\n direction: 'ltr',\n children: [\n { type: 'listitem', value: '1', detail: '0', format: '0', version: '1', text: 'Item A' },\n { type: 'listitem', value: '2', detail: '0', format: '0', version: '1', text: 'Item B' },\n ],\n },\n ])\n\n const result = asRecord(processRichTextField(JSON.stringify(doc)))\n\n const para = asRecord(asArray(result.children)[0])\n const boldText = asRecord(asArray(para.children)[0])\n expect(boldText.format).toBe(1)\n expect(boldText.detail).toBe(0)\n\n const list = asRecord(asArray(result.children)[1])\n expect(list.start).toBe(1)\n expect(list.indent).toBe(0)\n\n const itemA = asRecord(asArray(list.children)[0])\n const itemB = asRecord(asArray(list.children)[1])\n expect(itemA.value).toBe(1)\n expect(itemB.value).toBe(2)\n })\n})\n"],"names":["describe","expect","it","processRichTextField","asRecord","v","asArray","textNode","overrides","detail","format","mode","text","type","version","paragraphNode","children","direction","indent","lexicalDoc","toBeNull","undefined","toBeUndefined","toBe","doc","JSON","stringify","toEqual","node","result","input","listItemNode","start","value","textFormat","textStyle","child","paragraph","firstPara","secondPara","bold","normal","secondText","data","label","firstPass","secondPass","listType","para","boldText","list","itemA","itemB"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"unflattenObject.d.ts","sourceRoot":"","sources":["../../src/utilities/unflattenObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKvD,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,CAAA;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;IACvD,GAAG,EAAE,cAAc,CAAA;CACpB,CAAA;AAiCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,GAAI,kDAM7B,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CA0TxC,CAAA"}
1
+ {"version":3,"file":"unflattenObject.d.ts","sourceRoot":"","sources":["../../src/utilities/unflattenObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKvD,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,CAAA;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;IACvD,GAAG,EAAE,cAAc,CAAA;CACpB,CAAA;AAuCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,GAAI,kDAM7B,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAyUxC,CAAA"}
@@ -1,4 +1,4 @@
1
- import { getNestedFlattenedFields } from './flattenedFields.js';
1
+ import { getBlockFlattenedFields, getNestedFlattenedFields } from './flattenedFields.js';
2
2
  import { postProcessDocument } from './unflattenPostProcess.js';
3
3
  const indexSegment = /^\d+$/;
4
4
  const collectArrayLikeNames = (fields, into)=>{
@@ -9,6 +9,11 @@ const collectArrayLikeNames = (fields, into)=>{
9
9
  if (field.type === 'array' || field.type === 'blocks') {
10
10
  into.add(field.name);
11
11
  }
12
+ if (field.type === 'blocks') {
13
+ for (const block of field.blocks ?? []){
14
+ collectArrayLikeNames(getBlockFlattenedFields(block), into);
15
+ }
16
+ }
12
17
  const nested = getNestedFlattenedFields(field);
13
18
  if (nested) {
14
19
  collectArrayLikeNames(nested, into);
@@ -230,7 +235,9 @@ const collectArrayLikeNames = (fields, into)=>{
230
235
  blockObject[blockFieldName] = value;
231
236
  } else {
232
237
  if (!blockObject[blockFieldName] || typeof blockObject[blockFieldName] !== 'object') {
233
- blockObject[blockFieldName] = {};
238
+ const segmentAfterBlockField = pathSegments[i + 4];
239
+ const blockFieldIsArray = segmentAfterBlockField !== undefined && indexSegment.test(segmentAfterBlockField);
240
+ blockObject[blockFieldName] = blockFieldIsArray ? [] : {};
234
241
  }
235
242
  currentObject = blockObject[blockFieldName];
236
243
  i = i + 3;
@@ -254,6 +261,18 @@ const collectArrayLikeNames = (fields, into)=>{
254
261
  currentObject = arr[arrayIndex];
255
262
  i++;
256
263
  }
264
+ } else if (Array.isArray(currentObject) && indexSegment.test(segment)) {
265
+ // currentObject is an array we arrived at via block field traversal — treat the
266
+ // segment as a numeric index rather than a plain property name.
267
+ const idx = parseInt(segment, 10);
268
+ const arr = currentObject;
269
+ while(arr.length <= idx){
270
+ arr.push(null);
271
+ }
272
+ if (arr[idx] === null || arr[idx] === undefined) {
273
+ arr[idx] = {};
274
+ }
275
+ currentObject = arr[idx];
257
276
  } else {
258
277
  // Skip if already set to null (polymorphic relationship already processed)
259
278
  if (currentObject[segment] === null && isLast && segment === 'relationTo') {