Ctrl K
ComunidadGuías…

shared/schema.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { z } from "zod";
import type { ProsemirrorData, ProsemirrorDoc } from "./types";
import {
  CollectionPermission,
  type ImportableIntegrationService,
  IntegrationService,
} from "./types";

export const BaseImportInputItemSchema = z.object({
  permission: z.enum(CollectionPermission).optional(),
  /**
   * Marks a synthetic root node (e.g. Markdown's fallback "Documents" node)
   * so it can collapse when importing into an existing destination — its loose
   * child files then parent directly to the destination.
   */
  synthetic: z.boolean().optional(),
});

export type BaseImportInput = z.infer<typeof BaseImportInputItemSchema>[];

export const MarkdownImportInputItemSchema = BaseImportInputItemSchema.extend({
  externalId: z.string(),
});

export type MarkdownImportInput = z.infer<
  typeof MarkdownImportInputItemSchema
>[];

export const JSONImportInputItemSchema = BaseImportInputItemSchema.extend({
  externalId: z.string(),
});

export type JSONImportInput = z.infer<typeof JSONImportInputItemSchema>[];

/** The import input by service, plugins augment it with their importers. */
export interface ImportInputMap {
  [IntegrationService.Markdown]: MarkdownImportInput;
  [IntegrationService.OKF]: MarkdownImportInput;
  [IntegrationService.JSON]: JSONImportInput;
}

export type ImportInput<T extends ImportableIntegrationService> =
  T extends keyof ImportInputMap ? ImportInputMap[T] : BaseImportInput;

export const BaseImportTaskInputItemSchema = z.object({
  externalId: z.string(),
  parentExternalId: z.string().optional(),
  /** External id of the root node this item descends from. */
  rootExternalId: z.string().optional(),
  /** @deprecated Only present on tasks queued before rootExternalId existed. */
  collectionExternalId: z.string().optional(),
});

export type BaseImportTaskInput = z.infer<
  typeof BaseImportTaskInputItemSchema
>[];

/**
 * Manifest entry describing a single attachment discovered during the
 * Markdown zip bootstrap phase. The `id` is a pre-assigned UUID used both
 * as the attachment node id in per-page prosemirror output and as the
 * Attachment row id created during completion.
 */
export const MarkdownAttachmentManifestItemSchema = z.object({
  id: z.uuid(),
  name: z.string(),
  mimeType: z.string(),
  pathInZip: z.string(),
});

export type MarkdownAttachmentManifestItem = z.infer<
  typeof MarkdownAttachmentManifestItemSchema
>;

/**
 * Markdown importer scratch state. `storageKey` is set at import creation
 * (it's the only durable handle on the uploaded zip). `manifest` is added
 * by the bootstrap phase so the completion phase can re-download the zip
 * and create Attachment rows without re-walking the tree.
 */
export interface MarkdownImportScratch {
  storageKey: string;
  manifest?: MarkdownAttachmentManifestItem[];
  docMap?: Record<string, string>;
}

/**
 * Manifest entry describing a single attachment discovered during the JSON
 * zip bootstrap phase. `externalId` is the attachment's original id from the
 * export — used to rewrite `/api/attachments.redirect?id=<externalId>`
 * references in document/collection content into new redirect URLs that point
 * at the freshly created Attachment row (`id`).
 */
export const JSONAttachmentManifestItemSchema = z.object({
  id: z.uuid(),
  externalId: z.string(),
  name: z.string(),
  mimeType: z.string(),
  pathInZip: z.string(),
});

export type JSONAttachmentManifestItem = z.infer<
  typeof JSONAttachmentManifestItemSchema
>;

/**
 * JSON importer scratch state. `storageKey` is set at import creation (it's
 * the only durable handle on the uploaded zip). `manifest` is added by the
 * bootstrap phase so the completion phase can re-download the zip and create
 * Attachment rows without re-parsing the JSON files.
 */
export interface JSONImportScratch {
  storageKey: string;
  manifest?: JSONAttachmentManifestItem[];
}

/**
 * Per-importer scratch shape stored on `Import.scratch`. Holds cross-phase
 * state that the importer needs between bootstrap and completion but that
 * isn't part of any single task's input. Cleared when the import flips to
 * `Completed`.
 */
export interface ImportScratchMap {
  [IntegrationService.Markdown]: MarkdownImportScratch;
  [IntegrationService.OKF]: MarkdownImportScratch;
  [IntegrationService.JSON]: JSONImportScratch;
}

export type ImportScratch<T extends ImportableIntegrationService> =
  T extends keyof ImportScratchMap ? ImportScratchMap[T] : never;

/**
 * Per-page task input. Generated by the bootstrap task and consumed by
 * subsequent MarkdownAPIImportTask runs. `children` carries this document's
 * direct descendants so that each level of the document tree is scheduled
 * as a separate task wave; this preserves parent-before-child ordering
 * during persistence (createdAt of child tasks is strictly later than
 * parents'). The type is defined as a TypeScript interface rather than via
 * z.infer because it is only consumed internally — never validated at an
 * API boundary — and zod's recursive-schema ergonomics aren't worth the
 * cost here.
 */
export interface MarkdownPageImportTaskInputItem {
  externalId: string;
  parentExternalId?: string;
  rootExternalId?: string;
  /** @deprecated Only present on tasks queued before rootExternalId existed. */
  collectionExternalId?: string;
  title: string;
  path: string;
  markdownText: string;
  attachmentMap: MarkdownAttachmentManifestItem[];
  children?: MarkdownPageImportTaskInputItem[];
}

/**
 * Markdown import task input — a bootstrap row carrying only the base
 * placeholder item (the zip's `storageKey` lives on `Import.scratch`), or a
 * page row carrying per-document content.
 */
export type MarkdownImportTaskInput = (
  | BaseImportTaskInput[number]
  | MarkdownPageImportTaskInputItem
)[];

/**
 * Per-page task input for the JSON importer. Generated by the bootstrap task
 * once the zip has been parsed; consumed by subsequent JSONAPIImportTask runs.
 * `children` carries this document's direct descendants so each tree-depth
 * runs as its own task wave, preserving parent-before-child ordering during
 * persistence (createdAt of child tasks is strictly later than parents'). The
 * type is defined as a TypeScript interface rather than via z.infer because
 * it is only consumed internally — never validated at an API boundary — and
 * zod's recursive-schema ergonomics aren't worth the cost here.
 */
export interface JSONPageImportTaskInputItem {
  externalId: string;
  parentExternalId?: string;
  rootExternalId?: string;
  /** @deprecated Only present on tasks queued before rootExternalId existed. */
  collectionExternalId?: string;
  title: string;
  urlId?: string;
  icon?: string | null;
  color?: string | null;
  data: ProsemirrorData;
  createdById?: string;
  createdByName?: string;
  createdByEmail?: string | null;
  createdAt?: string;
  updatedAt?: string;
  publishedAt?: string | null;
  /** Map of external attachment id → manifest entry id, scoped to this doc. */
  attachmentIdMap: Record<string, string>;
  children?: JSONPageImportTaskInputItem[];
}

/**
 * JSON import task input — a bootstrap row carrying only the base placeholder
 * item (the zip's `storageKey` lives on `Import.scratch`), or a page row
 * carrying per-document content.
 */
export type JSONImportTaskInput = (
  | BaseImportTaskInput[number]
  | JSONPageImportTaskInputItem
)[];

/** The import task input by service, plugins augment it with their importers. */
export interface ImportTaskInputMap {
  [IntegrationService.Markdown]: MarkdownImportTaskInput;
  [IntegrationService.OKF]: MarkdownImportTaskInput;
  [IntegrationService.JSON]: JSONImportTaskInput;
}

export type ImportTaskInput<T extends ImportableIntegrationService> =
  T extends keyof ImportTaskInputMap
    ? ImportTaskInputMap[T]
    : BaseImportTaskInput;

// No reason to be here except for co-location with import task input.
export type ImportTaskOutput = {
  externalId: string;
  title: string;
  icon?: string | null;
  color?: string | null;
  urlId?: string;
  author?: string;
  /** Original author's id in the source system, used for user remapping. */
  createdById?: string;
  /** Original author's email in the source system, used for user remapping. */
  createdByEmail?: string | null;
  content: ProsemirrorDoc;
  createdAt?: Date;
  updatedAt?: Date;
  publishedAt?: Date | null;
}[];

export const IssueSource = z.object({
  id: z.string().nonempty(),
  name: z.string().nonempty(),
  owner: z.object({
    id: z.string().nonempty(),
    name: z.string().nonempty(),
  }),
  service: z.enum(IntegrationService),
});

export type IssueSource = z.infer<typeof IssueSource>;