@taskless-app/shared 0.1.34 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/importPackage.d.ts +1059 -0
- package/dist/importPackage.js +657 -0
- package/dist/importPackage.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1059 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Версия формата, которую пишет сегодняшний коннектор и понимает сегодняшнее
|
|
4
|
+
* ядро. Растёт на ЛОМАЮЩЕМ изменении формы; добавление необязательного поля
|
|
5
|
+
* версию не двигает — старое ядро прочитает такой пакет, не заметив нового поля.
|
|
6
|
+
*
|
|
7
|
+
* В примере спеки версии нет, но задача требует её прямо: «схема обязана быть
|
|
8
|
+
* версионированной, иначе завтрашний коннектор сломает вчерашний пакет».
|
|
9
|
+
*/
|
|
10
|
+
export declare const IMPORT_PACKAGE_FORMAT_VERSION = 1;
|
|
11
|
+
/**
|
|
12
|
+
* Версии, которые ядро умеет читать. Отдельно от текущей, потому что читать мы
|
|
13
|
+
* обязаны шире, чем писать: пакет мог быть собран неделю назад и пролежать в
|
|
14
|
+
* очереди, пока ядро обновилось.
|
|
15
|
+
*/
|
|
16
|
+
export declare const SUPPORTED_IMPORT_PACKAGE_VERSIONS: readonly [1];
|
|
17
|
+
export declare const IMPORT_EXTERNAL_ID_MAX = 400;
|
|
18
|
+
export declare const IMPORT_TITLE_MAX = 1000;
|
|
19
|
+
export declare const IMPORT_HTML_MAX = 5000000;
|
|
20
|
+
export declare const IMPORT_FILE_PATH_MAX = 600;
|
|
21
|
+
export declare const IMPORT_LABEL_MAX = 200;
|
|
22
|
+
export declare const IMPORT_LABELS_MAX = 100;
|
|
23
|
+
export declare const IMPORT_URL_MAX = 2000;
|
|
24
|
+
export declare const IMPORT_SOURCE_MAX = 100;
|
|
25
|
+
export declare const IMPORT_ATTACHMENT_REFS_MAX = 500;
|
|
26
|
+
/**
|
|
27
|
+
* Машинное имя исходного сервиса. Без `:` и пробелов сознательно: на этом стоит
|
|
28
|
+
* однозначность ключа идемпотентности (`importEntityKey`).
|
|
29
|
+
*/
|
|
30
|
+
export declare const IMPORT_SOURCE_RE: RegExp;
|
|
31
|
+
/**
|
|
32
|
+
* НЕВИДИМЫЕ СИМВОЛЫ — всё, чего человек в строке не увидит, а машина учтёт.
|
|
33
|
+
*
|
|
34
|
+
* Используется у `ImportExternalId` и `ImportAttachmentName` — оба алфавит не
|
|
35
|
+
* сужают (в отличие от `ImportFilePath`, который 2026-08-26 перешёл на белый
|
|
36
|
+
* список `IMPORT_FILE_PATH_RE` и вообще не пропускает ничего вне
|
|
37
|
+
* `[A-Za-z0-9._-]`, так что для него эта проверка стала бы мёртвым кодом —
|
|
38
|
+
* невидимые символы и суррогаты закрыты для пути уже тем, что их нет в
|
|
39
|
+
* алфавите). `externalId` и `name` сознательно не сужают алфавит (см. их
|
|
40
|
+
* комментарии): оба — либо чужой id, либо человекочитаемое имя файла, где
|
|
41
|
+
* скобки/пробелы/кириллица законны, поэтому для них нужен именно перечень
|
|
42
|
+
* запрещённых классов, а не белый список.
|
|
43
|
+
*
|
|
44
|
+
* Классами Unicode, а не перечнем кодов, потому что перечень тут всегда неполон:
|
|
45
|
+
* • `Cc` — управляющие C0 (включая NUL), DEL и C1. NUL Postgres не хранит
|
|
46
|
+
* в `text`;
|
|
47
|
+
* • `Cf` — форматирующие: zero-width space, BOM, мягкий перенос,
|
|
48
|
+
* bidi-переопределения. Ключ идемпотентности показывается человеку, а
|
|
49
|
+
* такой символ делает две разные строки неразличимыми на глаз;
|
|
50
|
+
* • `Zl`/`Zp` — разделители строки и абзаца: разрыв строки ломает однострочную
|
|
51
|
+
* запись в лог прогона и в отчёт о карантине;
|
|
52
|
+
* • `Default_Ignorable_Code_Point` — свойство, а не категория, и без него в
|
|
53
|
+
* проверке оставалась дыра: селекторы вариаций (U+FE0F), монгольские FVS
|
|
54
|
+
* (U+180B–U+180D) и combining grapheme joiner (U+034F) лежат в категории
|
|
55
|
+
* `Mn`, то есть ни под один класс выше не подходят, — а невидимы ровно так
|
|
56
|
+
* же. `p42` и `p42\u{FE0F}` давали ДВА РАЗНЫХ ключа идемпотентности,
|
|
57
|
+
* неразличимых на экране карантина. Нашли верификаторы TERMES и SCARAB на
|
|
58
|
+
* ревью MOVE-7;
|
|
59
|
+
* • `U+2800` BRAILLE PATTERN BLANK — единственный код здесь перечнем, а не
|
|
60
|
+
* классом, и это не лень. Свойства «рисуется пустым местом» в Unicode нет:
|
|
61
|
+
* у него категория `So`, `Default_Ignorable` он не имеет, — а выглядит как
|
|
62
|
+
* пустая ячейка в любом шрифте с Брайлем (тем же приёмом делают невидимые
|
|
63
|
+
* ники в мессенджерах). Остальные известные пустые глифы — филлеры хангыля
|
|
64
|
+
* (U+115F, U+1160, U+3164, U+FFA0), кхмерские U+17B4/U+17B5, монгольский
|
|
65
|
+
* U+180E — все `Default_Ignorable` и ловятся строкой выше; проверено
|
|
66
|
+
* перебором, U+2800 из них единственный выпадающий. Нашёл верификатор
|
|
67
|
+
* MYRMEX третьим кругом на том же ревью.
|
|
68
|
+
*
|
|
69
|
+
* Пробелы категории `Zs` (в т.ч. неразрывный) сюда НЕ входят намеренно: они видны
|
|
70
|
+
* как пробел, а по краям строки их и так снимает отдельная проверка.
|
|
71
|
+
*
|
|
72
|
+
* Побочно под запрет попадают ZWJ и теговые символы, то есть составные эмодзи в
|
|
73
|
+
* качестве `externalId`. Машинного id такой формы у сервисов не бывает;
|
|
74
|
+
* появится — вырежется один класс, а не вся проверка.
|
|
75
|
+
*/
|
|
76
|
+
export declare const IMPORT_INVISIBLE_CHARS_RE: RegExp;
|
|
77
|
+
/**
|
|
78
|
+
* ОДИНОЧНЫЙ (НЕПАРНЫЙ) СУРРОГАТ — не «невидимый символ», а вообще не символ, и
|
|
79
|
+
* поэтому его не ловит НИ ОДНО свойство Unicode: для `\p{...}`-классов такого
|
|
80
|
+
* кода просто не существует. Проверка отдельная именно по этой причине, а не
|
|
81
|
+
* ради красоты.
|
|
82
|
+
*
|
|
83
|
+
* Чем это хуже всего, что чинили выше. Невидимые символы дают два ключа,
|
|
84
|
+
* НЕРАЗЛИЧИМЫХ НА ГЛАЗ. Одиночные суррогаты дают два ключа, РАЗНЫХ по схеме и
|
|
85
|
+
* СОВПАДАЮЩИХ БАЙТ В БАЙТ после кодирования в UTF-8: все 2048 значений
|
|
86
|
+
* `U+D800`…`U+DFFF` схлопываются в один и тот же `U+FFFD`. То есть на границе
|
|
87
|
+
* с Postgres (колонка `text`, куда ключ и едет) две разные сущности становятся
|
|
88
|
+
* одной — прямая коллизия идемпотентности, а не косметика.
|
|
89
|
+
*
|
|
90
|
+
* И это не только про злой умысел: `JSON.parse` такую строку принимает молча,
|
|
91
|
+
* а получается она обычным наивным `slice(0, N)` где-нибудь выше по цепочке —
|
|
92
|
+
* если срез пришёлся на середину суррогатной пары (эмодзи, редкий CJK,
|
|
93
|
+
* математические знаки). Классические грабли обрезки, а не атака.
|
|
94
|
+
*
|
|
95
|
+
* Регулярка без флага `u` намеренно: она работает с UTF-16 code units, а
|
|
96
|
+
* искать надо ровно непарную половину пары. Нашёл верификатор HORNET
|
|
97
|
+
* четвёртым кругом на ревью MOVE-7.
|
|
98
|
+
*
|
|
99
|
+
* Используется у `ImportExternalId` и `ImportAttachmentName` — как и
|
|
100
|
+
* `IMPORT_INVISIBLE_CHARS_RE`, у `ImportFilePath` эта проверка стала
|
|
101
|
+
* избыточной: белый список `IMPORT_FILE_PATH_RE` не пропускает суррогат по
|
|
102
|
+
* построению.
|
|
103
|
+
*/
|
|
104
|
+
export declare const IMPORT_LONE_SURROGATE_RE: RegExp;
|
|
105
|
+
/**
|
|
106
|
+
* Идентификатор сущности В ИСХОДНОМ СЕРВИСЕ — основа идемпотентности: по нему
|
|
107
|
+
* повторный прогон находит уже перенесённое и обновляет, а не плодит копии.
|
|
108
|
+
* Обязан быть СТАБИЛЬНЫМ между выгрузками (id страницы — да, её заголовок — нет).
|
|
109
|
+
*
|
|
110
|
+
* Строка, а не число: у Confluence это число, у Notion — uuid, у следующего
|
|
111
|
+
* сервиса что-то третье. Алфавит поэтому НЕ сужаем до латиницы с цифрами: чужой
|
|
112
|
+
* сервис вправе выдавать что угодно, а коннектор, вынужденный перекодировать id,
|
|
113
|
+
* потерял бы ровно ту стабильность, на которой всё стоит.
|
|
114
|
+
*
|
|
115
|
+
* Но два класса символов запрещены, и оба — про то, что с этой строкой будет
|
|
116
|
+
* ДАЛЬШЕ:
|
|
117
|
+
*
|
|
118
|
+
* • НЕВИДИМЫЕ (`IMPORT_INVISIBLE_CHARS_RE`). `externalId` уезжает в ключ
|
|
119
|
+
* идемпотентности (`importEntityKey`), а тот — в колонку БД, в лог прогона и
|
|
120
|
+
* в текст ошибок. Postgres не хранит NUL в `text`, так что пакет с таким id
|
|
121
|
+
* падал бы уже на записи — в ядре импорта, далеко от места, где мусор
|
|
122
|
+
* приехал. Остальные невидимые не роняют запись, но делают два разных ключа
|
|
123
|
+
* неразличимыми для человека, который смотрит на карантин и решает, одна это
|
|
124
|
+
* сущность или две. Печатность ключа — заявленный контракт, и держать его
|
|
125
|
+
* обязана схема: сам `importEntityKey` чистая склейка и вход проверять не
|
|
126
|
+
* может.
|
|
127
|
+
*
|
|
128
|
+
* • КРАЙНИЕ ПРОБЕЛЫ. `"p42"` и `"p42 "` — разные ключи и потому разные
|
|
129
|
+
* сущности, хотя различие невидимо глазом; такой пакет молча импортируется
|
|
130
|
+
* дважды. Пробелы ВНУТРИ оставлены: они видны, ключ не ломают (свободная
|
|
131
|
+
* часть в нём последняя) и у сервисов, где id это путь или заголовок, законны.
|
|
132
|
+
*/
|
|
133
|
+
export declare const ImportExternalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
134
|
+
/**
|
|
135
|
+
* Момент времени из исходного сервиса — ISO 8601 СО СМЕЩЕНИЕМ. Смещение
|
|
136
|
+
* обязательно: у экспортов сплошь и рядом локальное время сервера, и без него
|
|
137
|
+
* даты уезжают на часовой пояс, а «кто раньше» перестаёт работать. Строка, а не
|
|
138
|
+
* Date, потому что пакет — это JSON на диске.
|
|
139
|
+
*/
|
|
140
|
+
export declare const ImportDate: z.ZodString;
|
|
141
|
+
/**
|
|
142
|
+
* ПУТЬ К ФАЙЛУ ВЛОЖЕНИЯ ВНУТРИ ПАКЕТА — относительный, от манифеста
|
|
143
|
+
* (`files/a3-scheme.png`). Спека говорит «манифест JSON плюс папка файлов», и
|
|
144
|
+
* подкаталог тут не украшение: в выгрузке Confluence вложений много, и два
|
|
145
|
+
* разных документа спокойно принесут по своему `image.png` — плоские имена их
|
|
146
|
+
* столкнут, папка нет.
|
|
147
|
+
*
|
|
148
|
+
* БЕЛЫЙ СПИСОК, А НЕ ПЕРЕЧЕНЬ ЗАПРЕЩЁННОГО. Четыре круга ревью на этом же
|
|
149
|
+
* пакете (MOVE-7) чинили контракт «путь печатный» отказом от всё новых и новых
|
|
150
|
+
* находок — управляющие ASCII, невидимый Unicode классами `Cf`/`Zl`/`Zp`,
|
|
151
|
+
* категория `Mn` через `Default_Ignorable_Code_Point`, отдельно Брайль
|
|
152
|
+
* `U+2800`, отдельно `%` из-за percent-декодирования при резолве. Каждый раунд
|
|
153
|
+
* закрывал настоящую дыру и открывал место для следующей: гонка за
|
|
154
|
+
* запрещённым принципиально неполна. Путь внутри пакета при этом выбирает
|
|
155
|
+
* сам коннектор — это не то поле, где живёт человекочитаемое имя (оно в
|
|
156
|
+
* `name` у `ImportAttachment`), поэтому сузить алфавит для него можно без
|
|
157
|
+
* потери: скобки, пробелы, кириллица никуда не делись, просто переехали в
|
|
158
|
+
* `name`.
|
|
159
|
+
*
|
|
160
|
+
* `^[A-Za-z0-9._-]+(?:/[A-Za-z0-9._-]+)*$` закрывает весь класс разом, по
|
|
161
|
+
* построению, а не перечислением:
|
|
162
|
+
* • пробел, скобки, `%`, `\` — вне алфавита, схема их не пропустит;
|
|
163
|
+
* • НЕВИДИМЫЙ Unicode и одиночные суррогаты — тоже вне алфавита: строка,
|
|
164
|
+
* целиком состоящая из `A-Za-z0-9._-`, ни того, ни другого не содержит
|
|
165
|
+
* физически. `IMPORT_INVISIBLE_CHARS_RE`/`IMPORT_LONE_SURROGATE_RE` здесь
|
|
166
|
+
* больше не нужны — они остаются только у `ImportExternalId`, чей алфавит
|
|
167
|
+
* сознательно не сужен (см. его комментарий);
|
|
168
|
+
* • ведущий `/`, пустой сегмент (`files//a.png`), обратный слэш —
|
|
169
|
+
* исключены формой регулярки (сегмент — непустая последовательность
|
|
170
|
+
* разрешённых символов, разделитель ровно один `/`).
|
|
171
|
+
*
|
|
172
|
+
* Регулярка НЕ ловит сегменты `.` и `..` — они состоят из разрешённых
|
|
173
|
+
* символов и под неё подходят, поэтому запрет на них — отдельная проверка
|
|
174
|
+
* ниже, как и раньше.
|
|
175
|
+
*/
|
|
176
|
+
export declare const IMPORT_FILE_PATH_RE: RegExp;
|
|
177
|
+
export declare const ImportFilePath: z.ZodEffects<z.ZodString, string, string>;
|
|
178
|
+
/**
|
|
179
|
+
* Человек из исходного сервиса. Здесь он ПРИЗРАК: коннектор знает только то, что
|
|
180
|
+
* было в экспорте, а сопоставление с нашими пользователями — работа ядра
|
|
181
|
+
* (MOVE-10). Поэтому обязателен ровно `externalId`, а `name` — то, что показать
|
|
182
|
+
* человеку, пока сопоставления нет.
|
|
183
|
+
*
|
|
184
|
+
* `email` тут ПОДСКАЗКА ДЛЯ СОПОСТАВЛЕНИЯ, а не адрес входа: спека прямо
|
|
185
|
+
* запрещает класть чужую почту в наше уникальное поле `User.email`, иначе импорт
|
|
186
|
+
* становится способом занять чужой адрес.
|
|
187
|
+
*/
|
|
188
|
+
export declare const ImportPerson: z.ZodObject<{
|
|
189
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
190
|
+
name: z.ZodString;
|
|
191
|
+
email: z.ZodOptional<z.ZodString>;
|
|
192
|
+
/** Логин в исходном сервисе — вторая зацепка, когда почты в экспорте нет. */
|
|
193
|
+
username: z.ZodOptional<z.ZodString>;
|
|
194
|
+
avatarUrl: z.ZodOptional<z.ZodString>;
|
|
195
|
+
}, "strip", z.ZodTypeAny, {
|
|
196
|
+
externalId: string;
|
|
197
|
+
name: string;
|
|
198
|
+
email?: string | undefined;
|
|
199
|
+
username?: string | undefined;
|
|
200
|
+
avatarUrl?: string | undefined;
|
|
201
|
+
}, {
|
|
202
|
+
externalId: string;
|
|
203
|
+
name: string;
|
|
204
|
+
email?: string | undefined;
|
|
205
|
+
username?: string | undefined;
|
|
206
|
+
avatarUrl?: string | undefined;
|
|
207
|
+
}>;
|
|
208
|
+
export type ImportPerson = z.infer<typeof ImportPerson>;
|
|
209
|
+
/**
|
|
210
|
+
* Пространство — верхний контейнер исходного сервиса (space в Confluence,
|
|
211
|
+
* teamspace в Notion). Во что оно превратится у нас, решает ядро.
|
|
212
|
+
*/
|
|
213
|
+
export declare const ImportSpace: z.ZodObject<{
|
|
214
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
215
|
+
title: z.ZodString;
|
|
216
|
+
descriptionHtml: z.ZodOptional<z.ZodString>;
|
|
217
|
+
/** Ссылка на пространство в исходном сервисе — чтобы после переезда было куда вернуться. */
|
|
218
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
219
|
+
}, "strip", z.ZodTypeAny, {
|
|
220
|
+
externalId: string;
|
|
221
|
+
title: string;
|
|
222
|
+
descriptionHtml?: string | undefined;
|
|
223
|
+
sourceUrl?: string | undefined;
|
|
224
|
+
}, {
|
|
225
|
+
externalId: string;
|
|
226
|
+
title: string;
|
|
227
|
+
descriptionHtml?: string | undefined;
|
|
228
|
+
sourceUrl?: string | undefined;
|
|
229
|
+
}>;
|
|
230
|
+
export type ImportSpace = z.infer<typeof ImportSpace>;
|
|
231
|
+
/**
|
|
232
|
+
* Состояние спринта. ПЕРЕЧИСЛЕНИЕМ, в отличие от `status` у задачи, который
|
|
233
|
+
* едет строкой исходного сервиса, — и это не непоследовательность. Статусов у
|
|
234
|
+
* чужого трекера сколько угодно и своих, раскладывать их по нашим умеет только
|
|
235
|
+
* ядро с картой воркфлоу; состояний же спринта ровно три, и они одни и те же у
|
|
236
|
+
* Jira, YouTrack и у нас (`Sprint.state` в схеме: `planned | active |
|
|
237
|
+
* completed`). Перечисление здесь ловит опечатку коннектора на входе, а не
|
|
238
|
+
* через неделю в отчёте.
|
|
239
|
+
*/
|
|
240
|
+
export declare const IMPORT_SPRINT_STATES: readonly ["planned", "active", "completed"];
|
|
241
|
+
export type ImportSprintState = (typeof IMPORT_SPRINT_STATES)[number];
|
|
242
|
+
/**
|
|
243
|
+
* MOVE-28: СПРИНТ — итерация исходного трекера (sprint в Jira, sprint/веха в
|
|
244
|
+
* YouTrack). Первый шаг к переносу спринтов спринтами, а не меткой на задаче
|
|
245
|
+
* (MOVE-29): здесь только форма, ни выкачки, ни раскладки по нашим таблицам.
|
|
246
|
+
*
|
|
247
|
+
* `spaceId` ОБЯЗАТЕЛЕН — спринт принадлежит пространству, как страница (у
|
|
248
|
+
* задачи `spaceId` необязательный, потому что у плоских экспортов задача
|
|
249
|
+
* приезжает без проекта и уезжает в запасной; у спринта такого случая нет:
|
|
250
|
+
* итерация без доски бессмысленна, и коннектор, который не знает её
|
|
251
|
+
* пространства, обязан споткнуться здесь, а не создать бесхозный спринт).
|
|
252
|
+
*
|
|
253
|
+
* Как и везде в пакете, `externalId` — id ИЗ ЧУЖОГО СЕРВИСА, а не наш cuid.
|
|
254
|
+
* Даты необязательные: у планируемого спринта их сплошь и рядом нет.
|
|
255
|
+
*/
|
|
256
|
+
export declare const ImportSprint: z.ZodObject<{
|
|
257
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
258
|
+
spaceId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
259
|
+
name: z.ZodString;
|
|
260
|
+
/** Цель итерации — короткий текст, который человек писал в трекере. */
|
|
261
|
+
goal: z.ZodOptional<z.ZodString>;
|
|
262
|
+
startsAt: z.ZodOptional<z.ZodString>;
|
|
263
|
+
endsAt: z.ZodOptional<z.ZodString>;
|
|
264
|
+
state: z.ZodEnum<["planned", "active", "completed"]>;
|
|
265
|
+
}, "strip", z.ZodTypeAny, {
|
|
266
|
+
externalId: string;
|
|
267
|
+
spaceId: string;
|
|
268
|
+
name: string;
|
|
269
|
+
goal?: string | undefined;
|
|
270
|
+
startsAt?: string | undefined;
|
|
271
|
+
endsAt?: string | undefined;
|
|
272
|
+
state: "active" | "completed" | "planned";
|
|
273
|
+
}, {
|
|
274
|
+
externalId: string;
|
|
275
|
+
spaceId: string;
|
|
276
|
+
name: string;
|
|
277
|
+
goal?: string | undefined;
|
|
278
|
+
startsAt?: string | undefined;
|
|
279
|
+
endsAt?: string | undefined;
|
|
280
|
+
state: "active" | "completed" | "planned";
|
|
281
|
+
}>;
|
|
282
|
+
export type ImportSprint = z.infer<typeof ImportSprint>;
|
|
283
|
+
/**
|
|
284
|
+
* К чему прицеплен комментарий (`on` из спеки). `id` здесь — `externalId` цели
|
|
285
|
+
* в исходном сервисе, не наш cuid.
|
|
286
|
+
*/
|
|
287
|
+
export declare const ImportCommentTarget: z.ZodObject<{
|
|
288
|
+
kind: z.ZodEnum<["page", "task"]>;
|
|
289
|
+
id: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
290
|
+
}, "strip", z.ZodTypeAny, {
|
|
291
|
+
kind: "page" | "task";
|
|
292
|
+
id: string;
|
|
293
|
+
}, {
|
|
294
|
+
kind: "page" | "task";
|
|
295
|
+
id: string;
|
|
296
|
+
}>;
|
|
297
|
+
export type ImportCommentTarget = z.infer<typeof ImportCommentTarget>;
|
|
298
|
+
/**
|
|
299
|
+
* Страница.
|
|
300
|
+
*
|
|
301
|
+
* ВНИМАНИЕ К ИМЕНАМ: `spaceId`, `parentId`, `authorId` — это `externalId` ИЗ
|
|
302
|
+
* ЧУЖОГО СЕРВИСА, а не ссылки на наши таблицы. Имена взяты из спеки, и по имени
|
|
303
|
+
* они читаются как внешние ключи на нашу базу — по сути это не так, наши id
|
|
304
|
+
* появятся только в ядре, когда сущность уже создана.
|
|
305
|
+
*
|
|
306
|
+
* Дерево едет ССЫЛКАМИ (`parentId`), а не вложенностью: вложенность заставила бы
|
|
307
|
+
* коннектор собирать дерево целиком в памяти и ломалась бы на циклах, которые в
|
|
308
|
+
* чужих экспортах встречаются. `null`/отсутствие — корень пространства.
|
|
309
|
+
*/
|
|
310
|
+
export declare const ImportPage: z.ZodObject<{
|
|
311
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
312
|
+
spaceId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
313
|
+
parentId: z.ZodOptional<z.ZodNullable<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>>;
|
|
314
|
+
title: z.ZodString;
|
|
315
|
+
/** Тело в HTML — см. про ProseMirror в шапке файла. */
|
|
316
|
+
bodyHtml: z.ZodString;
|
|
317
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
318
|
+
/** Кто правил последним, если сервис это отдаёт, — иначе автор и правщик сливаются. */
|
|
319
|
+
lastEditorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
320
|
+
createdAt: z.ZodString;
|
|
321
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
322
|
+
/** `externalId` вложений этой страницы — сами файлы в `attachments` манифеста. */
|
|
323
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>, "many">>;
|
|
324
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
325
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
326
|
+
}, "strip", z.ZodTypeAny, {
|
|
327
|
+
externalId: string;
|
|
328
|
+
spaceId: string;
|
|
329
|
+
parentId?: string | null | undefined;
|
|
330
|
+
title: string;
|
|
331
|
+
bodyHtml: string;
|
|
332
|
+
authorId?: string | undefined;
|
|
333
|
+
lastEditorId?: string | undefined;
|
|
334
|
+
createdAt: string;
|
|
335
|
+
updatedAt?: string | undefined;
|
|
336
|
+
attachments?: string[] | undefined;
|
|
337
|
+
labels?: string[] | undefined;
|
|
338
|
+
sourceUrl?: string | undefined;
|
|
339
|
+
}, {
|
|
340
|
+
externalId: string;
|
|
341
|
+
spaceId: string;
|
|
342
|
+
parentId?: string | null | undefined;
|
|
343
|
+
title: string;
|
|
344
|
+
bodyHtml: string;
|
|
345
|
+
authorId?: string | undefined;
|
|
346
|
+
lastEditorId?: string | undefined;
|
|
347
|
+
createdAt: string;
|
|
348
|
+
updatedAt?: string | undefined;
|
|
349
|
+
attachments?: string[] | undefined;
|
|
350
|
+
labels?: string[] | undefined;
|
|
351
|
+
sourceUrl?: string | undefined;
|
|
352
|
+
}>;
|
|
353
|
+
export type ImportPage = z.infer<typeof ImportPage>;
|
|
354
|
+
/**
|
|
355
|
+
* Задача. Статус едет СТРОКОЙ ИСХОДНОГО СЕРВИСА, а не нашим enum'ом: у чужого
|
|
356
|
+
* трекера свои состояния, и раскладывать их по нашим — работа ядра, где для
|
|
357
|
+
* этого есть карта воркфлоу. Коннектор, который угадывает наш статус, обманет
|
|
358
|
+
* ровно один раз и незаметно.
|
|
359
|
+
*
|
|
360
|
+
* `spaceId`, `parentId`, `authorId`, `assigneeId` — тоже внешние id, см. `ImportPage`.
|
|
361
|
+
*/
|
|
362
|
+
export declare const ImportTask: z.ZodObject<{
|
|
363
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
364
|
+
spaceId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
365
|
+
parentId: z.ZodOptional<z.ZodNullable<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>>;
|
|
366
|
+
title: z.ZodString;
|
|
367
|
+
descriptionHtml: z.ZodOptional<z.ZodString>;
|
|
368
|
+
status: z.ZodOptional<z.ZodString>;
|
|
369
|
+
priority: z.ZodOptional<z.ZodString>;
|
|
370
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
371
|
+
assigneeId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
372
|
+
/**
|
|
373
|
+
* MOVE-28: в каком спринте задача — `externalId` из `sprints`. Необязателен
|
|
374
|
+
* дважды: у источника без спринтов их нет вовсе, и внутри источника со
|
|
375
|
+
* спринтами задача может лежать в бэклоге.
|
|
376
|
+
*/
|
|
377
|
+
sprintId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
378
|
+
createdAt: z.ZodString;
|
|
379
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
380
|
+
dueAt: z.ZodOptional<z.ZodString>;
|
|
381
|
+
completedAt: z.ZodOptional<z.ZodString>;
|
|
382
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>, "many">>;
|
|
383
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
384
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
385
|
+
}, "strip", z.ZodTypeAny, {
|
|
386
|
+
externalId: string;
|
|
387
|
+
spaceId?: string | undefined;
|
|
388
|
+
parentId?: string | null | undefined;
|
|
389
|
+
title: string;
|
|
390
|
+
descriptionHtml?: string | undefined;
|
|
391
|
+
status?: string | undefined;
|
|
392
|
+
priority?: string | undefined;
|
|
393
|
+
authorId?: string | undefined;
|
|
394
|
+
assigneeId?: string | undefined;
|
|
395
|
+
sprintId?: string | undefined;
|
|
396
|
+
createdAt: string;
|
|
397
|
+
updatedAt?: string | undefined;
|
|
398
|
+
dueAt?: string | undefined;
|
|
399
|
+
completedAt?: string | undefined;
|
|
400
|
+
attachments?: string[] | undefined;
|
|
401
|
+
labels?: string[] | undefined;
|
|
402
|
+
sourceUrl?: string | undefined;
|
|
403
|
+
}, {
|
|
404
|
+
externalId: string;
|
|
405
|
+
spaceId?: string | undefined;
|
|
406
|
+
parentId?: string | null | undefined;
|
|
407
|
+
title: string;
|
|
408
|
+
descriptionHtml?: string | undefined;
|
|
409
|
+
status?: string | undefined;
|
|
410
|
+
priority?: string | undefined;
|
|
411
|
+
authorId?: string | undefined;
|
|
412
|
+
assigneeId?: string | undefined;
|
|
413
|
+
sprintId?: string | undefined;
|
|
414
|
+
createdAt: string;
|
|
415
|
+
updatedAt?: string | undefined;
|
|
416
|
+
dueAt?: string | undefined;
|
|
417
|
+
completedAt?: string | undefined;
|
|
418
|
+
attachments?: string[] | undefined;
|
|
419
|
+
labels?: string[] | undefined;
|
|
420
|
+
sourceUrl?: string | undefined;
|
|
421
|
+
}>;
|
|
422
|
+
export type ImportTask = z.infer<typeof ImportTask>;
|
|
423
|
+
/**
|
|
424
|
+
* Комментарий к странице или задаче. `parentId` — ответ на другой комментарий
|
|
425
|
+
* (ветка обсуждения), не владелец: владелец всегда в `on`.
|
|
426
|
+
*/
|
|
427
|
+
export declare const ImportComment: z.ZodObject<{
|
|
428
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
429
|
+
on: z.ZodObject<{
|
|
430
|
+
kind: z.ZodEnum<["page", "task"]>;
|
|
431
|
+
id: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
432
|
+
}, "strip", z.ZodTypeAny, {
|
|
433
|
+
kind: "page" | "task";
|
|
434
|
+
id: string;
|
|
435
|
+
}, {
|
|
436
|
+
kind: "page" | "task";
|
|
437
|
+
id: string;
|
|
438
|
+
}>;
|
|
439
|
+
parentId: z.ZodOptional<z.ZodNullable<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>>;
|
|
440
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
441
|
+
bodyHtml: z.ZodString;
|
|
442
|
+
createdAt: z.ZodString;
|
|
443
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
444
|
+
}, "strip", z.ZodTypeAny, {
|
|
445
|
+
externalId: string;
|
|
446
|
+
on: {
|
|
447
|
+
kind: "page" | "task";
|
|
448
|
+
id: string;
|
|
449
|
+
};
|
|
450
|
+
parentId?: string | null | undefined;
|
|
451
|
+
authorId?: string | undefined;
|
|
452
|
+
bodyHtml: string;
|
|
453
|
+
createdAt: string;
|
|
454
|
+
updatedAt?: string | undefined;
|
|
455
|
+
}, {
|
|
456
|
+
externalId: string;
|
|
457
|
+
on: {
|
|
458
|
+
kind: "page" | "task";
|
|
459
|
+
id: string;
|
|
460
|
+
};
|
|
461
|
+
parentId?: string | null | undefined;
|
|
462
|
+
authorId?: string | undefined;
|
|
463
|
+
bodyHtml: string;
|
|
464
|
+
createdAt: string;
|
|
465
|
+
updatedAt?: string | undefined;
|
|
466
|
+
}>;
|
|
467
|
+
export type ImportComment = z.infer<typeof ImportComment>;
|
|
468
|
+
/**
|
|
469
|
+
* Вложение. Метаданные здесь, БАЙТЫ — файлом внутри пакета (`file`). Владельца у
|
|
470
|
+
* вложения нет: на него ссылаются страница и задача через свой `attachments`,
|
|
471
|
+
* как и показано в спеке — один и тот же файл так может принадлежать нескольким
|
|
472
|
+
* сущностям, не размножаясь.
|
|
473
|
+
*/
|
|
474
|
+
/**
|
|
475
|
+
* Имя файла, каким его видит ЧЕЛОВЕК (в отличие от `file` — служебного пути,
|
|
476
|
+
* белый список для которого сузил алфавит до ASCII). `name` алфавит не сужает
|
|
477
|
+
* — скобки, пробелы, кириллица здесь как раз законны, это и есть то поле,
|
|
478
|
+
* куда они переехали из `ImportFilePath` (см. её комментарий). Но именно
|
|
479
|
+
* поэтому `name` — единственное поле в файле, которое ПОКАЗЫВАЕТСЯ человеку
|
|
480
|
+
* буквально как имя файла, и оставлять его вовсе без защиты от невидимых
|
|
481
|
+
* символов значило бы закрыть RTL-override-подмену расширения у `file` и
|
|
482
|
+
* `externalId` и оставить открытой ровно у того поля, где она опаснее всего:
|
|
483
|
+
* `"invoicefdp.exe"` пройдёт схему и отрисуется человеку как
|
|
484
|
+
* «invoice.pdf» при реальном `.exe`. Найдено ревью SPHINX на MOVE-7.
|
|
485
|
+
*/
|
|
486
|
+
export declare const ImportAttachmentName: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
|
|
487
|
+
export declare const ImportAttachment: z.ZodObject<{
|
|
488
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
489
|
+
/** Относительный путь внутри пакета: `files/a3-scheme.png`. */
|
|
490
|
+
file: z.ZodEffects<z.ZodString, string, string>;
|
|
491
|
+
/** Как файл назывался у человека — `file` в пакете может быть обеззаражен коннектором. */
|
|
492
|
+
name: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
|
|
493
|
+
mime: z.ZodOptional<z.ZodString>;
|
|
494
|
+
byteSize: z.ZodOptional<z.ZodNumber>;
|
|
495
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
496
|
+
createdAt: z.ZodOptional<z.ZodString>;
|
|
497
|
+
}, "strip", z.ZodTypeAny, {
|
|
498
|
+
externalId: string;
|
|
499
|
+
file: string;
|
|
500
|
+
name?: string | undefined;
|
|
501
|
+
mime?: string | undefined;
|
|
502
|
+
byteSize?: number | undefined;
|
|
503
|
+
authorId?: string | undefined;
|
|
504
|
+
createdAt?: string | undefined;
|
|
505
|
+
}, {
|
|
506
|
+
externalId: string;
|
|
507
|
+
file: string;
|
|
508
|
+
name?: string | undefined;
|
|
509
|
+
mime?: string | undefined;
|
|
510
|
+
byteSize?: number | undefined;
|
|
511
|
+
authorId?: string | undefined;
|
|
512
|
+
createdAt?: string | undefined;
|
|
513
|
+
}>;
|
|
514
|
+
export type ImportAttachment = z.infer<typeof ImportAttachment>;
|
|
515
|
+
/**
|
|
516
|
+
* МАНИФЕСТ — то, что коннектор кладёт в `manifest.json`, а ядро читает первым и
|
|
517
|
+
* единственным входом. Все сущности здесь; рядом лежит только папка файлов.
|
|
518
|
+
*/
|
|
519
|
+
export declare const ImportPackageManifest: z.ZodObject<{
|
|
520
|
+
/**
|
|
521
|
+
* Версия формата. Стоит ПЕРВЫМ полем и проверяется ДО остальной схемы (см.
|
|
522
|
+
* `parseImportPackageManifest`): пакет завтрашней версии обязан получить
|
|
523
|
+
* внятный отказ, а не ворох ошибок валидации, из которых не видно, что дело
|
|
524
|
+
* в версии.
|
|
525
|
+
*/
|
|
526
|
+
formatVersion: z.ZodNumber;
|
|
527
|
+
/**
|
|
528
|
+
* Машинное имя исходного сервиса (`confluence`). Входит в ключ
|
|
529
|
+
* идемпотентности: одинаковые `externalId` из разных сервисов — разные
|
|
530
|
+
* сущности, и без имени сервиса второй импорт «узнал» бы чужое.
|
|
531
|
+
*/
|
|
532
|
+
source: z.ZodString;
|
|
533
|
+
/** Адрес инсталляции — на self-hosted сервисах одного имени сервиса мало. */
|
|
534
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
535
|
+
/** Когда снят экспорт (не когда собран пакет) — по нему видно, насколько данные протухли. */
|
|
536
|
+
exportedAt: z.ZodOptional<z.ZodString>;
|
|
537
|
+
/** Чем собран пакет — чтобы по кривому импорту было видно, какой коннектор чинить. */
|
|
538
|
+
generator: z.ZodOptional<z.ZodObject<{
|
|
539
|
+
name: z.ZodString;
|
|
540
|
+
version: z.ZodOptional<z.ZodString>;
|
|
541
|
+
}, "strip", z.ZodTypeAny, {
|
|
542
|
+
name: string;
|
|
543
|
+
version?: string | undefined;
|
|
544
|
+
}, {
|
|
545
|
+
name: string;
|
|
546
|
+
version?: string | undefined;
|
|
547
|
+
}>>;
|
|
548
|
+
people: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
549
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
550
|
+
name: z.ZodString;
|
|
551
|
+
email: z.ZodOptional<z.ZodString>;
|
|
552
|
+
/** Логин в исходном сервисе — вторая зацепка, когда почты в экспорте нет. */
|
|
553
|
+
username: z.ZodOptional<z.ZodString>;
|
|
554
|
+
avatarUrl: z.ZodOptional<z.ZodString>;
|
|
555
|
+
}, "strip", z.ZodTypeAny, {
|
|
556
|
+
externalId: string;
|
|
557
|
+
name: string;
|
|
558
|
+
email?: string | undefined;
|
|
559
|
+
username?: string | undefined;
|
|
560
|
+
avatarUrl?: string | undefined;
|
|
561
|
+
}, {
|
|
562
|
+
externalId: string;
|
|
563
|
+
name: string;
|
|
564
|
+
email?: string | undefined;
|
|
565
|
+
username?: string | undefined;
|
|
566
|
+
avatarUrl?: string | undefined;
|
|
567
|
+
}>, "many">>;
|
|
568
|
+
spaces: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
569
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
570
|
+
title: z.ZodString;
|
|
571
|
+
descriptionHtml: z.ZodOptional<z.ZodString>;
|
|
572
|
+
/** Ссылка на пространство в исходном сервисе — чтобы после переезда было куда вернуться. */
|
|
573
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
574
|
+
}, "strip", z.ZodTypeAny, {
|
|
575
|
+
externalId: string;
|
|
576
|
+
title: string;
|
|
577
|
+
descriptionHtml?: string | undefined;
|
|
578
|
+
sourceUrl?: string | undefined;
|
|
579
|
+
}, {
|
|
580
|
+
externalId: string;
|
|
581
|
+
title: string;
|
|
582
|
+
descriptionHtml?: string | undefined;
|
|
583
|
+
sourceUrl?: string | undefined;
|
|
584
|
+
}>, "many">>;
|
|
585
|
+
/**
|
|
586
|
+
* MOVE-28. Секция НЕОБЯЗАТЕЛЬНАЯ, и `.default([])` здесь — вся обратная
|
|
587
|
+
* совместимость: пакет Confluence и уже выкачанные пакеты Jira/YouTrack
|
|
588
|
+
* секции не знают, разбираются как раньше и получают пустой список.
|
|
589
|
+
* `formatVersion` по этой же причине не двигается — см. её комментарий:
|
|
590
|
+
* версия растёт на ЛОМАЮЩЕМ изменении формы.
|
|
591
|
+
*/
|
|
592
|
+
sprints: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
593
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
594
|
+
spaceId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
595
|
+
name: z.ZodString;
|
|
596
|
+
/** Цель итерации — короткий текст, который человек писал в трекере. */
|
|
597
|
+
goal: z.ZodOptional<z.ZodString>;
|
|
598
|
+
startsAt: z.ZodOptional<z.ZodString>;
|
|
599
|
+
endsAt: z.ZodOptional<z.ZodString>;
|
|
600
|
+
state: z.ZodEnum<["planned", "active", "completed"]>;
|
|
601
|
+
}, "strip", z.ZodTypeAny, {
|
|
602
|
+
externalId: string;
|
|
603
|
+
spaceId: string;
|
|
604
|
+
name: string;
|
|
605
|
+
goal?: string | undefined;
|
|
606
|
+
startsAt?: string | undefined;
|
|
607
|
+
endsAt?: string | undefined;
|
|
608
|
+
state: "active" | "completed" | "planned";
|
|
609
|
+
}, {
|
|
610
|
+
externalId: string;
|
|
611
|
+
spaceId: string;
|
|
612
|
+
name: string;
|
|
613
|
+
goal?: string | undefined;
|
|
614
|
+
startsAt?: string | undefined;
|
|
615
|
+
endsAt?: string | undefined;
|
|
616
|
+
state: "active" | "completed" | "planned";
|
|
617
|
+
}>, "many">>;
|
|
618
|
+
pages: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
619
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
620
|
+
spaceId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
621
|
+
parentId: z.ZodOptional<z.ZodNullable<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>>;
|
|
622
|
+
title: z.ZodString;
|
|
623
|
+
/** Тело в HTML — см. про ProseMirror в шапке файла. */
|
|
624
|
+
bodyHtml: z.ZodString;
|
|
625
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
626
|
+
/** Кто правил последним, если сервис это отдаёт, — иначе автор и правщик сливаются. */
|
|
627
|
+
lastEditorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
628
|
+
createdAt: z.ZodString;
|
|
629
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
630
|
+
/** `externalId` вложений этой страницы — сами файлы в `attachments` манифеста. */
|
|
631
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>, "many">>;
|
|
632
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
633
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
634
|
+
}, "strip", z.ZodTypeAny, {
|
|
635
|
+
externalId: string;
|
|
636
|
+
spaceId: string;
|
|
637
|
+
parentId?: string | null | undefined;
|
|
638
|
+
title: string;
|
|
639
|
+
bodyHtml: string;
|
|
640
|
+
authorId?: string | undefined;
|
|
641
|
+
lastEditorId?: string | undefined;
|
|
642
|
+
createdAt: string;
|
|
643
|
+
updatedAt?: string | undefined;
|
|
644
|
+
attachments?: string[] | undefined;
|
|
645
|
+
labels?: string[] | undefined;
|
|
646
|
+
sourceUrl?: string | undefined;
|
|
647
|
+
}, {
|
|
648
|
+
externalId: string;
|
|
649
|
+
spaceId: string;
|
|
650
|
+
parentId?: string | null | undefined;
|
|
651
|
+
title: string;
|
|
652
|
+
bodyHtml: string;
|
|
653
|
+
authorId?: string | undefined;
|
|
654
|
+
lastEditorId?: string | undefined;
|
|
655
|
+
createdAt: string;
|
|
656
|
+
updatedAt?: string | undefined;
|
|
657
|
+
attachments?: string[] | undefined;
|
|
658
|
+
labels?: string[] | undefined;
|
|
659
|
+
sourceUrl?: string | undefined;
|
|
660
|
+
}>, "many">>;
|
|
661
|
+
tasks: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
662
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
663
|
+
spaceId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
664
|
+
parentId: z.ZodOptional<z.ZodNullable<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>>;
|
|
665
|
+
title: z.ZodString;
|
|
666
|
+
descriptionHtml: z.ZodOptional<z.ZodString>;
|
|
667
|
+
status: z.ZodOptional<z.ZodString>;
|
|
668
|
+
priority: z.ZodOptional<z.ZodString>;
|
|
669
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
670
|
+
assigneeId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
671
|
+
/**
|
|
672
|
+
* MOVE-28: в каком спринте задача — `externalId` из `sprints`. Необязателен
|
|
673
|
+
* дважды: у источника без спринтов их нет вовсе, и внутри источника со
|
|
674
|
+
* спринтами задача может лежать в бэклоге.
|
|
675
|
+
*/
|
|
676
|
+
sprintId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
677
|
+
createdAt: z.ZodString;
|
|
678
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
679
|
+
dueAt: z.ZodOptional<z.ZodString>;
|
|
680
|
+
completedAt: z.ZodOptional<z.ZodString>;
|
|
681
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>, "many">>;
|
|
682
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
683
|
+
sourceUrl: z.ZodOptional<z.ZodString>;
|
|
684
|
+
}, "strip", z.ZodTypeAny, {
|
|
685
|
+
externalId: string;
|
|
686
|
+
spaceId?: string | undefined;
|
|
687
|
+
parentId?: string | null | undefined;
|
|
688
|
+
title: string;
|
|
689
|
+
descriptionHtml?: string | undefined;
|
|
690
|
+
status?: string | undefined;
|
|
691
|
+
priority?: string | undefined;
|
|
692
|
+
authorId?: string | undefined;
|
|
693
|
+
assigneeId?: string | undefined;
|
|
694
|
+
sprintId?: string | undefined;
|
|
695
|
+
createdAt: string;
|
|
696
|
+
updatedAt?: string | undefined;
|
|
697
|
+
dueAt?: string | undefined;
|
|
698
|
+
completedAt?: string | undefined;
|
|
699
|
+
attachments?: string[] | undefined;
|
|
700
|
+
labels?: string[] | undefined;
|
|
701
|
+
sourceUrl?: string | undefined;
|
|
702
|
+
}, {
|
|
703
|
+
externalId: string;
|
|
704
|
+
spaceId?: string | undefined;
|
|
705
|
+
parentId?: string | null | undefined;
|
|
706
|
+
title: string;
|
|
707
|
+
descriptionHtml?: string | undefined;
|
|
708
|
+
status?: string | undefined;
|
|
709
|
+
priority?: string | undefined;
|
|
710
|
+
authorId?: string | undefined;
|
|
711
|
+
assigneeId?: string | undefined;
|
|
712
|
+
sprintId?: string | undefined;
|
|
713
|
+
createdAt: string;
|
|
714
|
+
updatedAt?: string | undefined;
|
|
715
|
+
dueAt?: string | undefined;
|
|
716
|
+
completedAt?: string | undefined;
|
|
717
|
+
attachments?: string[] | undefined;
|
|
718
|
+
labels?: string[] | undefined;
|
|
719
|
+
sourceUrl?: string | undefined;
|
|
720
|
+
}>, "many">>;
|
|
721
|
+
comments: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
722
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
723
|
+
on: z.ZodObject<{
|
|
724
|
+
kind: z.ZodEnum<["page", "task"]>;
|
|
725
|
+
id: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
726
|
+
}, "strip", z.ZodTypeAny, {
|
|
727
|
+
kind: "page" | "task";
|
|
728
|
+
id: string;
|
|
729
|
+
}, {
|
|
730
|
+
kind: "page" | "task";
|
|
731
|
+
id: string;
|
|
732
|
+
}>;
|
|
733
|
+
parentId: z.ZodOptional<z.ZodNullable<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>>;
|
|
734
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
735
|
+
bodyHtml: z.ZodString;
|
|
736
|
+
createdAt: z.ZodString;
|
|
737
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
|
738
|
+
}, "strip", z.ZodTypeAny, {
|
|
739
|
+
externalId: string;
|
|
740
|
+
on: {
|
|
741
|
+
kind: "page" | "task";
|
|
742
|
+
id: string;
|
|
743
|
+
};
|
|
744
|
+
parentId?: string | null | undefined;
|
|
745
|
+
authorId?: string | undefined;
|
|
746
|
+
bodyHtml: string;
|
|
747
|
+
createdAt: string;
|
|
748
|
+
updatedAt?: string | undefined;
|
|
749
|
+
}, {
|
|
750
|
+
externalId: string;
|
|
751
|
+
on: {
|
|
752
|
+
kind: "page" | "task";
|
|
753
|
+
id: string;
|
|
754
|
+
};
|
|
755
|
+
parentId?: string | null | undefined;
|
|
756
|
+
authorId?: string | undefined;
|
|
757
|
+
bodyHtml: string;
|
|
758
|
+
createdAt: string;
|
|
759
|
+
updatedAt?: string | undefined;
|
|
760
|
+
}>, "many">>;
|
|
761
|
+
attachments: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
762
|
+
externalId: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
|
|
763
|
+
/** Относительный путь внутри пакета: `files/a3-scheme.png`. */
|
|
764
|
+
file: z.ZodEffects<z.ZodString, string, string>;
|
|
765
|
+
/** Как файл назывался у человека — `file` в пакете может быть обеззаражен коннектором. */
|
|
766
|
+
name: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
|
|
767
|
+
mime: z.ZodOptional<z.ZodString>;
|
|
768
|
+
byteSize: z.ZodOptional<z.ZodNumber>;
|
|
769
|
+
authorId: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
770
|
+
createdAt: z.ZodOptional<z.ZodString>;
|
|
771
|
+
}, "strip", z.ZodTypeAny, {
|
|
772
|
+
externalId: string;
|
|
773
|
+
file: string;
|
|
774
|
+
name?: string | undefined;
|
|
775
|
+
mime?: string | undefined;
|
|
776
|
+
byteSize?: number | undefined;
|
|
777
|
+
authorId?: string | undefined;
|
|
778
|
+
createdAt?: string | undefined;
|
|
779
|
+
}, {
|
|
780
|
+
externalId: string;
|
|
781
|
+
file: string;
|
|
782
|
+
name?: string | undefined;
|
|
783
|
+
mime?: string | undefined;
|
|
784
|
+
byteSize?: number | undefined;
|
|
785
|
+
authorId?: string | undefined;
|
|
786
|
+
createdAt?: string | undefined;
|
|
787
|
+
}>, "many">>;
|
|
788
|
+
}, "strip", z.ZodTypeAny, {
|
|
789
|
+
formatVersion: number;
|
|
790
|
+
source: string;
|
|
791
|
+
sourceUrl?: string | undefined;
|
|
792
|
+
exportedAt?: string | undefined;
|
|
793
|
+
generator?: {
|
|
794
|
+
name: string;
|
|
795
|
+
version?: string | undefined;
|
|
796
|
+
} | undefined;
|
|
797
|
+
people: {
|
|
798
|
+
externalId: string;
|
|
799
|
+
name: string;
|
|
800
|
+
email?: string | undefined;
|
|
801
|
+
username?: string | undefined;
|
|
802
|
+
avatarUrl?: string | undefined;
|
|
803
|
+
}[];
|
|
804
|
+
spaces: {
|
|
805
|
+
externalId: string;
|
|
806
|
+
title: string;
|
|
807
|
+
descriptionHtml?: string | undefined;
|
|
808
|
+
sourceUrl?: string | undefined;
|
|
809
|
+
}[];
|
|
810
|
+
sprints: {
|
|
811
|
+
externalId: string;
|
|
812
|
+
spaceId: string;
|
|
813
|
+
name: string;
|
|
814
|
+
goal?: string | undefined;
|
|
815
|
+
startsAt?: string | undefined;
|
|
816
|
+
endsAt?: string | undefined;
|
|
817
|
+
state: "active" | "completed" | "planned";
|
|
818
|
+
}[];
|
|
819
|
+
pages: {
|
|
820
|
+
externalId: string;
|
|
821
|
+
spaceId: string;
|
|
822
|
+
parentId?: string | null | undefined;
|
|
823
|
+
title: string;
|
|
824
|
+
bodyHtml: string;
|
|
825
|
+
authorId?: string | undefined;
|
|
826
|
+
lastEditorId?: string | undefined;
|
|
827
|
+
createdAt: string;
|
|
828
|
+
updatedAt?: string | undefined;
|
|
829
|
+
attachments?: string[] | undefined;
|
|
830
|
+
labels?: string[] | undefined;
|
|
831
|
+
sourceUrl?: string | undefined;
|
|
832
|
+
}[];
|
|
833
|
+
tasks: {
|
|
834
|
+
externalId: string;
|
|
835
|
+
spaceId?: string | undefined;
|
|
836
|
+
parentId?: string | null | undefined;
|
|
837
|
+
title: string;
|
|
838
|
+
descriptionHtml?: string | undefined;
|
|
839
|
+
status?: string | undefined;
|
|
840
|
+
priority?: string | undefined;
|
|
841
|
+
authorId?: string | undefined;
|
|
842
|
+
assigneeId?: string | undefined;
|
|
843
|
+
sprintId?: string | undefined;
|
|
844
|
+
createdAt: string;
|
|
845
|
+
updatedAt?: string | undefined;
|
|
846
|
+
dueAt?: string | undefined;
|
|
847
|
+
completedAt?: string | undefined;
|
|
848
|
+
attachments?: string[] | undefined;
|
|
849
|
+
labels?: string[] | undefined;
|
|
850
|
+
sourceUrl?: string | undefined;
|
|
851
|
+
}[];
|
|
852
|
+
comments: {
|
|
853
|
+
externalId: string;
|
|
854
|
+
on: {
|
|
855
|
+
kind: "page" | "task";
|
|
856
|
+
id: string;
|
|
857
|
+
};
|
|
858
|
+
parentId?: string | null | undefined;
|
|
859
|
+
authorId?: string | undefined;
|
|
860
|
+
bodyHtml: string;
|
|
861
|
+
createdAt: string;
|
|
862
|
+
updatedAt?: string | undefined;
|
|
863
|
+
}[];
|
|
864
|
+
attachments: {
|
|
865
|
+
externalId: string;
|
|
866
|
+
file: string;
|
|
867
|
+
name?: string | undefined;
|
|
868
|
+
mime?: string | undefined;
|
|
869
|
+
byteSize?: number | undefined;
|
|
870
|
+
authorId?: string | undefined;
|
|
871
|
+
createdAt?: string | undefined;
|
|
872
|
+
}[];
|
|
873
|
+
}, {
|
|
874
|
+
formatVersion: number;
|
|
875
|
+
source: string;
|
|
876
|
+
sourceUrl?: string | undefined;
|
|
877
|
+
exportedAt?: string | undefined;
|
|
878
|
+
generator?: {
|
|
879
|
+
name: string;
|
|
880
|
+
version?: string | undefined;
|
|
881
|
+
} | undefined;
|
|
882
|
+
people?: {
|
|
883
|
+
externalId: string;
|
|
884
|
+
name: string;
|
|
885
|
+
email?: string | undefined;
|
|
886
|
+
username?: string | undefined;
|
|
887
|
+
avatarUrl?: string | undefined;
|
|
888
|
+
}[] | undefined;
|
|
889
|
+
spaces?: {
|
|
890
|
+
externalId: string;
|
|
891
|
+
title: string;
|
|
892
|
+
descriptionHtml?: string | undefined;
|
|
893
|
+
sourceUrl?: string | undefined;
|
|
894
|
+
}[] | undefined;
|
|
895
|
+
sprints?: {
|
|
896
|
+
externalId: string;
|
|
897
|
+
spaceId: string;
|
|
898
|
+
name: string;
|
|
899
|
+
goal?: string | undefined;
|
|
900
|
+
startsAt?: string | undefined;
|
|
901
|
+
endsAt?: string | undefined;
|
|
902
|
+
state: "active" | "completed" | "planned";
|
|
903
|
+
}[] | undefined;
|
|
904
|
+
pages?: {
|
|
905
|
+
externalId: string;
|
|
906
|
+
spaceId: string;
|
|
907
|
+
parentId?: string | null | undefined;
|
|
908
|
+
title: string;
|
|
909
|
+
bodyHtml: string;
|
|
910
|
+
authorId?: string | undefined;
|
|
911
|
+
lastEditorId?: string | undefined;
|
|
912
|
+
createdAt: string;
|
|
913
|
+
updatedAt?: string | undefined;
|
|
914
|
+
attachments?: string[] | undefined;
|
|
915
|
+
labels?: string[] | undefined;
|
|
916
|
+
sourceUrl?: string | undefined;
|
|
917
|
+
}[] | undefined;
|
|
918
|
+
tasks?: {
|
|
919
|
+
externalId: string;
|
|
920
|
+
spaceId?: string | undefined;
|
|
921
|
+
parentId?: string | null | undefined;
|
|
922
|
+
title: string;
|
|
923
|
+
descriptionHtml?: string | undefined;
|
|
924
|
+
status?: string | undefined;
|
|
925
|
+
priority?: string | undefined;
|
|
926
|
+
authorId?: string | undefined;
|
|
927
|
+
assigneeId?: string | undefined;
|
|
928
|
+
sprintId?: string | undefined;
|
|
929
|
+
createdAt: string;
|
|
930
|
+
updatedAt?: string | undefined;
|
|
931
|
+
dueAt?: string | undefined;
|
|
932
|
+
completedAt?: string | undefined;
|
|
933
|
+
attachments?: string[] | undefined;
|
|
934
|
+
labels?: string[] | undefined;
|
|
935
|
+
sourceUrl?: string | undefined;
|
|
936
|
+
}[] | undefined;
|
|
937
|
+
comments?: {
|
|
938
|
+
externalId: string;
|
|
939
|
+
on: {
|
|
940
|
+
kind: "page" | "task";
|
|
941
|
+
id: string;
|
|
942
|
+
};
|
|
943
|
+
parentId?: string | null | undefined;
|
|
944
|
+
authorId?: string | undefined;
|
|
945
|
+
bodyHtml: string;
|
|
946
|
+
createdAt: string;
|
|
947
|
+
updatedAt?: string | undefined;
|
|
948
|
+
}[] | undefined;
|
|
949
|
+
attachments?: {
|
|
950
|
+
externalId: string;
|
|
951
|
+
file: string;
|
|
952
|
+
name?: string | undefined;
|
|
953
|
+
mime?: string | undefined;
|
|
954
|
+
byteSize?: number | undefined;
|
|
955
|
+
authorId?: string | undefined;
|
|
956
|
+
createdAt?: string | undefined;
|
|
957
|
+
}[] | undefined;
|
|
958
|
+
}>;
|
|
959
|
+
export type ImportPackageManifest = z.infer<typeof ImportPackageManifest>;
|
|
960
|
+
/** Виды сущностей пакета — по ним же строится ключ идемпотентности. */
|
|
961
|
+
export declare const IMPORT_ENTITY_KINDS: readonly ["person", "space", "sprint", "page", "task", "comment", "attachment"];
|
|
962
|
+
export type ImportEntityKind = (typeof IMPORT_ENTITY_KINDS)[number];
|
|
963
|
+
/**
|
|
964
|
+
* Вид сущности → список в манифесте. Таблицей, а не склейкой `kind + "s"`:
|
|
965
|
+
* у `person` список называется `people`, и правило «плюс s» тут врёт.
|
|
966
|
+
*/
|
|
967
|
+
export declare const IMPORT_ENTITY_FIELDS: Record<ImportEntityKind, keyof ImportPackageManifest>;
|
|
968
|
+
/**
|
|
969
|
+
* КЛЮЧ ИДЕМПОТЕНТНОСТИ. Собирается ЗДЕСЬ и только здесь: ядро, коннектор и отчёт
|
|
970
|
+
* о прогоне обязаны считать его одинаково, иначе повторный прогон не узнает уже
|
|
971
|
+
* перенесённое и создаст второй экземпляр всего. Это тот же тройной ключ, что у
|
|
972
|
+
* `ImportedEntity` в спеке (`source`, `kind`, `externalId`).
|
|
973
|
+
*
|
|
974
|
+
* Разделитель `:` однозначен, потому что свободная часть ровно одна и она
|
|
975
|
+
* ПОСЛЕДНЯЯ: `source` ограничен `IMPORT_SOURCE_RE`, `kind` — фиксированный
|
|
976
|
+
* перечень, двоеточий в них не бывает. `externalId` может содержать что угодно
|
|
977
|
+
* (в Notion как раз двоеточия) — на хвосте это разбор не ломает.
|
|
978
|
+
*
|
|
979
|
+
* Ключ ПЕЧАТНЫЙ — это КОНТРАКТ, и держится он не здесь: сама функция всего лишь
|
|
980
|
+
* склейка и вход не проверяет. Печатность обеспечивают `IMPORT_SOURCE_RE` и
|
|
981
|
+
* запрет невидимых символов в `ImportExternalId` (`IMPORT_INVISIBLE_CHARS_RE` —
|
|
982
|
+
* управляющие, zero-width, bidi, разрывы строк); звать ключ на строках, не
|
|
983
|
+
* прошедших схему, значит остаться без этой гарантии. Нужна она затем, что ключ
|
|
984
|
+
* поедет в колонку БД, в лог прогона и на экран карантина: Postgres не хранит
|
|
985
|
+
* NUL в `text`, а два ключа, различающиеся невидимым символом, человек не
|
|
986
|
+
* различит и в глаза.
|
|
987
|
+
*/
|
|
988
|
+
export declare function importEntityKey(source: string, kind: ImportEntityKind, externalId: string): string;
|
|
989
|
+
/** Почему пакет не принят. */
|
|
990
|
+
export type ImportPackageRejection =
|
|
991
|
+
/** Версия формата новее, чем понимает это ядро, — читать нельзя, гадать тем более. */
|
|
992
|
+
{
|
|
993
|
+
reason: "unsupported_version";
|
|
994
|
+
version: number;
|
|
995
|
+
supported: readonly number[];
|
|
996
|
+
message: string;
|
|
997
|
+
}
|
|
998
|
+
/** Версии нет вовсе или она не число — это не пакет импорта. */
|
|
999
|
+
| {
|
|
1000
|
+
reason: "missing_version";
|
|
1001
|
+
message: string;
|
|
1002
|
+
}
|
|
1003
|
+
/** Версия наша, но форма не сходится — обычный отказ валидации. */
|
|
1004
|
+
| {
|
|
1005
|
+
reason: "invalid";
|
|
1006
|
+
issues: z.ZodIssue[];
|
|
1007
|
+
message: string;
|
|
1008
|
+
};
|
|
1009
|
+
export type ImportPackageParseResult = {
|
|
1010
|
+
ok: true;
|
|
1011
|
+
manifest: ImportPackageManifest;
|
|
1012
|
+
} | {
|
|
1013
|
+
ok: false;
|
|
1014
|
+
error: ImportPackageRejection;
|
|
1015
|
+
};
|
|
1016
|
+
export declare function isSupportedImportPackageVersion(version: number): boolean;
|
|
1017
|
+
/**
|
|
1018
|
+
* Сырой JSON манифеста → типизированный манифест или ВНЯТНЫЙ ОТКАЗ.
|
|
1019
|
+
*
|
|
1020
|
+
* Версия смотрится ДО схемы намеренно. Если бы `formatVersion` был просто
|
|
1021
|
+
* `z.literal(1)`, пакет завтрашнего коннектора отвалился бы наравне с битым
|
|
1022
|
+
* JSON — и человек, глядя на список ошибок полей, чинил бы не то.
|
|
1023
|
+
*
|
|
1024
|
+
* Не бросает: пакет приходит извне, и ядру нужен отказ, который можно положить в
|
|
1025
|
+
* карантин и показать человеку, а не исключение.
|
|
1026
|
+
*/
|
|
1027
|
+
export declare function parseImportPackageManifest(raw: unknown): ImportPackageParseResult;
|
|
1028
|
+
/** Изъян связности пакета: схема прошла, но ссылки внутри никуда не ведут. */
|
|
1029
|
+
export type ImportPackageProblem = {
|
|
1030
|
+
kind: ImportEntityKind;
|
|
1031
|
+
externalId: string;
|
|
1032
|
+
/** `duplicate_external_id` ломает идемпотентность, `dangling_ref` — целостность. */
|
|
1033
|
+
problem: "duplicate_external_id" | "dangling_ref";
|
|
1034
|
+
/** Какое поле указывает в пустоту (для `dangling_ref`). */
|
|
1035
|
+
field?: string;
|
|
1036
|
+
message: string;
|
|
1037
|
+
};
|
|
1038
|
+
/**
|
|
1039
|
+
* Проверка СВЯЗНОСТИ — то, что Zod не ловит по устройству: он проверяет каждую
|
|
1040
|
+
* сущность отдельно, а пакет ломается на связях между ними.
|
|
1041
|
+
*
|
|
1042
|
+
* Ловит две вещи, и обе про идемпотентность:
|
|
1043
|
+
* • повторный `externalId` внутри вида — ключ перестаёт быть ключом, и
|
|
1044
|
+
* повторный прогон будет находить то одну сущность, то другую;
|
|
1045
|
+
* • ссылку в никуда (родитель, автор, пространство, владелец, вложение) — ядру
|
|
1046
|
+
* нечего с ней делать, а молча уронить связь значит потерять кусок дерева и
|
|
1047
|
+
* узнать об этом от человека через неделю.
|
|
1048
|
+
*
|
|
1049
|
+
* Отдаёт СПИСОК, а не бросает: пакет с десятком висячих ссылок должен уехать в
|
|
1050
|
+
* карантин целиком и показаться человеку одним экраном. Что делать с проблемами
|
|
1051
|
+
* — решает ядро (MOVE-11).
|
|
1052
|
+
*/
|
|
1053
|
+
export declare function checkImportPackageRefs(manifest: ImportPackageManifest): ImportPackageProblem[];
|
|
1054
|
+
/**
|
|
1055
|
+
* Пути файлов, которые обязаны лежать в пакете. Ядру нужно ДО раскладки понять,
|
|
1056
|
+
* полон ли пакет: недостающий файл на середине прогона — это половина импорта и
|
|
1057
|
+
* ручная уборка.
|
|
1058
|
+
*/
|
|
1059
|
+
export declare function importPackageFilePaths(manifest: ImportPackageManifest): string[];
|