     1	const passthrough = (value) => value
     2	const identityTranslate = (_field, value) => value
     3	const identityScope = (query) => query
     4	
     5	const selectColumnsOnBuilder = (builder, columns) => {
     6	  if (!columns) return builder
     7	  if (Array.isArray(columns)) {
     8	    columns.forEach((column) => builder.select(column))
     9	  } else if (typeof columns === 'object') {
    10	    builder.select(columns)
    11	  }
    12	  return builder
    13	}
    14	
    15	const normalizeArray = (value, normalizeFn) => value.map((entry) => normalizeFn(entry))
    16	
    17	const normalizeBelongsToValue = (value) => {
    18	  if (value === null || value === undefined) return value
    19	  if (Array.isArray(value)) {
    20	    return normalizeArray(value, normalizeBelongsToValue)
    21	  }
    22	  return String(value)
    23	}
    24	
    25	const baseAdapter = ({
    26	  knex,
    27	  tableName,
    28	  idColumn,
    29	  translateColumn,
    30	  translateFilterValue = identityTranslate,
    31	  applyResourceScope = identityScope,
    32	  isCanonical = false,
    33	}) => {
    34	  const buildBaseQuery = ({ transaction } = {}) => {
    35	    const query = (transaction || knex)(tableName)
    36	    return applyResourceScope(query)
    37	  }
    38	
    39	  return {
    40	    isCanonical: () => isCanonical,
    41	    getTableName: () => tableName,
    42	    getIdColumn: () => idColumn,
    43	    translateColumn,
    44	    translateFilterValue,
    45	    applyResourceScope,
    46	    buildBaseQuery,
    47	    selectColumns: (builder, columns) => selectColumnsOnBuilder(builder, columns),
    48	  }
    49	}
    50	
    51	const createLegacyAdapter = ({ knex, schemaInfo }) => {
    52	  const tableName = schemaInfo.tableName
    53	  const idColumn = schemaInfo.idProperty || 'id'
    54	
    55	  const translateColumn = (column) => column
    56	
    57	  return baseAdapter({
    58	    knex,
    59	    tableName,
    60	    idColumn,
    61	    translateColumn,
    62	    translateFilterValue: identityTranslate,
    63	    applyResourceScope: identityScope,
    64	    isCanonical: false,
    65	  })
    66	}
    67	
    68	const createCanonicalAdapter = ({ knex, schemaInfo }) => {
    69	  const descriptor = schemaInfo.descriptor || {}
    70	  const canonical = descriptor.canonical || {}
    71	  const canonicalFieldMap = descriptor.canonicalFieldMap || {}
    72	  const fieldsInfo = descriptor.fields || {}
    73	  const belongsToInfo = descriptor.belongsTo || {}
    74	
    75	  const translateColumn = (field) => {
    76	    if (!field) return field
    77	    if (field === 'id') return 'id'
    78	
    79	    const canonicalEntry = canonicalFieldMap[field]
    80	    if (typeof canonicalEntry === 'string') {
    81	      return canonicalEntry
    82	    }
    83	    if (canonicalEntry && typeof canonicalEntry === 'object') {
    84	      if (canonicalEntry.slot) return canonicalEntry.slot
    85	      if (canonicalEntry.slotColumn) return canonicalEntry.slotColumn
    86	      if (canonicalEntry.idSlot) return canonicalEntry.idSlot
    87	      if (canonicalEntry.typeSlot && field.endsWith('_type')) return canonicalEntry.typeSlot
    88	    }
    89	
    90	    if (!canonicalEntry && field.endsWith('_id')) {
    91	      const alias = field.slice(0, -3)
    92	      const aliasEntry = canonicalFieldMap[alias]
    93	      if (typeof aliasEntry === 'string') return aliasEntry
    94	      if (aliasEntry?.idSlot) return aliasEntry.idSlot
    95	    }
    96	
    97	    if (!canonicalEntry && field.endsWith('_type')) {
    98	      const alias = field.slice(0, -5)
    99	      const aliasEntry = canonicalFieldMap[alias]
   100	      if (aliasEntry?.typeSlot) return aliasEntry.typeSlot
   101	    }
   102	
   103	    const fieldInfo = fieldsInfo[field]
   104	    if (fieldInfo?.slot) return fieldInfo.slot
   105	
   106	    const belongsInfo = belongsToInfo[field]
   107	    if (belongsInfo?.idColumn) return belongsInfo.idColumn
   108	
   109	    return field
   110	  }
   111	
   112	  const translateFilterValue = (field, value) => {
   113	    if (value === null || value === undefined) return value
   114	
   115	    const searchField = schemaInfo.searchSchemaStructure?.[field]
   116	    const schemaField = schemaInfo.schemaStructure?.[field]
   117	    const isRelationship = Boolean(
   118	      searchField?.isRelationship ||
   119	      schemaField?.belongsTo ||
   120	      schemaField?.belongsToPolymorphic
   121	    )
   122	
   123	    if (!isRelationship) {
   124	      if (Array.isArray(value)) {
   125	        return normalizeArray(value, passthrough)
   126	      }
   127	      return value
   128	    }
   129	
   130	    return normalizeBelongsToValue(value)
   131	  }
   132	
   133	  const applyResourceScope = (query) => {
   134	    return query
   135	      .where(canonical.tenantColumn, descriptor.tenant)
   136	      .where(canonical.resourceColumn, descriptor.resource)
   137	  }
   138	
   139	  return baseAdapter({
   140	    knex,
   141	    tableName: canonical.tableName,
   142	    idColumn: 'id',
   143	    translateColumn,
   144	    translateFilterValue,
   145	    applyResourceScope,
   146	    isCanonical: true,
   147	  })
   148	}
   149	
   150	export const createStorageAdapter = ({ knex, schemaInfo }) => {
   151	  if (!schemaInfo) {
   152	    throw new Error('createStorageAdapter requires schemaInfo')
   153	  }
   154	
   155	  if (schemaInfo?.descriptor?.canonical?.tableName) {
   156	    return createCanonicalAdapter({ knex, schemaInfo })
   157	  }
   158	
   159	  return createLegacyAdapter({ knex, schemaInfo })
   160	}
