/** * Module Links — cross-module associations that live in "neutral territory" * outside any specific module's schema. * * Each link defines a pivot table that contains only ID pairs. There are no * DB-level foreign key constraints, so modules remain independently * swappable, and templates can introduce new cross-module references * without modifying any module package. */ /** * Metadata describing a linkable entity — the entity kind that can participate * in a link from another module. * * Each module exposes one {@link LinkableDefinition} per entity it wants to * expose to other modules (e.g. a CRM module exposes `person` and * `organization`). */ export interface LinkableDefinition { /** Owning module name (e.g. "crm"). */ module: string; /** Entity name within the module (e.g. "person"). Used to build the join-column name. */ entity: string; /** Underlying DB table name (informational; used by codegen / diagnostics). */ table: string; /** Primary-key column name on the entity's table. Defaults to "id". */ primaryKey?: string; /** TypeID prefix for IDs of this entity (informational). */ idPrefix?: string; } /** * One side of a link. `isList: true` means that, from the opposite side, * this entity appears as a list. */ export interface LinkSide { linkable: LinkableDefinition; isList?: boolean; } export type LinkSideInput = LinkableDefinition | LinkSide; export interface LinkDefinitionOptions { /** * When true, deleting the link row should cascade-delete the target side * (enforced by the link service, not the database). */ deleteCascade?: boolean; database?: { /** Override the auto-generated pivot table name. */ tableName?: string; /** Override the auto-generated left-side ID column name. */ leftColumn?: string; /** Override the auto-generated right-side ID column name. */ rightColumn?: string; }; /** * Mark the link as externally-owned and non-materialized. * * Read-only links participate in cross-module query traversal, but they do * not create a neutral-territory pivot table and cannot be mutated through * the runtime link service. */ readOnly?: { list(filter?: { leftId?: string; rightId?: string; }): Promise; }; } export type LinkCardinality = "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many"; /** * A fully-resolved link definition. Produced by {@link defineLink} and * consumed by codegen (`generateLinkTableSql`) and the runtime link service. */ export interface LinkDefinition { left: LinkSide; right: LinkSide; /** Generated pivot table name (e.g. "crm_person_products_product"). */ tableName: string; /** Left-side ID column name in the pivot table (e.g. "crm_person_id"). */ leftColumn: string; /** Right-side ID column name in the pivot table (e.g. "products_product_id"). */ rightColumn: string; /** Inferred cardinality from the `isList` flags on each side. */ cardinality: LinkCardinality; deleteCascade: boolean; /** * Read-only links are resolved from an externally-owned relation instead of * a generated pivot table. */ readOnly?: { list(filter?: { leftId?: string; rightId?: string; }): Promise; }; } /** * Declare a link between two linkable entities. * * @example * ```ts * // one-to-one * defineLink(crm.linkable.person, products.linkable.product) * * // one-to-many (each person has many products) * defineLink( * crm.linkable.person, * { linkable: products.linkable.product, isList: true }, * ) * * // many-to-many * defineLink( * { linkable: crm.linkable.person, isList: true }, * { linkable: products.linkable.product, isList: true }, * ) * ``` */ export declare function defineLink(left: LinkSideInput, right: LinkSideInput, options?: LinkDefinitionOptions): LinkDefinition; /** * SQL DDL for materializing a link's pivot table. */ export interface LinkTableSql { /** `CREATE TABLE IF NOT EXISTS …` statement. */ createTable: string; /** `CREATE [UNIQUE] INDEX IF NOT EXISTS …` statements. */ indexes: string[]; } /** * Generate the SQL needed to materialize a link's pivot table. * * Columns: `id` (primary key), `_id`, `_id`, `created_at`, * `updated_at`, `deleted_at`. Unique indexes are created based on the * link's cardinality so the database enforces the relationship shape. */ export declare function generateLinkTableSql(def: LinkDefinition): LinkTableSql; /** * Medusa-style link spec keyed by module name, then by join-column name. * * @example * ```ts * { * crm: { person_id: "pers_abc" }, * products: { product_id: "prod_xyz" }, * } * ``` */ export type LinkKeyRecord = Record; export type LinkSpec = Record; export interface ResolvedLinkSpec { definition: LinkDefinition; leftId: string; rightId: string; } /** * Match a {@link LinkSpec} to a {@link LinkDefinition} from the provided list, * returning the matched definition along with the resolved left/right IDs * (in the order the definition declares). * * Throws when the spec doesn't match exactly one definition. */ export declare function resolveLinkFromSpec(spec: LinkSpec, defs: LinkDefinition[]): ResolvedLinkSpec; /** * A row in a link's pivot table. */ export interface LinkRow { id: string; leftId: string; rightId: string; createdAt: Date; updatedAt: Date; deletedAt: Date | null; } /** * Filter for {@link LinkService.list}. All provided fields are ANDed. * * The plural fields (`leftIds`/`rightIds`) match any of the given IDs in a * single batched lookup — implementations MUST resolve them with one * query/roundtrip, not one per ID. An empty array can never match and * short-circuits to `[]`. Singular and plural fields for the same side * combine by intersection. */ export interface LinkListFilter { /** Match a single left-side ID. */ leftId?: string; /** Match a single right-side ID. */ rightId?: string; /** Match any of the given left-side IDs (one batched query). */ leftIds?: string[]; /** Match any of the given right-side IDs (one batched query). */ rightIds?: string[]; } /** * Runtime service for manipulating link rows. Templates register an * implementation in the module container under `"link"`. */ export interface LinkService { /** Create a link between two entity IDs. Idempotent on the unique pair. */ create(linkKey: string, leftId: string, rightId: string): Promise; /** Create using a Medusa-style module-keyed spec. */ create(spec: LinkSpec): Promise; /** Soft-delete a link (sets `deleted_at`). */ dismiss(linkKey: string, leftId: string, rightId: string): Promise; dismiss(spec: LinkSpec): Promise; /** Hard-delete a link row. */ delete(linkKey: string, leftId: string, rightId: string): Promise; delete(spec: LinkSpec): Promise; /** List link rows matching the given filter (non-soft-deleted only). */ list(linkKey: string, filter?: LinkListFilter): Promise; } //# sourceMappingURL=links.d.ts.map