     1	import { validatePostPayload } from '../lib/querying-writing/payload-validators.js'
     2	import { processRelationships } from '../lib/writing/relationship-processor.js'
     3	import { createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
     4	import {
     5	  setupCommonRequest,
     6	  validateResourceAttributesBeforeWrite,
     7	  validateRelationshipAccess,
     8	  applyFieldSetters,
     9	  validatePivotResource,
    10	  handleRecordReturnAfterWrite,
    11	  handleWriteMethodError
    12	} from './common.js'
    13	
    14	/**
    15	 * POST
    16	 * Creates a new resource. The request must include a JSON:API document with a 'data' object
    17	 * containing 'type' and 'attributes'. It can also establish relationships to existing resources.
    18	 * The returned document contains the created resource with its server-assigned ID.
    19	 */
    20	export default async function postMethod ({
    21	  params,
    22	  context,
    23	  vars,
    24	  helpers,
    25	  scope,
    26	  scopes,
    27	  runHooks,
    28	  apiOptions,
    29	  pluginOptions,
    30	  scopeOptions,
    31	  scopeName,
    32	  api,
    33	  log
    34	}) {
    35	  context.method = 'post'
    36	
    37	  try {
    38	    const { schema, schemaStructure, schemaRelationships } = await setupCommonRequest({
    39	      params,
    40	      context,
    41	      vars,
    42	      scopes,
    43	      scopeOptions,
    44	      scopeName,
    45	      api,
    46	      helpers
    47	    })
    48	
    49	    // Run early hooks for pre-processing (e.g., file handling)
    50	    await runHooks('beforeProcessing')
    51	    await runHooks('beforeProcessingPost')
    52	
    53	    // Validate POST payload to ensure it follows JSON:API format and references valid resources.
    54	    // This checks the payload has required 'data' object with 'type' and 'attributes', validates
    55	    // that data.type matches a real resource type (preventing creation of non-existent resources),
    56	    // and ensures any relationships reference valid resource types with proper ID format.
    57	    // Example: data.type: 'articles' must be a registered scope, relationships.author must reference 'users'.
    58	    validatePostPayload(context.inputRecord, scopes)
    59	
    60	    // Validate that user has read access to all related resources
    61	    // This ensures users can only create relationships to resources they can access
    62	    await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
    63	
    64	    // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
    65	    // Example: relationships.author -> author_id: '123' for storage
    66	    // Example: relationships.tags -> array of pivot records to create later
    67	    const { belongsToUpdates, manyToManyRelationships } = await processRelationships(
    68	      scope,
    69	      { context }
    70	    )
    71	
    72	    // Merge belongsTo updates into attributes before validation (like PUT/PATCH do)
    73	    if (Object.keys(belongsToUpdates).length > 0) {
    74	      context.inputRecord.data.attributes = {
    75	        ...context.inputRecord.data.attributes,
    76	        ...belongsToUpdates
    77	      }
    78	    }
    79	
    80	    await validateResourceAttributesBeforeWrite({
    81	      context,
    82	      schema,
    83	      belongsToUpdates,
    84	      runHooks
    85	    })
    86	
    87	    if (context.inputRecord?.data) {
    88	      const inputData = context.inputRecord.data
    89	      context.minimalRecord = {
    90	        type: scopeName,
    91	        ...(inputData.id !== undefined && inputData.id !== null ? { id: String(inputData.id) } : {}),
    92	        attributes: structuredClone(inputData.attributes || {}),
    93	        relationships: structuredClone(inputData.relationships || {})
    94	      }
    95	    }
    96	
    97	    // Centralised checkPermissions function
    98	    await scope.checkPermissions({
    99	      method: 'post',
   100	      originalContext: context,
   101	    })
   102	
   103	    await runHooks('beforeDataCall')
   104	    await runHooks('beforeDataCallPost')
   105	
   106	    // Apply field setters after validation and before storage
   107	    if (context.inputRecord?.data?.attributes) {
   108	      context.inputRecord.data.attributes = await applyFieldSetters(
   109	        context.inputRecord.data.attributes,
   110	        context.schemaInfo,
   111	        context,
   112	        api,
   113	        helpers
   114	      )
   115	    }
   116	
   117	    // Create the main record - storage helper should return the created record with its ID
   118	    context.id = await helpers.dataPost({
   119	      scopeName,
   120	      context
   121	    })
   122	
   123	    await runHooks('afterDataCallPost')
   124	    await runHooks('afterDataCall')
   125	
   126	    // Process many-to-many relationships after main record creation
   127	    for (const { relName, relDef, relData } of manyToManyRelationships) {
   128	      if (api.anyapi?.links?.attachMany && relDef?.through) {
   129	        await api.anyapi.links.attachMany({
   130	          context,
   131	          scopeName,
   132	          relName,
   133	          relDef,
   134	          relData,
   135	        })
   136	        continue
   137	      }
   138	
   139	      // Validate pivot resource exists
   140	      validatePivotResource(scopes, relDef, relName)
   141	      await createPivotRecords(api, context.id, relDef, relData, context.transaction)
   142	    }
   143	
   144	    const ret = await handleRecordReturnAfterWrite({
   145	      context,
   146	      scopeName,
   147	      api,
   148	      scopes,
   149	      schemaStructure,
   150	      schemaRelationships,
   151	      scopeOptions,
   152	      vars,
   153	      runHooks,
   154	      helpers,
   155	      log
   156	    })
   157	
   158	    // Commit transaction if we created it
   159	    if (context.shouldCommit) {
   160	      await context.transaction.commit()
   161	      await runHooks('afterCommit')
   162	    }
   163	
   164	    return ret
   165	  } catch (error) {
   166	    await handleWriteMethodError(error, context, 'POST', scopeName, log, runHooks)
   167	  }
   168	}
