@kernhq/module-quire 0.13.1 → 0.15.0

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.
Files changed (123) hide show
  1. package/dist/contract/models.d.ts +394 -0
  2. package/dist/contract/models.d.ts.map +1 -1
  3. package/dist/contract/models.js +284 -0
  4. package/dist/contract/models.js.map +1 -1
  5. package/dist/contract/permissions.d.ts +14 -0
  6. package/dist/contract/permissions.d.ts.map +1 -1
  7. package/dist/contract/permissions.js +114 -0
  8. package/dist/contract/permissions.js.map +1 -1
  9. package/dist/contract/properties.d.ts +7 -7
  10. package/dist/contract/router.d.ts +1124 -96
  11. package/dist/contract/router.d.ts.map +1 -1
  12. package/dist/contract/router.js +343 -1
  13. package/dist/contract/router.js.map +1 -1
  14. package/dist/server/_impl.d.ts +983 -1168
  15. package/dist/server/_impl.d.ts.map +1 -1
  16. package/dist/server/_impl.js +308 -0
  17. package/dist/server/_impl.js.map +1 -1
  18. package/dist/server/export/html.d.ts +67 -0
  19. package/dist/server/export/html.d.ts.map +1 -0
  20. package/dist/server/export/html.js +206 -0
  21. package/dist/server/export/html.js.map +1 -0
  22. package/dist/server/export/markdown.d.ts +51 -0
  23. package/dist/server/export/markdown.d.ts.map +1 -0
  24. package/dist/server/export/markdown.js +351 -0
  25. package/dist/server/export/markdown.js.map +1 -0
  26. package/dist/server/export/pdf.d.ts +20 -0
  27. package/dist/server/export/pdf.d.ts.map +1 -0
  28. package/dist/server/export/pdf.js +91 -0
  29. package/dist/server/export/pdf.js.map +1 -0
  30. package/dist/server/export/zip.d.ts +31 -0
  31. package/dist/server/export/zip.d.ts.map +1 -0
  32. package/dist/server/export/zip.js +158 -0
  33. package/dist/server/export/zip.js.map +1 -0
  34. package/dist/server/import/csv.d.ts +77 -0
  35. package/dist/server/import/csv.d.ts.map +1 -0
  36. package/dist/server/import/csv.js +263 -0
  37. package/dist/server/import/csv.js.map +1 -0
  38. package/dist/server/import/html.d.ts +52 -0
  39. package/dist/server/import/html.d.ts.map +1 -0
  40. package/dist/server/import/html.js +472 -0
  41. package/dist/server/import/html.js.map +1 -0
  42. package/dist/server/import/markdown.d.ts +63 -0
  43. package/dist/server/import/markdown.d.ts.map +1 -0
  44. package/dist/server/import/markdown.js +692 -0
  45. package/dist/server/import/markdown.js.map +1 -0
  46. package/dist/server/import/plan.d.ts +70 -0
  47. package/dist/server/import/plan.d.ts.map +1 -0
  48. package/dist/server/import/plan.js +761 -0
  49. package/dist/server/import/plan.js.map +1 -0
  50. package/dist/server/import/ydoc.d.ts +35 -0
  51. package/dist/server/import/ydoc.d.ts.map +1 -0
  52. package/dist/server/import/ydoc.js +91 -0
  53. package/dist/server/import/ydoc.js.map +1 -0
  54. package/dist/server/import/zip.d.ts +63 -0
  55. package/dist/server/import/zip.d.ts.map +1 -0
  56. package/dist/server/import/zip.js +308 -0
  57. package/dist/server/import/zip.js.map +1 -0
  58. package/dist/server/index.d.ts.map +1 -1
  59. package/dist/server/index.js +3 -1
  60. package/dist/server/index.js.map +1 -1
  61. package/dist/server/render.d.ts +158 -0
  62. package/dist/server/render.d.ts.map +1 -1
  63. package/dist/server/render.js +240 -0
  64. package/dist/server/render.js.map +1 -1
  65. package/dist/server/schema.d.ts +704 -1
  66. package/dist/server/schema.d.ts.map +1 -1
  67. package/dist/server/schema.js +254 -1
  68. package/dist/server/schema.js.map +1 -1
  69. package/dist/server/services/databases.d.ts +10 -10
  70. package/dist/server/services/export.d.ts +176 -0
  71. package/dist/server/services/export.d.ts.map +1 -0
  72. package/dist/server/services/export.js +822 -0
  73. package/dist/server/services/export.js.map +1 -0
  74. package/dist/server/services/import.d.ts +109 -0
  75. package/dist/server/services/import.d.ts.map +1 -0
  76. package/dist/server/services/import.js +570 -0
  77. package/dist/server/services/import.js.map +1 -0
  78. package/dist/server/services/index.d.ts +32 -1
  79. package/dist/server/services/index.d.ts.map +1 -1
  80. package/dist/server/services/index.js +87 -4
  81. package/dist/server/services/index.js.map +1 -1
  82. package/dist/server/services/macros.d.ts +83 -0
  83. package/dist/server/services/macros.d.ts.map +1 -0
  84. package/dist/server/services/macros.js +488 -0
  85. package/dist/server/services/macros.js.map +1 -0
  86. package/dist/server/services/publications.d.ts +2 -1
  87. package/dist/server/services/publications.d.ts.map +1 -1
  88. package/dist/server/services/publications.js +43 -4
  89. package/dist/server/services/publications.js.map +1 -1
  90. package/dist/server/services/templates.d.ts +135 -0
  91. package/dist/server/services/templates.d.ts.map +1 -0
  92. package/dist/server/services/templates.js +897 -0
  93. package/dist/server/services/templates.js.map +1 -0
  94. package/dist/server/services/versions.d.ts +13 -1
  95. package/dist/server/services/versions.d.ts.map +1 -1
  96. package/dist/server/services/versions.js +3 -1
  97. package/dist/server/services/versions.js.map +1 -1
  98. package/migrations/0010_transfers.sql +154 -0
  99. package/migrations/0011_templates.sql +157 -0
  100. package/migrations/meta/_journal.json +14 -0
  101. package/package.json +5 -5
  102. package/src/client/components/ExportDialog.svelte +685 -0
  103. package/src/client/components/ImportDialog.svelte +702 -0
  104. package/src/client/components/ImportReport.svelte +310 -0
  105. package/src/client/components/NewSpaceDialog.svelte +77 -8
  106. package/src/client/components/PageEditor.svelte +80 -0
  107. package/src/client/components/PagePicker.svelte +264 -0
  108. package/src/client/components/SaveAsTemplateDialog.svelte +502 -0
  109. package/src/client/components/SidebarSpaces.svelte +112 -1
  110. package/src/client/components/TemplatePicker.svelte +437 -0
  111. package/src/client/i18n.ts +941 -0
  112. package/src/client/index.ts +51 -0
  113. package/src/client/mock.ts +592 -0
  114. package/src/client/module.ts +36 -0
  115. package/src/client/pages/PageView.svelte +86 -0
  116. package/src/client/pages/SpacePage.svelte +20 -4
  117. package/src/client/pages/TransfersPage.svelte +570 -0
  118. package/src/client/permissions.ts +11 -0
  119. package/src/client/query.ts +40 -0
  120. package/src/client/transfers.ts +142 -0
  121. package/src/contract/models.ts +343 -0
  122. package/src/contract/permissions.ts +117 -0
  123. package/src/contract/router.ts +375 -0
@@ -0,0 +1,570 @@
1
+ import { KernError, uuidv7 } from '@kernhq/kernel';
2
+ import { and, asc, desc, eq, inArray, isNull, lt } from 'drizzle-orm';
3
+ import { initialRank, rankBetween } from '../../client/rank.js';
4
+ import { MODULE_ID } from '../../contract/index.js';
5
+ import { planImport } from '../import/plan.js';
6
+ import { pageDocToYState } from '../import/ydoc.js';
7
+ import { readZip } from '../import/zip.js';
8
+ import { textFromPageDoc } from '../render.js';
9
+ import { importJobs, pages, pageVersions, spaces } from '../schema.js';
10
+ import { documentNameOf } from './pages.js';
11
+ /**
12
+ * How long a job may sit un-finished before it is given up on.
13
+ *
14
+ * The same hole as an export's, and worse here on both counts. `retryLimit: 0` means a worker
15
+ * killed mid-import is never re-tried, so the row is `running` from the moment the process dies —
16
+ * and decision 5 above means nothing ever deletes it either, so unlike an export it does not even
17
+ * age out of the list. A row that says "running" a month later is a screen telling somebody their
18
+ * pages are still on their way.
19
+ *
20
+ * Two hours is the job's whole budget with room to spare: `quireJobs` gives `import`
21
+ * `expireInSeconds: 3600` and one attempt. Nothing legitimate is still running after it, and a job
22
+ * that is has already been abandoned by the queue.
23
+ */
24
+ const ABANDONED_AFTER_MS = 2 * 60 * 60 * 1000;
25
+ /**
26
+ * What a job that lost its worker says.
27
+ *
28
+ * It leads with the thing somebody wants to know before anything else, which is the same thing the
29
+ * dialog says about every other import failure: the write is one transaction, so an import that did
30
+ * not finish did not half-finish.
31
+ */
32
+ const ABANDONED = 'This import stopped before it finished and was given up on after two hours. The worker running ' +
33
+ 'it went away — usually a restart, or an archive too big for the memory it had. Nothing was ' +
34
+ 'written: an import is one transaction, so the space is exactly as it was. Upload it again.';
35
+ /** Neither `done` nor `failed`, and old enough that nothing is still going to happen to it. */
36
+ const isAbandoned = (row) => (row.state === 'queued' || row.state === 'running') &&
37
+ row.createdAt.getTime() < Date.now() - ABANDONED_AFTER_MS;
38
+ /**
39
+ * The largest archive one job will read.
40
+ *
41
+ * The plan is built in memory before anything is written — see the note at the top — so the limit is
42
+ * about what one worker may hold rather than about what a person may upload. The zip reader has its
43
+ * own, stricter limit on what an archive *unpacks* to.
44
+ */
45
+ const MAX_ARCHIVE_BYTES = 256 * 1024 * 1024;
46
+ /** Rows written at a time, so a four-thousand-page import is not four thousand round trips. */
47
+ const BATCH = 200;
48
+ const countsOf = (value) => {
49
+ const raw = (value ?? {});
50
+ const n = (v) => (typeof v === 'number' && Number.isFinite(v) && v >= 0 ? Math.trunc(v) : 0);
51
+ return { total: n(raw.total), done: n(raw.done), skipped: n(raw.skipped), failed: n(raw.failed) };
52
+ };
53
+ /**
54
+ * The stored report, parsed rather than cast.
55
+ *
56
+ * The column is jsonb written by a previous release of this module, so a cast promises fields that
57
+ * may not be there. Anything unreadable is dropped rather than thrown: a report is a record of what
58
+ * happened, and failing to *show* one would be a worse answer than showing what is legible.
59
+ */
60
+ function reportOf(value) {
61
+ if (!Array.isArray(value))
62
+ return [];
63
+ const out = [];
64
+ for (const raw of value) {
65
+ if (typeof raw !== 'object' || raw === null)
66
+ continue;
67
+ const entry = raw;
68
+ if (typeof entry.path !== 'string')
69
+ continue;
70
+ if (entry.outcome !== 'imported' && entry.outcome !== 'skipped' && entry.outcome !== 'failed')
71
+ continue;
72
+ out.push({
73
+ path: entry.path,
74
+ outcome: entry.outcome,
75
+ pageId: typeof entry.pageId === 'string' ? entry.pageId : null,
76
+ reason: typeof entry.reason === 'string' ? entry.reason : null,
77
+ });
78
+ }
79
+ return out;
80
+ }
81
+ export function toImportJob(row) {
82
+ return {
83
+ id: row.id,
84
+ workspaceId: row.workspaceId,
85
+ requestedBy: row.requestedBy,
86
+ source: row.source,
87
+ targetId: row.targetId,
88
+ sourceFileId: row.sourceFileId,
89
+ state: row.state,
90
+ error: row.error,
91
+ counts: countsOf(row.counts),
92
+ report: reportOf(row.report),
93
+ createdAt: row.createdAt.toISOString(),
94
+ finishedAt: row.finishedAt?.toISOString() ?? null,
95
+ };
96
+ }
97
+ /** Counters over report rows, which is what makes `total` equal to the number of rows in it. */
98
+ export function countReport(report) {
99
+ return {
100
+ total: report.length,
101
+ done: report.filter((entry) => entry.outcome === 'imported').length,
102
+ skipped: report.filter((entry) => entry.outcome === 'skipped').length,
103
+ failed: report.filter((entry) => entry.outcome === 'failed').length,
104
+ };
105
+ }
106
+ export function quireImport(kernel, access, databases) {
107
+ /** The row, or `notFound`. Fenced to the person who asked for it, exactly as an export is. */
108
+ async function own(tx, workspaceId, jobId, principal) {
109
+ const [row] = await tx
110
+ .select()
111
+ .from(importJobs)
112
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.id, jobId)))
113
+ .limit(1);
114
+ if (!row)
115
+ throw KernError.notFound('Import');
116
+ /*
117
+ * NOT_FOUND rather than FORBIDDEN, the same as an export and for a related reason: a report names
118
+ * every file in somebody's archive, which is a listing of what they had somewhere else. "That one
119
+ * is not yours" would confirm that a colleague imported something, and when.
120
+ */
121
+ const mine = principal.instanceAdmin || principal.kind === 'service' || row.requestedBy === principal.userId;
122
+ if (!mine)
123
+ throw KernError.notFound('Import');
124
+ return row;
125
+ }
126
+ /**
127
+ * Tell the one person whose job this is that it moved — and nobody else.
128
+ *
129
+ * `kernel.realtime.change` publishes to the **workspace** channel, which the gateway joins every
130
+ * socket to at `hello` with no per-message filter — right for a page, wrong for a transfer. `own()`
131
+ * answers NOT_FOUND rather than FORBIDDEN so that an id belonging to somebody else's import
132
+ * confirms nothing, and then the broadcast told the whole workspace that a job with that id
133
+ * changed, and when. `services/export.ts` carries the long version of this note.
134
+ */
135
+ async function announce(workspaceId, requestedBy, jobId) {
136
+ await kernel.realtime
137
+ .toUser(requestedBy, {
138
+ t: 'change',
139
+ workspaceId: workspaceId,
140
+ change: { module: 'quire', entity: 'import', id: jobId, op: 'updated' },
141
+ })
142
+ .catch(() => undefined);
143
+ }
144
+ async function patch(workspaceId, requestedBy, jobId, values) {
145
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
146
+ await tx
147
+ .update(importJobs)
148
+ .set(values)
149
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.id, jobId)));
150
+ });
151
+ await announce(workspaceId, requestedBy, jobId);
152
+ }
153
+ /**
154
+ * The uploaded archive's bytes, checked against the tenant that is asking for them.
155
+ *
156
+ * The workspace is **compared here rather than trusted**. `core.files.get` answers a service
157
+ * principal without a membership check — that is what makes it callable from a module at all — and
158
+ * `fileId` came from the request, so this comparison is the only thing stopping one workspace
159
+ * naming another's upload and having Quire unpack it into a space of its own.
160
+ */
161
+ async function archiveBytes(workspaceId, fileId) {
162
+ const file = await kernel
163
+ .call('core.files.get', { id: fileId })
164
+ .catch(() => null);
165
+ if (!file || file.workspaceId !== workspaceId)
166
+ throw KernError.notFound('File');
167
+ if (file.status !== 'ready' || !file.key)
168
+ throw KernError.badRequest('That upload has not finished. Wait for it to appear, then import it.');
169
+ if (file.size > MAX_ARCHIVE_BYTES)
170
+ throw KernError.badRequest(`That archive is ${Math.round(file.size / 1_048_576)} MB, and one import may carry ` +
171
+ `${MAX_ARCHIVE_BYTES / 1_048_576} MB. Export it in smaller pieces — one space at a time.`);
172
+ const object = await kernel.storage.get(file.key);
173
+ const chunks = [];
174
+ for await (const chunk of object.body)
175
+ chunks.push(Buffer.from(chunk));
176
+ return Buffer.concat(chunks);
177
+ }
178
+ /**
179
+ * May this person still write here?
180
+ *
181
+ * Both keys, and both at space scope. `quire.page.import` is the one that says the bulk shape is
182
+ * available at all; `quire.page.create` is "a space you may write to", which is a different
183
+ * question a workspace can answer differently — a space closed for editing during a freeze is
184
+ * closed to an import as well, and reading only the import key would walk straight past that.
185
+ */
186
+ async function requireWritable(principal, workspaceId, spaceId) {
187
+ await access.requireSpace(principal, 'quire.page.import', workspaceId, spaceId);
188
+ await access.requireSpace(principal, 'quire.page.create', workspaceId, spaceId);
189
+ }
190
+ /**
191
+ * Write the plan.
192
+ *
193
+ * One transaction, in an order that is not arbitrary: pages before databases, because a database
194
+ * hangs on a page; properties before rows, because a row's cells are keyed by the properties'
195
+ * *actual* keys; and documents last, because a document is the only part that leaves the database.
196
+ */
197
+ async function write(tx, principal, workspaceId, spaceId, plan) {
198
+ /*
199
+ * Imported pages land after whatever the space already has, so an import never reorders the tree
200
+ * somebody built. The rank is chained from the last existing root rather than minted per page:
201
+ * `rankBetween(last, null)` is only strictly increasing if each call sees the previous answer.
202
+ */
203
+ const [lastRoot] = await tx
204
+ .select({ position: pages.position })
205
+ .from(pages)
206
+ .where(and(eq(pages.workspaceId, workspaceId), eq(pages.spaceId, spaceId), isNull(pages.parentId), isNull(pages.deletedAt)))
207
+ .orderBy(desc(pages.position))
208
+ .limit(1);
209
+ const tail = new Map([[null, lastRoot?.position ?? null]]);
210
+ const nextPosition = (parentId) => {
211
+ const previous = tail.get(parentId) ?? null;
212
+ const position = previous === null ? initialRank() : rankBetween(previous, null);
213
+ tail.set(parentId, position);
214
+ return position;
215
+ };
216
+ const now = new Date();
217
+ const rows = plan.pages.map((page) => ({
218
+ id: page.id,
219
+ workspaceId,
220
+ spaceId,
221
+ parentId: page.parentId,
222
+ position: nextPosition(page.parentId),
223
+ kind: 'page',
224
+ title: page.title.slice(0, 500),
225
+ text: textFromPageDoc(page.doc),
226
+ createdBy: principal.userId,
227
+ updatedBy: principal.userId,
228
+ createdAt: now,
229
+ updatedAt: now,
230
+ }));
231
+ for (let at = 0; at < rows.length; at += BATCH)
232
+ await tx.insert(pages).values(rows.slice(at, at + BATCH));
233
+ const documents = plan.pages
234
+ .filter((page) => (page.doc.content ?? []).length > 0)
235
+ .map((page) => ({ pageId: page.id, doc: page.doc }));
236
+ for (const planned of plan.databases) {
237
+ const database = await databases.create(tx, principal, workspaceId, {
238
+ spaceId,
239
+ pageId: planned.hostPageId,
240
+ name: planned.name.slice(0, 200),
241
+ inline: false,
242
+ });
243
+ /*
244
+ * `databases.create` seeds a text column called `Name`, which is right for a database somebody
245
+ * creates by hand and wrong for one that arrives with its own columns: the CSV's first column
246
+ * is the row *title*, so the seeded one would sit beside the title, empty, in every view.
247
+ */
248
+ for (const seeded of database.properties)
249
+ await databases.removeProperty(tx, workspaceId, seeded.id);
250
+ const keys = [];
251
+ for (const column of planned.columns) {
252
+ const property = await databases.addProperty(tx, workspaceId, database.id, {
253
+ name: column.name.slice(0, 120),
254
+ type: column.type,
255
+ config: column.config,
256
+ });
257
+ keys.push(property.key);
258
+ }
259
+ const rowRows = planned.rows.map((row) => {
260
+ const props = {};
261
+ // Zipped against the keys the database actually minted — never against a key guessed here.
262
+ row.values.forEach((value, index) => {
263
+ const key = keys[index];
264
+ if (key !== undefined && value !== null)
265
+ props[key] = value;
266
+ });
267
+ return {
268
+ id: row.id,
269
+ workspaceId,
270
+ spaceId,
271
+ parentId: planned.hostPageId,
272
+ position: nextPosition(planned.hostPageId),
273
+ kind: 'page',
274
+ title: row.title.slice(0, 500),
275
+ text: textFromPageDoc(row.doc),
276
+ databaseId: database.id,
277
+ props,
278
+ createdBy: principal.userId,
279
+ updatedBy: principal.userId,
280
+ createdAt: now,
281
+ updatedAt: now,
282
+ };
283
+ });
284
+ for (let at = 0; at < rowRows.length; at += BATCH)
285
+ await tx.insert(pages).values(rowRows.slice(at, at + BATCH));
286
+ for (const row of planned.rows)
287
+ if ((row.doc.content ?? []).length > 0)
288
+ documents.push({ pageId: row.id, doc: row.doc });
289
+ }
290
+ /*
291
+ * The document, then the version — in that order and both inside the transaction.
292
+ *
293
+ * `versions.capture` is not used here on purpose: it reads the state back from the collab service
294
+ * and returns *null* when it cannot, so an import would quietly produce pages with no history at
295
+ * all. The bytes are already in hand, so the row is written from them directly, and a collab
296
+ * service that will not take the document fails the import instead.
297
+ */
298
+ for (const { pageId, doc } of documents) {
299
+ const state = pageDocToYState(doc);
300
+ await kernel.call('collab.document.replace', {
301
+ name: documentNameOf({ workspaceId, id: pageId }),
302
+ state: state.toString('base64'),
303
+ });
304
+ await tx.insert(pageVersions).values({
305
+ id: uuidv7(),
306
+ workspaceId,
307
+ pageId,
308
+ kind: 'import',
309
+ label: null,
310
+ state,
311
+ // `Y.encodeSnapshot` needs a document somebody has edited; a version with no snapshot simply
312
+ // cannot be diffed against another, which is true of the first version of every page.
313
+ snapshot: null,
314
+ text: textFromPageDoc(doc),
315
+ size: state.length,
316
+ authorId: principal.userId ?? null,
317
+ });
318
+ }
319
+ }
320
+ /**
321
+ * Put the imported pages in the workspace search index.
322
+ *
323
+ * Best effort, after the transaction: a page that is not findable until somebody edits it is a real
324
+ * defect — three hundred pages nobody can search for is most of what an import is *for* — and a
325
+ * search service that is briefly down must not undo an import that has already committed.
326
+ *
327
+ * The rule about *which* pages are indexed lives in `server/index.ts`'s `searchDocument`, and is
328
+ * repeated rather than imported because importing it would make this file and the module definition
329
+ * depend on each other. There is exactly one rule to keep in step: only a space whose visibility is
330
+ * `open` is indexed at all, because `SearchDocument.acl` cannot yet express who may read a
331
+ * restricted space.
332
+ */
333
+ async function indexPages(workspaceId, spaceId, plan) {
334
+ try {
335
+ const found = await kernel.database.withWorkspace(workspaceId, async (tx) => {
336
+ const [space] = await tx
337
+ .select({ key: spaces.key, visibility: spaces.visibility })
338
+ .from(spaces)
339
+ .where(and(eq(spaces.workspaceId, workspaceId), eq(spaces.id, spaceId)))
340
+ .limit(1);
341
+ if (space?.visibility !== 'open')
342
+ return null;
343
+ const rows = await tx
344
+ .select({
345
+ id: pages.id,
346
+ title: pages.title,
347
+ text: pages.text,
348
+ kind: pages.kind,
349
+ updatedAt: pages.updatedAt,
350
+ })
351
+ .from(pages)
352
+ .where(and(eq(pages.workspaceId, workspaceId), eq(pages.spaceId, spaceId)))
353
+ .orderBy(asc(pages.id));
354
+ return { space, rows };
355
+ });
356
+ if (!found)
357
+ return;
358
+ const imported = new Set([
359
+ ...plan.pages.map((page) => page.id),
360
+ ...plan.databases.flatMap((database) => database.rows.map((row) => row.id)),
361
+ ]);
362
+ const documents = found.rows
363
+ .filter((row) => imported.has(row.id))
364
+ .map((row) => ({
365
+ workspaceId: workspaceId,
366
+ object: { module: MODULE_ID, type: 'page', id: row.id },
367
+ title: row.title || 'Untitled',
368
+ body: row.text,
369
+ url: `/quire/${found.space.key}/${row.id}`,
370
+ icon: 'file-text',
371
+ acl: null,
372
+ updatedAt: row.updatedAt.toISOString(),
373
+ attributes: { spaceId, kind: row.kind },
374
+ }));
375
+ for (let at = 0; at < documents.length; at += BATCH)
376
+ await kernel.call('core.search.index', { documents: documents.slice(at, at + BATCH) });
377
+ }
378
+ catch (err) {
379
+ kernel.log.warn({ err: String(err), spaceId }, 'quire: imported pages could not be indexed');
380
+ }
381
+ }
382
+ return {
383
+ toImportJob,
384
+ /**
385
+ * Record the request and hand it to a worker.
386
+ *
387
+ * Nothing is read here beyond the file's own header: a zip of thousands of files is unbounded
388
+ * work, so the request answers with a row somebody can watch. What *is* done here is every check
389
+ * that can be made cheaply and would otherwise fail a minute later in a worker nobody is
390
+ * watching — the space exists, the caller may write to it, the file is this workspace's and has
391
+ * finished uploading.
392
+ */
393
+ async start(tx, principal, workspaceId, input) {
394
+ const userId = principal.userId;
395
+ if (!userId)
396
+ throw new KernError('FORBIDDEN', 'An import belongs to a person, and this caller is a service');
397
+ await access.spaceRow(tx, workspaceId, input.spaceId);
398
+ await requireWritable(principal, workspaceId, input.spaceId);
399
+ const file = await kernel
400
+ .call('core.files.get', { id: input.fileId })
401
+ .catch(() => null);
402
+ if (!file || file.workspaceId !== workspaceId)
403
+ throw KernError.notFound('File');
404
+ if (file.status !== 'ready')
405
+ throw KernError.badRequest('That upload has not finished. Wait for it to appear, then import it.');
406
+ const [row] = await tx
407
+ .insert(importJobs)
408
+ .values({
409
+ id: uuidv7(),
410
+ workspaceId,
411
+ requestedBy: userId,
412
+ source: input.source,
413
+ targetId: input.spaceId,
414
+ sourceFileId: input.fileId,
415
+ state: 'queued',
416
+ counts: { total: 0, done: 0, skipped: 0, failed: 0 },
417
+ report: [],
418
+ })
419
+ .returning();
420
+ if (!row)
421
+ throw new KernError('INTERNAL', 'The import could not be recorded');
422
+ return row;
423
+ },
424
+ /**
425
+ * One job, and the one place a *watched* job can give up on itself.
426
+ *
427
+ * The dialog polls this and never the list, so `reap` — which runs beside the list — would never
428
+ * reach the row somebody is actually looking at. One row rather than a workspace-wide update,
429
+ * because this is on a path that runs every second and a half while a job is moving.
430
+ */
431
+ async get(tx, workspaceId, jobId, principal) {
432
+ const row = await own(tx, workspaceId, jobId, principal);
433
+ if (!isAbandoned(row))
434
+ return row;
435
+ const [failed] = await tx
436
+ .update(importJobs)
437
+ .set({ state: 'failed', error: ABANDONED, finishedAt: new Date() })
438
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.id, jobId)))
439
+ .returning();
440
+ return failed ?? row;
441
+ },
442
+ /**
443
+ * Fail this workspace's abandoned jobs, so a dead one stops reading as work in progress.
444
+ *
445
+ * This is not the sweep decision 5 above argues against, and the difference is the whole point:
446
+ * a sweep would *delete* the record of a dangerous operation, and this writes down that it did
447
+ * not happen. The row stays, with a reason on it.
448
+ *
449
+ * Called from `imports.start` and `imports.list`, which is the same opportunistic shape
450
+ * `exports.sweep` explains: every tenant table is under FORCE row-level security keyed on
451
+ * `app.workspace_id`, and Quire keeps no `workspaces` table, so a cron job has nothing to
452
+ * enumerate. The limit is the same one and is worth stating: a workspace nobody comes back to
453
+ * keeps its lie until somebody looks. The transfers list polls every two seconds while anything
454
+ * is running, so in practice the screen being lied to is the one that ends it.
455
+ */
456
+ async reap(tx, workspaceId) {
457
+ const abandoned = await tx
458
+ .update(importJobs)
459
+ .set({ state: 'failed', error: ABANDONED, finishedAt: new Date() })
460
+ .where(and(eq(importJobs.workspaceId, workspaceId), inArray(importJobs.state, ['queued', 'running']), lt(importJobs.createdAt, new Date(Date.now() - ABANDONED_AFTER_MS))))
461
+ .returning({ id: importJobs.id });
462
+ return abandoned.length;
463
+ },
464
+ /** This person's own imports, newest first. Somebody else's are not listed and cannot be. */
465
+ list(tx, workspaceId, principal, limit) {
466
+ const userId = principal.userId;
467
+ if (!userId)
468
+ return Promise.resolve([]);
469
+ return tx
470
+ .select()
471
+ .from(importJobs)
472
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.requestedBy, userId)))
473
+ .orderBy(desc(importJobs.createdAt))
474
+ .limit(limit);
475
+ },
476
+ /**
477
+ * Mark a job failed without having run it.
478
+ *
479
+ * One caller: the router, when the queue refuses the job it has just recorded. Without it the row
480
+ * sits `queued` for ever and reads as work in progress, which is the worst of the three states to
481
+ * be wrong about — worse here than for an export, because somebody watching it is waiting to find
482
+ * out whether their pages arrived.
483
+ */
484
+ async fail(tx, workspaceId, jobId, reason) {
485
+ await tx
486
+ .update(importJobs)
487
+ .set({ state: 'failed', error: reason.slice(0, 2000), finishedAt: new Date() })
488
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.id, jobId)));
489
+ },
490
+ /**
491
+ * The job: read the archive, build the plan, write it once.
492
+ *
493
+ * Everything before the transaction holds nothing — fetching a few hundred megabytes from storage
494
+ * and parsing thousands of files must not do so with a connection open. Everything inside it is
495
+ * one act, so the space either gains the whole import or nothing at all.
496
+ */
497
+ async run(workspaceId, jobId) {
498
+ /*
499
+ * The claim is **one conditional UPDATE**, so exactly one attempt can ever hold this row.
500
+ *
501
+ * `select` then `update` is a lost update under READ COMMITTED — two workers both read
502
+ * `queued`, both write `running`, and both run — and here that means every page in the archive
503
+ * written twice. `where state = 'queued'` closes it: Postgres re-evaluates the predicate after
504
+ * the other transaction commits, so the loser matches no row. `done` and `failed` are excluded
505
+ * by the same clause, which is what makes a retry of a finished job a no-op. See
506
+ * `services/export.ts` for what this costs — a row whose worker died stays `running` until
507
+ * `reap` gives up on it, rather than being re-run by the next retry.
508
+ */
509
+ const job = await kernel.database.withWorkspace(workspaceId, async (tx) => {
510
+ const [row] = await tx
511
+ .update(importJobs)
512
+ .set({ state: 'running', error: null })
513
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.id, jobId), eq(importJobs.state, 'queued')))
514
+ .returning();
515
+ return row ?? null;
516
+ });
517
+ if (!job)
518
+ return;
519
+ await announce(workspaceId, job.requestedBy, jobId);
520
+ try {
521
+ /*
522
+ * The import runs as the person who asked for it, which is the whole permission model here.
523
+ * A job has no principal of its own, so it fetches theirs — and when it cannot, it fails
524
+ * rather than falling back to something more permissive.
525
+ */
526
+ const principal = await kernel
527
+ .call('core.users.principal', { userId: job.requestedBy })
528
+ .catch(() => null);
529
+ if (!principal)
530
+ throw new KernError('INTERNAL', 'The person who asked for this import could not be identified');
531
+ // Asked again, now, at the moment of writing — see the note at the top of the file.
532
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
533
+ await access.spaceRow(tx, workspaceId, job.targetId);
534
+ await requireWritable(principal, workspaceId, job.targetId);
535
+ });
536
+ const bytes = await archiveBytes(workspaceId, job.sourceFileId);
537
+ const plan = planImport(readZip(bytes), job.source);
538
+ const counts = countReport(plan.report);
539
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
540
+ await write(tx, principal, workspaceId, job.targetId, plan);
541
+ await tx
542
+ .update(importJobs)
543
+ .set({ state: 'done', counts, report: plan.report, finishedAt: new Date() })
544
+ .where(and(eq(importJobs.workspaceId, workspaceId), eq(importJobs.id, jobId)));
545
+ }, { userId: principal.userId });
546
+ await indexPages(workspaceId, job.targetId, plan);
547
+ await announce(workspaceId, job.requestedBy, jobId);
548
+ /*
549
+ * The *space* still goes to the whole workspace, and that is the line between the two.
550
+ * Pages arriving in a shared space is news for everyone whose sidebar shows that space — it
551
+ * is the same change a `pages.create` announces. Which job put them there is not.
552
+ */
553
+ await kernel.realtime
554
+ .change(workspaceId, { module: 'quire', entity: 'space', id: job.targetId, op: 'updated' })
555
+ .catch(() => undefined);
556
+ kernel.log.info({ jobId, workspaceId, source: job.source, ...counts }, 'quire: import finished');
557
+ }
558
+ catch (err) {
559
+ const message = err instanceof Error ? err.message : String(err);
560
+ await patch(workspaceId, job.requestedBy, jobId, {
561
+ state: 'failed',
562
+ error: message.slice(0, 2000),
563
+ finishedAt: new Date(),
564
+ });
565
+ kernel.log.warn({ err: message, jobId, workspaceId }, 'quire: import failed');
566
+ }
567
+ },
568
+ };
569
+ }
570
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/server/services/import.ts"],"names":[],"mappings":"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"}