@kernhq/module-quire 0.14.0 → 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.
- package/dist/contract/models.d.ts +209 -0
- package/dist/contract/models.d.ts.map +1 -1
- package/dist/contract/models.js +151 -0
- package/dist/contract/models.js.map +1 -1
- package/dist/contract/permissions.d.ts.map +1 -1
- package/dist/contract/permissions.js +32 -0
- package/dist/contract/permissions.js.map +1 -1
- package/dist/contract/properties.d.ts +3 -3
- package/dist/contract/router.d.ts +509 -8
- package/dist/contract/router.d.ts.map +1 -1
- package/dist/contract/router.js +211 -1
- package/dist/contract/router.js.map +1 -1
- package/dist/server/_impl.d.ts +573 -1269
- package/dist/server/_impl.d.ts.map +1 -1
- package/dist/server/_impl.js +180 -0
- package/dist/server/_impl.js.map +1 -1
- package/dist/server/export/markdown.d.ts.map +1 -1
- package/dist/server/export/markdown.js +40 -1
- package/dist/server/export/markdown.js.map +1 -1
- package/dist/server/render.d.ts +158 -0
- package/dist/server/render.d.ts.map +1 -1
- package/dist/server/render.js +240 -0
- package/dist/server/render.js.map +1 -1
- package/dist/server/schema.d.ts +260 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +108 -1
- package/dist/server/schema.js.map +1 -1
- package/dist/server/services/databases.d.ts +5 -5
- package/dist/server/services/index.d.ts +6 -0
- package/dist/server/services/index.d.ts.map +1 -1
- package/dist/server/services/index.js +27 -3
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/macros.d.ts +83 -0
- package/dist/server/services/macros.d.ts.map +1 -0
- package/dist/server/services/macros.js +488 -0
- package/dist/server/services/macros.js.map +1 -0
- package/dist/server/services/publications.d.ts +2 -1
- package/dist/server/services/publications.d.ts.map +1 -1
- package/dist/server/services/publications.js +43 -4
- package/dist/server/services/publications.js.map +1 -1
- package/dist/server/services/templates.d.ts +135 -0
- package/dist/server/services/templates.d.ts.map +1 -0
- package/dist/server/services/templates.js +897 -0
- package/dist/server/services/templates.js.map +1 -0
- package/dist/server/services/versions.d.ts +12 -0
- package/dist/server/services/versions.d.ts.map +1 -1
- package/dist/server/services/versions.js +3 -1
- package/dist/server/services/versions.js.map +1 -1
- package/migrations/0011_templates.sql +157 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/src/client/components/NewSpaceDialog.svelte +77 -8
- package/src/client/components/PageEditor.svelte +80 -0
- package/src/client/components/PagePicker.svelte +264 -0
- package/src/client/components/SaveAsTemplateDialog.svelte +502 -0
- package/src/client/components/SidebarSpaces.svelte +33 -1
- package/src/client/components/TemplatePicker.svelte +437 -0
- package/src/client/i18n.ts +327 -0
- package/src/client/index.ts +19 -0
- package/src/client/mock.ts +274 -0
- package/src/client/pages/PageView.svelte +50 -0
- package/src/client/pages/SpacePage.svelte +20 -4
- package/src/client/query.ts +17 -0
- package/src/contract/models.ts +191 -0
- package/src/contract/permissions.ts +33 -0
- package/src/contract/router.ts +228 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/services/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/services/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a macro is allowed to say, to the person actually reading.
|
|
3
|
+
*
|
|
4
|
+
* Five of the eight macros draw pages that are not in the document — `pageChildren`,
|
|
5
|
+
* `excerptInclude`, `includePage`, `recentlyUpdated` and `contributors`. Everything about them that
|
|
6
|
+
* matters is in this file, because this is the only place that decides *which* pages, and the
|
|
7
|
+
* decision is the feature's whole risk: a children macro that forgets its filter still compiles,
|
|
8
|
+
* still renders, and looks right to the author, because the author can read everything on it.
|
|
9
|
+
*
|
|
10
|
+
* The rule, in three parts, and `macros.int.test.ts` holds all three:
|
|
11
|
+
*
|
|
12
|
+
* 1. **The renderer never reads a database.** `renderPageDoc` draws a reading macro only from a
|
|
13
|
+
* `MacroResolver` it was handed. With none — which is what every caller written before this
|
|
14
|
+
* feature passes, the exporters and the publish-time render included — a reading macro draws
|
|
15
|
+
* its frame and nothing else. Fail-closed is the default rather than a branch to remember.
|
|
16
|
+
* 2. **A signed-in reader sees exactly what they could open.** Every page named here is asked
|
|
17
|
+
* about with `quire.page.view` at *page* scope, so a DENY bound to one page removes it.
|
|
18
|
+
* 3. **A published site has no reader, so the publication is the audience.** The set of publicly
|
|
19
|
+
* reachable pages — which `publications.walk` already computes, root first and pruned — is the
|
|
20
|
+
* whole of what exists. Anything outside it is not drawn as a title with a dead link; it is
|
|
21
|
+
* not drawn.
|
|
22
|
+
*
|
|
23
|
+
* Two structural choices follow from that and are worth stating rather than leaving to be inferred.
|
|
24
|
+
*
|
|
25
|
+
* **Resolution is a pass, not a per-node lookup.** `macrosIn` collects the reading macros in a
|
|
26
|
+
* document and de-duplicates them by their attributes, this file answers them against one audience,
|
|
27
|
+
* and `renderPageDoc` reads the answers out of a map. The alternative — resolving inside the
|
|
28
|
+
* renderer — is a database round trip per macro in render order, on a request that is otherwise two
|
|
29
|
+
* queries, and it would make the renderer async for every caller that has no macros at all.
|
|
30
|
+
*
|
|
31
|
+
* **A prune is not a filter.** Where a page is not visible, its descendants go with it. Removing a
|
|
32
|
+
* page from a children list and then listing its children anyway would name every page under it to
|
|
33
|
+
* whoever guessed there was something there, which is exactly the shape `publications.ts`'s walk
|
|
34
|
+
* exists to avoid — and the reason that walk prunes is the reason this one does.
|
|
35
|
+
*/
|
|
36
|
+
import type { Principal } from '@kernhq/contracts';
|
|
37
|
+
import type { Kernel, Tx } from '@kernhq/kernel';
|
|
38
|
+
import { type PageDoc } from '@kernhq/ui/editor/page-doc';
|
|
39
|
+
import { type MacroResolver } from '../render.js';
|
|
40
|
+
import type { QuireAccess } from './access.js';
|
|
41
|
+
/**
|
|
42
|
+
* Who is being drawn for.
|
|
43
|
+
*
|
|
44
|
+
* A discriminated union with exactly two members, and there is deliberately no third: a caller
|
|
45
|
+
* cannot express "resolve this for nobody in particular", because that is the shape that leaks. A
|
|
46
|
+
* caller with no audience passes no resolver at all, and the renderer draws empty frames.
|
|
47
|
+
*/
|
|
48
|
+
export type MacroAudience = {
|
|
49
|
+
kind: 'reader';
|
|
50
|
+
principal: Principal;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The publicly reachable pages, as `publications.walk` computed them for *this* publication.
|
|
54
|
+
* A set rather than a publication id, so this file cannot disagree with the walk about what
|
|
55
|
+
* "public" means — there is only one definition of it and it is not here.
|
|
56
|
+
*/
|
|
57
|
+
| {
|
|
58
|
+
kind: 'publication';
|
|
59
|
+
pageIds: ReadonlySet<string>;
|
|
60
|
+
};
|
|
61
|
+
export interface MacroContext {
|
|
62
|
+
/** The page the document belongs to. `null` for a render with no page — a preview, an import. */
|
|
63
|
+
pageId: string | null;
|
|
64
|
+
/** Where to send a reader for a page id, if this render has addresses at all. */
|
|
65
|
+
pageHref?: (pageId: string) => string | null;
|
|
66
|
+
/** For the dates a list macro draws. `principal.locale` for a reader; the site's for a publication. */
|
|
67
|
+
locale?: string | null;
|
|
68
|
+
}
|
|
69
|
+
export declare function quireMacros(kernel: Kernel, access: QuireAccess): {
|
|
70
|
+
/**
|
|
71
|
+
* Answer every reading macro in a document, for one audience, in one pass.
|
|
72
|
+
*
|
|
73
|
+
* Returns the synchronous lookup `renderPageDoc` takes. A macro this could not answer — a page
|
|
74
|
+
* that was deleted, a page the audience may not see, a lookup that failed — is simply absent
|
|
75
|
+
* from the map, and an absent answer is the empty frame. There is no path through this function
|
|
76
|
+
* that returns a page the audience failed the gate on, and no way to express one: `visible` is
|
|
77
|
+
* the only thing that builds a `MacroPageRef`'s input, and every caller of `refOf` has just
|
|
78
|
+
* awaited it.
|
|
79
|
+
*/
|
|
80
|
+
resolve(tx: Tx, workspaceId: string, doc: PageDoc | null | undefined, audience: MacroAudience, ctx: MacroContext): Promise<MacroResolver>;
|
|
81
|
+
};
|
|
82
|
+
export type QuireMacros = ReturnType<typeof quireMacros>;
|
|
83
|
+
//# sourceMappingURL=macros.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macros.d.ts","sourceRoot":"","sources":["../../../src/server/services/macros.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAGL,KAAK,OAAO,EAEb,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAGL,KAAK,aAAa,EAKnB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE;AAC1C;;;;GAIG;GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,CAAA;AAEzD,MAAM,WAAW,YAAY;IAC3B,iGAAiG;IACjG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IAC5C,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AA6GD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;IAgV3D;;;;;;;;;OASG;gBAEG,EAAE,eACO,MAAM,OACd,OAAO,GAAG,IAAI,GAAG,SAAS,YACrB,aAAa,OAClB,YAAY,GAChB,OAAO,CAAC,aAAa,CAAC;EA0H5B;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAA"}
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { PAGE_MACRO_MAX_DEPTH, PAGE_MACRO_MAX_ROWS, } from '@kernhq/ui/editor/page-doc';
|
|
2
|
+
import { and, asc, desc, eq, inArray, isNull, sql } from 'drizzle-orm';
|
|
3
|
+
import { pageDocFromBase64, pageDocFromState } from '../document.js';
|
|
4
|
+
import { macroKey, macrosIn, renderPageDoc, textFromPageDoc, } from '../render.js';
|
|
5
|
+
import { pages, pageVersions } from '../schema.js';
|
|
6
|
+
import { documentNameOf } from './pages.js';
|
|
7
|
+
/**
|
|
8
|
+
* How much one macro may ask for.
|
|
9
|
+
*
|
|
10
|
+
* The document decides how much work a request does, so these are enforced here as well as in the
|
|
11
|
+
* editor: a `depth` of 900 in a page somebody pasted in is a recursive query somebody pasted in.
|
|
12
|
+
* The editor's own clamps are the same constants, out of `page-doc.ts` — the one file in
|
|
13
|
+
* `@kernhq/ui` that imports nothing and is safe to load in a backend process.
|
|
14
|
+
*/
|
|
15
|
+
const MAX_DEPTH = PAGE_MACRO_MAX_DEPTH;
|
|
16
|
+
const MAX_ROWS = PAGE_MACRO_MAX_ROWS;
|
|
17
|
+
/**
|
|
18
|
+
* How many rows a "recently updated" macro reads before filtering.
|
|
19
|
+
*
|
|
20
|
+
* It has to over-fetch, because the filter runs after the sort: the newest twenty-five pages in a
|
|
21
|
+
* space are not the newest twenty-five *this reader may see*. Four times the ceiling is the
|
|
22
|
+
* compromise — a reader denied three quarters of a space still fills the list, and the query stays
|
|
23
|
+
* one bounded index scan rather than a walk of the whole space. A reader denied more than that gets
|
|
24
|
+
* a short list, which is honest: the alternative is paging until the list is full, which is a
|
|
25
|
+
* query whose cost is set by how much somebody is not allowed to see.
|
|
26
|
+
*/
|
|
27
|
+
const RECENT_CANDIDATES = MAX_ROWS * 4;
|
|
28
|
+
/** How long an excerpt drawn beside a title may be, before it stops being a hint and becomes prose. */
|
|
29
|
+
const EXCERPT_CHARS = 200;
|
|
30
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
31
|
+
/**
|
|
32
|
+
* A page id out of a document, or null.
|
|
33
|
+
*
|
|
34
|
+
* The same narrowing `macroPageId` applies in the editor, restated for the same reason the status
|
|
35
|
+
* tones are: this file must not load the extension. A value that is not a uuid reaches a
|
|
36
|
+
* `= $1::uuid` and turns a macro nobody can see into a 500 on the page that holds it.
|
|
37
|
+
*/
|
|
38
|
+
const pageIdAttr = (value) => typeof value === 'string' && UUID_RE.test(value) ? value.toLowerCase() : null;
|
|
39
|
+
/** A whole number inside a range, or the fallback — never the string that was in the document. */
|
|
40
|
+
function countAttr(value, min, max, fallback) {
|
|
41
|
+
const n = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);
|
|
42
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < min || n > max)
|
|
43
|
+
return fallback;
|
|
44
|
+
return n;
|
|
45
|
+
}
|
|
46
|
+
const flagAttr = (value) => value === true || value === 'true';
|
|
47
|
+
const CHILDREN_SORTS = ['position', 'title', 'updated'];
|
|
48
|
+
const sortAttr = (value) => CHILDREN_SORTS.includes(String(value)) ? value : 'position';
|
|
49
|
+
const RECENT_SCOPES = ['space', 'subtree'];
|
|
50
|
+
const scopeAttr = (value) => RECENT_SCOPES.includes(String(value)) ? value : 'space';
|
|
51
|
+
/** Trim flattened prose to something that sits under a title without becoming the page. */
|
|
52
|
+
function excerptOf(text) {
|
|
53
|
+
const clean = text.replace(/\s+/g, ' ').trim();
|
|
54
|
+
if (!clean)
|
|
55
|
+
return null;
|
|
56
|
+
if (clean.length <= EXCERPT_CHARS)
|
|
57
|
+
return clean;
|
|
58
|
+
// Cut at a space so the hint does not end mid-word; fall back to a hard cut for a long token.
|
|
59
|
+
const cut = clean.slice(0, EXCERPT_CHARS);
|
|
60
|
+
const space = cut.lastIndexOf(' ');
|
|
61
|
+
return `${(space > EXCERPT_CHARS / 2 ? cut.slice(0, space) : cut).trimEnd()}…`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The first `excerpt` node in a document, flattened to text.
|
|
65
|
+
*
|
|
66
|
+
* One page, one excerpt: a second is ignored, because "the excerpt" would otherwise mean whichever
|
|
67
|
+
* one the walk happened to reach first, and that is a page whose macro changes meaning when
|
|
68
|
+
* somebody edits a paragraph far away from it. Depth-bounded for the reason `templates.ts` gives —
|
|
69
|
+
* `doc` is whatever was written to a JSONB column, and an adversarially nested document walked
|
|
70
|
+
* recursively is a stack overflow, which in Node is a process rather than an exception.
|
|
71
|
+
*/
|
|
72
|
+
function markedExcerpt(doc, depth = 0) {
|
|
73
|
+
if (!doc || depth > MAX_DEPTH * 4)
|
|
74
|
+
return null;
|
|
75
|
+
for (const node of (doc.content ?? [])) {
|
|
76
|
+
if (node.type === 'excerpt') {
|
|
77
|
+
const text = textFromPageDoc({ type: 'doc', content: node.content ?? [] });
|
|
78
|
+
const clean = text.replace(/\s+/g, ' ').trim();
|
|
79
|
+
if (clean)
|
|
80
|
+
return clean;
|
|
81
|
+
}
|
|
82
|
+
const nested = markedExcerpt({ type: 'doc', content: node.content ?? [] }, depth + 1);
|
|
83
|
+
if (nested)
|
|
84
|
+
return nested;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
export function quireMacros(kernel, access) {
|
|
89
|
+
/**
|
|
90
|
+
* A page's prose, as a document — the live one first.
|
|
91
|
+
*
|
|
92
|
+
* Same order as `templates.ts`'s: what the person looking at the page can see, then the newest
|
|
93
|
+
* stored version for a page the collab service has forgotten or never held. An include that drew
|
|
94
|
+
* last week's published copy while the source page says something else is the kind of wrong that
|
|
95
|
+
* takes a day to notice.
|
|
96
|
+
*/
|
|
97
|
+
async function docOfPage(tx, workspaceId, pageId) {
|
|
98
|
+
const live = await kernel
|
|
99
|
+
.call('collab.document.state', {
|
|
100
|
+
name: documentNameOf({ workspaceId, id: pageId }),
|
|
101
|
+
})
|
|
102
|
+
.catch(() => null);
|
|
103
|
+
const fromLive = pageDocFromBase64(live?.state ?? null);
|
|
104
|
+
if (fromLive)
|
|
105
|
+
return fromLive;
|
|
106
|
+
const [version] = await tx
|
|
107
|
+
.select({ state: pageVersions.state })
|
|
108
|
+
.from(pageVersions)
|
|
109
|
+
.where(and(eq(pageVersions.workspaceId, workspaceId), eq(pageVersions.pageId, pageId)))
|
|
110
|
+
.orderBy(desc(pageVersions.createdAt))
|
|
111
|
+
.limit(1);
|
|
112
|
+
return version ? pageDocFromState(version.state) : null;
|
|
113
|
+
}
|
|
114
|
+
const selection = {
|
|
115
|
+
id: pages.id,
|
|
116
|
+
parentId: pages.parentId,
|
|
117
|
+
spaceId: pages.spaceId,
|
|
118
|
+
title: pages.title,
|
|
119
|
+
icon: pages.icon,
|
|
120
|
+
text: pages.text,
|
|
121
|
+
updatedAt: pages.updatedAt,
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* The pages a macro may consider at all, before anybody is asked about them.
|
|
125
|
+
*
|
|
126
|
+
* Trashed, archived, non-`page` and database rows are excluded here rather than in each macro,
|
|
127
|
+
* because every one of them wants the same answer and the one that forgot would be the leak.
|
|
128
|
+
* A database row is a page, which is exactly why it has to be named: a children macro on a page
|
|
129
|
+
* that happens to hold a database would otherwise draw four thousand rows as a table of contents.
|
|
130
|
+
*/
|
|
131
|
+
const drawable = (workspaceId) => and(eq(pages.workspaceId, workspaceId), eq(pages.kind, 'page'), isNull(pages.databaseId), isNull(pages.deletedAt), isNull(pages.archivedAt));
|
|
132
|
+
/**
|
|
133
|
+
* May this audience be shown this page?
|
|
134
|
+
*
|
|
135
|
+
* The one question this file exists to ask, and the only place either audience is interpreted.
|
|
136
|
+
* A publication is a set membership — no principal exists to ask, and the walk that built the set
|
|
137
|
+
* already applied every rule about what public means. A reader is a page-scoped permission check
|
|
138
|
+
* whose parent chain carries every ancestor, so a restriction set on a section applies to
|
|
139
|
+
* everything under it without having been copied down.
|
|
140
|
+
*/
|
|
141
|
+
async function visible(workspaceId, audience, page) {
|
|
142
|
+
if (audience.kind === 'publication')
|
|
143
|
+
return audience.pageIds.has(page.id);
|
|
144
|
+
return access.canPage(audience.principal, 'quire.page.view', workspaceId, {
|
|
145
|
+
pageId: page.id,
|
|
146
|
+
spaceId: page.spaceId,
|
|
147
|
+
ancestorIds: page.ancestorIds,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/** A visible page as the renderer wants it. Nothing here is reached for a page that failed the gate. */
|
|
151
|
+
function refOf(page, ctx, opts) {
|
|
152
|
+
return {
|
|
153
|
+
id: page.id,
|
|
154
|
+
title: page.title,
|
|
155
|
+
icon: page.icon,
|
|
156
|
+
href: ctx.pageHref?.(page.id) ?? null,
|
|
157
|
+
updated: opts.updated ? formatDate(page.updatedAt, ctx.locale) : null,
|
|
158
|
+
excerpt: opts.excerpt ? excerptOf(page.text) : null,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A date the reader can read, formatted here because the renderer does no date arithmetic.
|
|
163
|
+
*
|
|
164
|
+
* Through `Intl`, so a Persian workspace gets Persian digits and an Arabic one an Arabic calendar
|
|
165
|
+
* without this file knowing anything about either — the same bargain `templates.ts` makes for a
|
|
166
|
+
* template's `{{date}}`.
|
|
167
|
+
*/
|
|
168
|
+
function formatDate(value, locale) {
|
|
169
|
+
if (!(value instanceof Date) || Number.isNaN(value.getTime()))
|
|
170
|
+
return null;
|
|
171
|
+
try {
|
|
172
|
+
return new Intl.DateTimeFormat(locale || 'en', { dateStyle: 'medium' }).format(value);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(value);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** The chain above one page, so its children can be asked about without a query per level. */
|
|
179
|
+
async function scopeChain(tx, workspaceId, pageId) {
|
|
180
|
+
const scope = await access.scopeOf(tx, workspaceId, pageId).catch(() => null);
|
|
181
|
+
return scope ? scope.ancestorIds : [];
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The visible pages under one page, level by level, pruning as it goes.
|
|
185
|
+
*
|
|
186
|
+
* Breadth-first and one query per level rather than one recursive query, because the permission
|
|
187
|
+
* question is asked *between* levels: a page that fails the gate is never expanded, so its
|
|
188
|
+
* children are not read from the database at all. That is what makes this a prune rather than a
|
|
189
|
+
* filter, and it is also why the loop is bounded by `depth` — the number of queries is the depth
|
|
190
|
+
* the macro asked for, which is at most `MAX_DEPTH`.
|
|
191
|
+
*/
|
|
192
|
+
async function childrenTree(tx, workspaceId, audience, ctx, root, depth, sort, showExcerpt) {
|
|
193
|
+
const order = sort === 'title' ? asc(pages.title) : sort === 'updated' ? desc(pages.updatedAt) : asc(pages.position);
|
|
194
|
+
const top = [];
|
|
195
|
+
// The frontier carries each surviving page's own ancestor chain, which its children inherit.
|
|
196
|
+
let frontier = [
|
|
197
|
+
{
|
|
198
|
+
ref: { id: root.id, title: '', icon: null, href: null, updated: null, excerpt: null },
|
|
199
|
+
id: root.id,
|
|
200
|
+
ancestorIds: root.ancestorIds,
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
let drawn = 0;
|
|
204
|
+
for (let level = 0; level < depth && frontier.length > 0 && drawn < MAX_ROWS; level++) {
|
|
205
|
+
const parentIds = frontier.map((entry) => entry.id);
|
|
206
|
+
const rows = await tx
|
|
207
|
+
.select(selection)
|
|
208
|
+
.from(pages)
|
|
209
|
+
.where(and(drawable(workspaceId), inArray(pages.parentId, parentIds)))
|
|
210
|
+
.orderBy(order)
|
|
211
|
+
.limit(MAX_ROWS * 4);
|
|
212
|
+
const next = [];
|
|
213
|
+
for (const entry of frontier) {
|
|
214
|
+
for (const row of rows) {
|
|
215
|
+
if (row.parentId !== entry.id)
|
|
216
|
+
continue;
|
|
217
|
+
if (drawn >= MAX_ROWS)
|
|
218
|
+
break;
|
|
219
|
+
const scoped = { ...row, ancestorIds: [entry.id, ...entry.ancestorIds] };
|
|
220
|
+
if (!(await visible(workspaceId, audience, scoped)))
|
|
221
|
+
continue;
|
|
222
|
+
const ref = refOf(row, ctx, { excerpt: showExcerpt, updated: false });
|
|
223
|
+
drawn++;
|
|
224
|
+
if (level === 0)
|
|
225
|
+
top.push(ref);
|
|
226
|
+
else {
|
|
227
|
+
entry.ref.children ??= [];
|
|
228
|
+
entry.ref.children.push(ref);
|
|
229
|
+
}
|
|
230
|
+
next.push({ ref, id: row.id, ancestorIds: scoped.ancestorIds });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
frontier = next;
|
|
234
|
+
}
|
|
235
|
+
return top;
|
|
236
|
+
}
|
|
237
|
+
/** The most recently changed pages an audience may see, in a space or under one page. */
|
|
238
|
+
async function recentlyUpdated(tx, workspaceId, audience, ctx, home, scope, limit) {
|
|
239
|
+
/*
|
|
240
|
+
* A subtree is resolved with one recursive query rather than level by level, because unlike the
|
|
241
|
+
* children macro this list is ordered by time across the whole subtree — there is no level to
|
|
242
|
+
* stop at. The prune is then applied to the result: a page whose ancestor chain contains
|
|
243
|
+
* something the audience cannot see is dropped along with anything under it, which the chain
|
|
244
|
+
* carried on each row makes a check rather than a second walk.
|
|
245
|
+
*/
|
|
246
|
+
let rows;
|
|
247
|
+
let chains = new Map();
|
|
248
|
+
if (scope === 'subtree') {
|
|
249
|
+
const res = await tx.execute(sql `
|
|
250
|
+
with recursive tree as (
|
|
251
|
+
select p.id, p.parent_id, p.space_id, p.title, p.icon, p.text, p.updated_at,
|
|
252
|
+
array[]::uuid[] as path, 0 as depth
|
|
253
|
+
from mod_quire.pages p
|
|
254
|
+
where p.workspace_id = ${workspaceId}::uuid
|
|
255
|
+
and p.parent_id = ${home.id}::uuid
|
|
256
|
+
and p.kind = 'page' and p.database_id is null
|
|
257
|
+
and p.deleted_at is null and p.archived_at is null
|
|
258
|
+
union all
|
|
259
|
+
select c.id, c.parent_id, c.space_id, c.title, c.icon, c.text, c.updated_at,
|
|
260
|
+
tree.path || tree.id, tree.depth + 1
|
|
261
|
+
from mod_quire.pages c
|
|
262
|
+
join tree on c.parent_id = tree.id
|
|
263
|
+
where c.workspace_id = ${workspaceId}::uuid
|
|
264
|
+
and c.kind = 'page' and c.database_id is null
|
|
265
|
+
and c.deleted_at is null and c.archived_at is null
|
|
266
|
+
and tree.depth < ${MAX_DEPTH}
|
|
267
|
+
) cycle id set looped using cyclepath
|
|
268
|
+
select id, parent_id, space_id, title, icon, text, updated_at, path
|
|
269
|
+
from tree
|
|
270
|
+
order by updated_at desc
|
|
271
|
+
limit ${RECENT_CANDIDATES}
|
|
272
|
+
`);
|
|
273
|
+
rows = res.rows.map((row) => ({
|
|
274
|
+
id: row.id,
|
|
275
|
+
parentId: row.parent_id,
|
|
276
|
+
spaceId: row.space_id,
|
|
277
|
+
title: row.title,
|
|
278
|
+
icon: row.icon,
|
|
279
|
+
text: row.text,
|
|
280
|
+
updatedAt: row.updated_at instanceof Date ? row.updated_at : new Date(row.updated_at),
|
|
281
|
+
}));
|
|
282
|
+
chains = new Map(res.rows.map((row) => [row.id, [...(row.path ?? [])].reverse().concat(home.id, home.ancestorIds)]));
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
rows = await tx
|
|
286
|
+
.select(selection)
|
|
287
|
+
.from(pages)
|
|
288
|
+
.where(and(drawable(workspaceId), eq(pages.spaceId, home.spaceId)))
|
|
289
|
+
.orderBy(desc(pages.updatedAt))
|
|
290
|
+
.limit(RECENT_CANDIDATES);
|
|
291
|
+
}
|
|
292
|
+
const out = [];
|
|
293
|
+
for (const row of rows) {
|
|
294
|
+
if (out.length >= limit)
|
|
295
|
+
break;
|
|
296
|
+
/*
|
|
297
|
+
* A space-scoped list has no walk to carry a chain, so each surviving row is asked about with
|
|
298
|
+
* its own ancestors. `scopeOf` is one recursive query per row, which is why the candidate set
|
|
299
|
+
* is bounded — and why the subtree branch, which already knows every chain, does not pay it.
|
|
300
|
+
*/
|
|
301
|
+
const ancestorIds = chains.get(row.id) ?? (await scopeChain(tx, workspaceId, row.id));
|
|
302
|
+
if (!(await visible(workspaceId, audience, { ...row, ancestorIds })))
|
|
303
|
+
continue;
|
|
304
|
+
out.push(refOf(row, ctx, { excerpt: false, updated: true }));
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Who has written on this page.
|
|
310
|
+
*
|
|
311
|
+
* **A reader only.** Every other public response in this module is scrubbed of anything
|
|
312
|
+
* identifying a person — see the note at the top of `publications.ts` — and a page's authors are
|
|
313
|
+
* the customer's staff rather than the page's content. Somebody who wants a byline on a published
|
|
314
|
+
* page writes one. That decision is made by having no publication branch at all rather than by
|
|
315
|
+
* returning an empty list from one, so a later edit cannot turn it back on by accident.
|
|
316
|
+
*/
|
|
317
|
+
async function contributors(tx, workspaceId, audience, home, limit) {
|
|
318
|
+
if (audience.kind !== 'reader')
|
|
319
|
+
return null;
|
|
320
|
+
if (!(await visible(workspaceId, audience, home)))
|
|
321
|
+
return null;
|
|
322
|
+
const [row] = await tx
|
|
323
|
+
.select({ createdBy: pages.createdBy, updatedBy: pages.updatedBy })
|
|
324
|
+
.from(pages)
|
|
325
|
+
.where(and(eq(pages.workspaceId, workspaceId), eq(pages.id, home.id)))
|
|
326
|
+
.limit(1);
|
|
327
|
+
// Newest first, so a long-lived page credits whoever has been working on it lately.
|
|
328
|
+
const versions = await tx
|
|
329
|
+
.select({ authorId: pageVersions.authorId })
|
|
330
|
+
.from(pageVersions)
|
|
331
|
+
.where(and(eq(pageVersions.workspaceId, workspaceId), eq(pageVersions.pageId, home.id)))
|
|
332
|
+
.orderBy(desc(pageVersions.createdAt))
|
|
333
|
+
.limit(RECENT_CANDIDATES);
|
|
334
|
+
const ordered = [];
|
|
335
|
+
const seen = new Set();
|
|
336
|
+
for (const id of [row?.updatedBy, ...versions.map((v) => v.authorId), row?.createdBy]) {
|
|
337
|
+
if (!id || seen.has(id))
|
|
338
|
+
continue;
|
|
339
|
+
seen.add(id);
|
|
340
|
+
ordered.push(id);
|
|
341
|
+
if (ordered.length >= limit)
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
if (ordered.length === 0)
|
|
345
|
+
return null;
|
|
346
|
+
/*
|
|
347
|
+
* Names from core, which is the only thing that has them. A lookup that fails draws no
|
|
348
|
+
* contributors rather than a list of ids: an id is not a name, and printing one on somebody's
|
|
349
|
+
* page is both useless and a string a reader can try somewhere else.
|
|
350
|
+
*/
|
|
351
|
+
const people = await kernel
|
|
352
|
+
.call('core.users.list', { ids: ordered })
|
|
353
|
+
.catch(() => null);
|
|
354
|
+
if (!people)
|
|
355
|
+
return null;
|
|
356
|
+
const byId = new Map(people.map((person) => [person.id, person.name]));
|
|
357
|
+
const named = ordered
|
|
358
|
+
.map((id) => byId.get(id))
|
|
359
|
+
.filter((name) => typeof name === 'string' && name.trim().length > 0)
|
|
360
|
+
.map((name) => ({ name }));
|
|
361
|
+
return named.length > 0 ? { kind: 'people', people: named } : null;
|
|
362
|
+
}
|
|
363
|
+
/** The page a macro points at, with its chain, or null when it is not drawable at all. */
|
|
364
|
+
async function targetPage(tx, workspaceId, pageId) {
|
|
365
|
+
const [row] = await tx
|
|
366
|
+
.select(selection)
|
|
367
|
+
.from(pages)
|
|
368
|
+
.where(and(drawable(workspaceId), eq(pages.id, pageId)))
|
|
369
|
+
.limit(1);
|
|
370
|
+
if (!row)
|
|
371
|
+
return null;
|
|
372
|
+
return { ...row, ancestorIds: await scopeChain(tx, workspaceId, row.id) };
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
/**
|
|
376
|
+
* Answer every reading macro in a document, for one audience, in one pass.
|
|
377
|
+
*
|
|
378
|
+
* Returns the synchronous lookup `renderPageDoc` takes. A macro this could not answer — a page
|
|
379
|
+
* that was deleted, a page the audience may not see, a lookup that failed — is simply absent
|
|
380
|
+
* from the map, and an absent answer is the empty frame. There is no path through this function
|
|
381
|
+
* that returns a page the audience failed the gate on, and no way to express one: `visible` is
|
|
382
|
+
* the only thing that builds a `MacroPageRef`'s input, and every caller of `refOf` has just
|
|
383
|
+
* awaited it.
|
|
384
|
+
*/
|
|
385
|
+
async resolve(tx, workspaceId, doc, audience, ctx) {
|
|
386
|
+
const nodes = macrosIn(doc);
|
|
387
|
+
if (nodes.length === 0)
|
|
388
|
+
return () => null;
|
|
389
|
+
/*
|
|
390
|
+
* The page the document is on, which four of the five macros need — as the default target,
|
|
391
|
+
* as the space a recent list is drawn from, and as the page a byline is about. Without one
|
|
392
|
+
* (a preview, an import) only a macro carrying an explicit page id can resolve, and the rest
|
|
393
|
+
* draw empty frames.
|
|
394
|
+
*/
|
|
395
|
+
const home = ctx.pageId ? await targetPage(tx, workspaceId, ctx.pageId) : null;
|
|
396
|
+
const answers = new Map();
|
|
397
|
+
for (const node of nodes) {
|
|
398
|
+
const content = await resolveOne(tx, workspaceId, audience, ctx, home, node);
|
|
399
|
+
if (content)
|
|
400
|
+
answers.set(macroKey(node), content);
|
|
401
|
+
}
|
|
402
|
+
return (node) => answers.get(macroKey(node)) ?? null;
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
async function resolveOne(tx, workspaceId, audience, ctx, home, node) {
|
|
406
|
+
const attrs = node.attrs ?? {};
|
|
407
|
+
/*
|
|
408
|
+
* `pageId` null means "the page this macro is on" — what almost every use of a children macro
|
|
409
|
+
* wants, and what keeps it working after the page is copied into a template or duplicated.
|
|
410
|
+
*/
|
|
411
|
+
const named = pageIdAttr(attrs.pageId);
|
|
412
|
+
switch (node.type) {
|
|
413
|
+
case 'pageChildren': {
|
|
414
|
+
const root = named ? await targetPage(tx, workspaceId, named) : home;
|
|
415
|
+
if (!root)
|
|
416
|
+
return null;
|
|
417
|
+
/*
|
|
418
|
+
* The root of a children list is asked about too. Pointing a macro at a page the reader may
|
|
419
|
+
* not see must not list that page's children — the parent is the permission boundary its
|
|
420
|
+
* children inherit, so skipping this check would make the macro a way around it.
|
|
421
|
+
*/
|
|
422
|
+
if (!(await visible(workspaceId, audience, root)))
|
|
423
|
+
return null;
|
|
424
|
+
const pages_ = await childrenTree(tx, workspaceId, audience, ctx, root, countAttr(attrs.depth, 1, MAX_DEPTH, 1), sortAttr(attrs.sort), flagAttr(attrs.showExcerpt));
|
|
425
|
+
return { kind: 'pages', pages: pages_ };
|
|
426
|
+
}
|
|
427
|
+
case 'recentlyUpdated': {
|
|
428
|
+
const anchor = named ? await targetPage(tx, workspaceId, named) : home;
|
|
429
|
+
if (!anchor)
|
|
430
|
+
return null;
|
|
431
|
+
if (!(await visible(workspaceId, audience, anchor)))
|
|
432
|
+
return null;
|
|
433
|
+
const rows = await recentlyUpdated(tx, workspaceId, audience, ctx, anchor, scopeAttr(attrs.scope), countAttr(attrs.limit, 1, MAX_ROWS, 10));
|
|
434
|
+
return { kind: 'pages', pages: rows };
|
|
435
|
+
}
|
|
436
|
+
case 'excerptInclude': {
|
|
437
|
+
const target = named ? await targetPage(tx, workspaceId, named) : null;
|
|
438
|
+
if (!target)
|
|
439
|
+
return null;
|
|
440
|
+
if (!(await visible(workspaceId, audience, target)))
|
|
441
|
+
return null;
|
|
442
|
+
/*
|
|
443
|
+
* The marked excerpt where the source page has one, its opening prose where it does not —
|
|
444
|
+
* plain text either way. Lifting markup out of one document into another is how a half-open
|
|
445
|
+
* tag becomes the reader's problem on a page they did not write.
|
|
446
|
+
*/
|
|
447
|
+
const marked = markedExcerpt(await docOfPage(tx, workspaceId, target.id));
|
|
448
|
+
const text = marked ?? excerptOf(target.text);
|
|
449
|
+
if (!text)
|
|
450
|
+
return null;
|
|
451
|
+
const ref = refOf(target, ctx, { excerpt: false, updated: false });
|
|
452
|
+
return { kind: 'page', page: { ...ref, excerpt: text }, html: '' };
|
|
453
|
+
}
|
|
454
|
+
case 'includePage': {
|
|
455
|
+
const target = named ? await targetPage(tx, workspaceId, named) : null;
|
|
456
|
+
if (!target)
|
|
457
|
+
return null;
|
|
458
|
+
if (!(await visible(workspaceId, audience, target)))
|
|
459
|
+
return null;
|
|
460
|
+
const doc = await docOfPage(tx, workspaceId, target.id);
|
|
461
|
+
if (!doc)
|
|
462
|
+
return null;
|
|
463
|
+
/*
|
|
464
|
+
* **The included page is rendered with no resolver of its own**, so its macros draw empty
|
|
465
|
+
* frames. Two reasons, and the first is sufficient: a page that includes a page that
|
|
466
|
+
* includes the first is a cycle, and a cycle here is a request that never returns. The
|
|
467
|
+
* second is that a macro one level down would be resolved against an audience that reached
|
|
468
|
+
* it through two pages' permissions, which is a question nobody asked and this file would
|
|
469
|
+
* have to invent an answer to. One level of transclusion, stated rather than discovered.
|
|
470
|
+
*
|
|
471
|
+
* The HTML this produces is the only string the renderer emits unescaped, and this is the
|
|
472
|
+
* only place that produces it — by calling the same `renderPageDoc` that escapes everything
|
|
473
|
+
* else. See the note on `MacroContent`.
|
|
474
|
+
*/
|
|
475
|
+
const html = renderPageDoc(doc, { pageHref: ctx.pageHref });
|
|
476
|
+
return { kind: 'page', page: refOf(target, ctx, { excerpt: false, updated: false }), html };
|
|
477
|
+
}
|
|
478
|
+
case 'contributors': {
|
|
479
|
+
if (!home)
|
|
480
|
+
return null;
|
|
481
|
+
return contributors(tx, workspaceId, audience, home, countAttr(attrs.limit, 1, MAX_ROWS, 10));
|
|
482
|
+
}
|
|
483
|
+
default:
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=macros.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macros.js","sourceRoot":"","sources":["../../../src/server/services/macros.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { type Kernel, type Tx } from '@kernhq/kernel';
|
|
|
3
3
|
import type { Publication } from '../../contract/index.js';
|
|
4
4
|
import { publications } from '../schema.js';
|
|
5
5
|
import type { QuireAccess } from './access.js';
|
|
6
|
+
import type { QuireMacros } from './macros.js';
|
|
6
7
|
/**
|
|
7
8
|
* A public URL names its workspace by slug or by id; every query needs the id.
|
|
8
9
|
*
|
|
@@ -74,7 +75,7 @@ export declare function publicHtml(html: string, basePath: string, resolve: {
|
|
|
74
75
|
}): string;
|
|
75
76
|
/** A plain-text window around the first match, for a search result. */
|
|
76
77
|
export declare function snippetAround(text: string, query: string): string;
|
|
77
|
-
export declare function quirePublications(kernel: Kernel, access: QuireAccess, versions: QuireVersions): {
|
|
78
|
+
export declare function quirePublications(kernel: Kernel, access: QuireAccess, versions: QuireVersions, macroServices: QuireMacros): {
|
|
78
79
|
/**
|
|
79
80
|
* The anonymous path's transaction: the workspace set, and nothing writable.
|
|
80
81
|
*
|