import type { CustomBlockManifest } from "../manifest.js" import type { NotionDataSource, NotionDataSourceBindings, } from "./dataSource.js" import type { NotionPropertySchema } from "./propertySchema.js" type ResolveDataSourcesArgs = { manifest: CustomBlockManifest | null dataSourceBindings: NotionDataSourceBindings } /** * Builds the public {@link NotionDataSource} list the SDK exposes to consumers. * * Combines the host-supplied bindings (collection pointers + schemas) with the * manifest's declared data-source keys. When the manifest names a property * that isn't in `propertyIdsByKey`, falls back to {@link findPropertyIdByManifestProperty} * so renames in Notion still resolve as long as the schema name matches. */ export function resolveDataSources( args: ResolveDataSourcesArgs, ): NotionDataSource[] { if (args.manifest === null) { return Object.entries(args.dataSourceBindings).map(([key, binding]) => ({ key, collectionPointer: binding.collectionPointer, collectionSchema: binding.collectionSchema, propertyIdsByKey: { ...(binding.propertyIdsByKey ?? {}) }, propertySchemasById: binding.collectionSchema?.propertiesById ?? {}, })) } return Object.entries(args.manifest.dataSources).map( ([key, manifestDataSource]) => { const binding = args.dataSourceBindings[key] const propertySchemasById = binding?.collectionSchema?.propertiesById ?? {} const bindingPropertyIdsByKey = binding?.propertyIdsByKey ?? {} const propertyIdsByKey: Record = {} const manifestProperties = Object.entries( manifestDataSource.properties ?? {}, ) for (const [propertyKey, manifestProperty] of manifestProperties) { const propertyId = propertyKey in bindingPropertyIdsByKey ? bindingPropertyIdsByKey[propertyKey] : findPropertyIdByManifestProperty(propertySchemasById, { key: propertyKey, type: manifestProperty.type, }) const propertySchema = propertyId === undefined ? undefined : propertySchemasById[propertyId] propertyIdsByKey[propertyKey] = propertySchema?.type === manifestProperty.type ? propertyId : undefined } return { key, collectionPointer: binding?.collectionPointer, collectionSchema: binding?.collectionSchema, propertyIdsByKey, propertySchemasById, } }, ) } export function findPropertyIdByManifestProperty( propertySchemasById: Record, manifestProperty: { key: string; type: NotionPropertySchema["type"] }, ): string | undefined { // `ManifestProperty.name` is display copy for setup UI. Binding fallback is based // on the stable semantic key so copy changes don't affect property resolution. const propertySchemaForKey = propertySchemasById[manifestProperty.key] if (propertySchemaForKey?.type === manifestProperty.type) { return manifestProperty.key } const normalizedKey = normalizePropertyName(manifestProperty.key) for (const [propertyId, propertySchema] of Object.entries( propertySchemasById, )) { if ( propertySchema.type === manifestProperty.type && normalizePropertyName(propertySchema.name) === normalizedKey ) { return propertyId } } return undefined } function normalizePropertyName(value: string): string { return value.trim().toLowerCase() }