import type { ResourceAccessLevel } from "../workflow-access.js"; import type { CustomAgentArgs } from "./custom-agent.js"; import type { PageArgs } from "./page.js"; import type { TeamspaceArgs } from "./teamspace.js"; import type { ViewSchema } from "./views.js"; /** Apps-specific resource IDs used while declaring Notion-as-Code metadata. */ /** Synthetic workspace parent for teamspaces declared by an Apps project. */ export const APPS_WORKSPACE_RESOURCE_ID = "__notion_apps_workspace__"; /** Reserved prefix for resource IDs owned by the Apps runtime. */ export const APPS_RESERVED_RESOURCE_ID_PREFIX = "__notion_apps_"; export type ResourceId = string; export type Parent = { type: "resourceId"; resourceId: ResourceId; }; export type NotionAsCodeIcon = | { type: "emoji"; emoji: string } | { type: "notion_icon"; description: string; color?: string } | { type: "file"; resourceId: ResourceId }; /** A URL reference used as a page or database cover. */ export type PageCoverReference = { type: "url"; url: string; /** Vertical position from the top, where 0 is the top and 1 is the bottom. */ position?: number; }; export type NotionAsCodeSelectColor = | "default" | "gray" | "brown" | "orange" | "yellow" | "green" | "blue" | "purple" | "pink" | "red"; export type NotionAsCodeSelectOption = { name: string; color?: NotionAsCodeSelectColor; }; export type NotionAsCodeStatusOption = NotionAsCodeSelectOption & { default?: boolean; }; type PropertyBase = { resourceId: ResourceId; name: string; icon?: Extract; description?: string; }; /** Rollup aggregations supported for every target property type. */ export type RollupBaseAggregationType = | "count" | "count_values" | "unique" | "empty" | "not_empty" | "percent_empty" | "percent_not_empty" | "show_unique"; /** Numeric-only rollup aggregations. */ export type RollupNumberAggregationType = | RollupBaseAggregationType | "sum" | "average" | "median" | "min" | "max" | "range"; /** Date-only rollup aggregations. */ export type RollupDateAggregationType = | RollupBaseAggregationType | "earliest_date" | "latest_date" | "date_range"; export type RollupTargetPropertyType = | "title" | "text" | "number" | "select" | "multi_select" | "status" | "date" | "checkbox" | "url" | "email" | "phone_number" | "relation" | "person" | "created_time" | "last_edited_time" | "created_by" | "last_edited_by"; type RollupTarget = | { targetPropertyType: "number"; aggregation?: RollupNumberAggregationType; } | { targetPropertyType: "date" | "created_time" | "last_edited_time"; aggregation?: RollupDateAggregationType; } | { targetPropertyType: Exclude< RollupTargetPropertyType, "number" | "date" | "created_time" | "last_edited_time" >; aggregation?: RollupBaseAggregationType; }; type RelationProperty = PropertyBase & { type: "relation"; targetDataSourceResourceId: ResourceId; targetDataSourcePropertyResourceId?: ResourceId; limit?: 1; }; type FormulaProperty = PropertyBase & { type: "formula"; expression?: string; }; type RollupProperty = PropertyBase & RollupTarget & { type: "rollup"; relationPropertyResourceId: ResourceId; targetPropertyResourceId: ResourceId; }; type AutoIncrementIdProperty = PropertyBase & { type: "auto_increment_id"; prefix?: string; }; type PersonProperty = PropertyBase & { type: "person"; limit?: 1; }; type VerificationProperty = PropertyBase & { type: "verification"; verifierPropertyResourceId: ResourceId; }; /** A normalized Notion-as-Code property with its resolved display name. */ export type NotionAsCodeProperty = | (PropertyBase & { type: "title" }) | (PropertyBase & { type: "text" }) | (PropertyBase & { type: "number" }) | (PropertyBase & { type: "select"; options?: readonly NotionAsCodeSelectOption[] }) | (PropertyBase & { type: "multi_select"; options?: readonly NotionAsCodeSelectOption[] }) | { resourceId: ResourceId; name: string; type: "status"; icon?: Extract; description?: string; options: { todo: readonly NotionAsCodeStatusOption[]; inProgress: readonly NotionAsCodeStatusOption[]; complete: readonly NotionAsCodeStatusOption[]; }; } | (PropertyBase & { type: "date" }) | (PropertyBase & { type: "checkbox" }) | (PropertyBase & { type: "url" }) | (PropertyBase & { type: "email" }) | (PropertyBase & { type: "phone_number" }) | (PropertyBase & { type: "file" }) | RelationProperty | FormulaProperty | RollupProperty | (PropertyBase & { type: "created_time" }) | (PropertyBase & { type: "last_edited_time" }) | (PropertyBase & { type: "created_by" }) | (PropertyBase & { type: "last_edited_by" }) | AutoIncrementIdProperty | PersonProperty | VerificationProperty; /** * `T extends unknown` applies Omit to each property variant separately, preserving * variant-specific fields such as required status options. Plain Omit on the union * would retain only shared keys. */ type DistributivePropertyConfig = T extends unknown ? Omit : never; /** * Authoring properties are keyed by their schema object name. A property-level * `name` override would make the key and serialized property diverge. */ export type NotionAsCodePropertyConfig = DistributivePropertyConfig & { name?: never; }; /** A keyed property schema, with object keys supplying serialized property names. */ export type NotionAsCodeSchema = Record; type SchemaProperty = Schema[keyof Schema & string]; type PropertyType = NotionAsCodePropertyConfig["type"]; type PropertyResourceIdFor< Schema extends NotionAsCodeSchema, Types extends PropertyType = PropertyType, > = Extract, { type: Types }>["resourceId"]; export type PageLayoutPropertyConfig = { property: PropertyResourceIdFor; visibility?: "show" | "hide_if_empty" | "hide"; }; export type PageLayoutPropertySection = { name: string; properties: readonly PageLayoutPropertyConfig[]; }; export type PageLayoutPropertiesModule = { type: "properties"; } & ( | { properties?: readonly PageLayoutPropertyConfig[]; sections?: never; } | { sections: readonly PageLayoutPropertySection[]; properties?: never; } ); export type PageLayoutPersonStyle = "compact" | "large"; export type PageLayoutNumberAndFormulaStyle = "large" | "small"; type PageLayoutStyleFor = Property["type"] extends | "person" | "created_by" | "last_edited_by" ? { style?: PageLayoutPersonStyle } : Property["type"] extends "number" | "formula" ? { style?: PageLayoutNumberAndFormulaStyle } : { style?: never }; type PageLayoutPropertyModuleFor = Property extends unknown ? { type: "property"; property: Property["resourceId"]; } & PageLayoutStyleFor : never; /** * A standalone property module. Its available style is inferred from the * referenced property's type when the layout is declared with a literal schema. */ export type PageLayoutPropertyModule = PageLayoutPropertyModuleFor>; export type PageLayoutRelationConfig = { property: PropertyResourceIdFor; display: "pageSection" | "minimal"; }; export type PageLayoutRelationsGroupModule = { type: "relations"; relations: readonly PageLayoutRelationConfig[]; }; export type PageLayoutMainModule = | PageLayoutPropertiesModule | PageLayoutPropertyModule | PageLayoutRelationsGroupModule; export type PageLayoutSidebarModule = | PageLayoutPropertiesModule | PageLayoutPropertyModule; export type ContentPageLayoutTab = { type: "content"; name?: string; }; export type ViewPageLayoutTab = { type: "view"; resourceId: ResourceId; }; export type DatabasePageLayoutTab = ContentPageLayoutTab | ViewPageLayoutTab; export type DatabasePageLayout = { main: readonly PageLayoutMainModule[]; sidebar?: readonly PageLayoutSidebarModule[]; pinnedProperties?: readonly PropertyResourceIdFor[]; discussions?: "top" | "bottom" | "hidden"; tabs?: readonly DatabasePageLayoutTab[]; fullWidth?: boolean; showPropertyIcons?: boolean; showTemplates?: boolean; }; /** The normalized data-source shape emitted in provisioning artifacts. */ export type DataSourceDefinition< Properties extends readonly NotionAsCodeProperty[] = readonly NotionAsCodeProperty[], > = { resourceId: ResourceId; name: string; description?: string; properties: Properties; icon?: NotionAsCodeIcon; pageLayout?: DatabasePageLayout; }; export type TeamspaceIntent = TeamspaceArgs & { parent: Parent; }; /** * The normalized database shape emitted in provisioning artifacts. * * This intentionally does not depend on the authoring argument shape. Authoring * accepts keyed schemas and data-source maps; the recorder always stores arrays. */ export type DatabaseIntent = { resourceId: ResourceId; parent: Parent; dataSources: readonly DataSourceDefinition[]; views?: readonly ViewSchema[]; icon?: NotionAsCodeIcon; cover?: PageCoverReference; hideDataSourceTitle?: boolean; hideDatabaseTitleIfEmpty?: boolean; name?: string; description?: string; }; export type PageIntent = PageArgs & { parent: Parent }; export type CustomAgentIntent = Omit & { trustedUrls?: { type: "all" } | { type: "list"; urls: string[] }; access?: readonly { resourceId: ResourceId; level: ResourceAccessLevel }[]; }; export type ViewIntent = { databaseResourceId: ResourceId; view: ViewSchema; }; export type InfraAsCodeIntent = | ({ type: "teamspace" } & TeamspaceIntent) | ({ type: "database" } & DatabaseIntent) | ({ type: "page" } & PageIntent) | ({ type: "custom_agent" } & CustomAgentIntent) | ({ type: "view" } & ViewIntent);