mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
82d7041b6b
* chore: Refactor Markdown importer to use new import pipeline --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
156 lines
4.9 KiB
TypeScript
156 lines
4.9 KiB
TypeScript
import { z } from "zod";
|
|
import type { IntegrationService, ProsemirrorDoc } from "./types";
|
|
import {
|
|
CollectionPermission,
|
|
type ImportableIntegrationService,
|
|
IssueTrackerIntegrationService,
|
|
} from "./types";
|
|
import { PageType } from "plugins/notion/shared/types";
|
|
|
|
const BaseImportInputItemSchema = z.object({
|
|
permission: z.enum(CollectionPermission).optional(),
|
|
});
|
|
|
|
export type BaseImportInput = z.infer<typeof BaseImportInputItemSchema>[];
|
|
|
|
export const NotionImportInputItemSchema = BaseImportInputItemSchema.extend({
|
|
type: z.enum(PageType).optional(),
|
|
externalId: z.string().optional(),
|
|
});
|
|
|
|
export type NotionImportInput = z.infer<typeof NotionImportInputItemSchema>[];
|
|
|
|
export const MarkdownImportInputItemSchema = BaseImportInputItemSchema.extend({
|
|
externalId: z.string(),
|
|
});
|
|
|
|
export type MarkdownImportInput = z.infer<
|
|
typeof MarkdownImportInputItemSchema
|
|
>[];
|
|
|
|
export type ImportInput<T extends ImportableIntegrationService> =
|
|
T extends IntegrationService.Notion
|
|
? NotionImportInput
|
|
: T extends IntegrationService.Markdown
|
|
? MarkdownImportInput
|
|
: BaseImportInput;
|
|
|
|
export const BaseImportTaskInputItemSchema = z.object({
|
|
externalId: z.string(),
|
|
parentExternalId: z.string().optional(),
|
|
collectionExternalId: z.string().optional(),
|
|
});
|
|
|
|
export type BaseImportTaskInput = z.infer<
|
|
typeof BaseImportTaskInputItemSchema
|
|
>[];
|
|
|
|
export const NotionImportTaskInputItemSchema =
|
|
BaseImportTaskInputItemSchema.extend({
|
|
type: z.enum(PageType),
|
|
});
|
|
|
|
export type NotionImportTaskInput = z.infer<
|
|
typeof NotionImportTaskInputItemSchema
|
|
>[];
|
|
|
|
/**
|
|
* 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[];
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* `Processed`.
|
|
*/
|
|
export type ImportScratch<T extends ImportableIntegrationService> =
|
|
T extends IntegrationService.Markdown ? MarkdownImportScratch : 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;
|
|
collectionExternalId?: string;
|
|
title: string;
|
|
path: string;
|
|
markdownText: string;
|
|
attachmentMap: MarkdownAttachmentManifestItem[];
|
|
docMap: Record<string, string>;
|
|
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
|
|
)[];
|
|
|
|
export type ImportTaskInput<T extends ImportableIntegrationService> =
|
|
T extends IntegrationService.Notion
|
|
? NotionImportTaskInput
|
|
: T extends IntegrationService.Markdown
|
|
? MarkdownImportTaskInput
|
|
: BaseImportTaskInput;
|
|
|
|
// No reason to be here except for co-location with import task input.
|
|
export type ImportTaskOutput = {
|
|
externalId: string;
|
|
title: string;
|
|
icon?: string;
|
|
author?: string;
|
|
content: ProsemirrorDoc;
|
|
createdAt?: Date;
|
|
updatedAt?: Date;
|
|
}[];
|
|
|
|
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(IssueTrackerIntegrationService),
|
|
});
|
|
|
|
export type IssueSource = z.infer<typeof IssueSource>;
|