@opencxh/domain 1.234.0 → 1.236.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/entities/artifact/blocks.d.ts +8 -149
- package/dist/entities/artifact/markdown.d.ts +2 -6
- package/dist/entities/company/types.d.ts +2 -0
- package/dist/entities/contact/types.d.ts +8 -0
- package/dist/entities/document-template/index.d.ts +3 -0
- package/dist/entities/document-template/locale.d.ts +38 -0
- package/dist/entities/document-template/tokens.d.ts +27 -0
- package/dist/entities/document-template/types.d.ts +87 -0
- package/dist/entities/kb/types.d.ts +9 -0
- package/dist/entities/organization/types.d.ts +8 -0
- package/dist/entities/planning/capacity.d.ts +45 -2
- package/dist/index.cjs +21 -14
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1837 -1475
- package/dist/platform/document-blocks.d.ts +240 -0
- package/dist/platform/document-blocks.test.d.ts +1 -0
- package/dist/platform/document-markdown.d.ts +6 -0
- package/dist/platform/document-markdown.test.d.ts +1 -0
- package/dist/platform/document-parse.d.ts +2 -0
- package/dist/platform/document-text.d.ts +14 -0
- package/dist/platform/document-text.test.d.ts +1 -0
- package/package.json +1 -1
- /package/dist/entities/{artifact/blocks.test.d.ts → document-template/locale.test.d.ts} +0 -0
- /package/dist/entities/{artifact/markdown.test.d.ts → document-template/tokens.test.d.ts} +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The blocks a document is built from — the one vocabulary for every document in this platform.
|
|
3
|
+
*
|
|
4
|
+
* An artifact, a knowledge base article, a quote, a work order: one list of these. The name says
|
|
5
|
+
* document rather than artifact because the assistant was only the first writer — see
|
|
6
|
+
* `plans/DOCUMENTS-2026-09-14.md`. `entities/artifact/blocks.ts` keeps the old names as aliases.
|
|
7
|
+
*
|
|
8
|
+
* This is a **document** vocabulary, not the row vocabulary of the timeline
|
|
9
|
+
* (`../entities/activity/blocks.ts`). Those two look alike and deliberately are not: a timeline row sits
|
|
10
|
+
* among dozens of others and may therefore hold only ten blocks, has no paragraphs or headings,
|
|
11
|
+
* and its text nodes are *paths* into an activity. An artifact is a document — it has headings,
|
|
12
|
+
* paragraphs, a table and a KPI row, and its text is just text.
|
|
13
|
+
*
|
|
14
|
+
* What is carried over is that file's *discipline*, because it proved itself:
|
|
15
|
+
*
|
|
16
|
+
* - an optional `block_id`, so a block can be pointed at without rewriting the whole document;
|
|
17
|
+
* - {@link normalizeDocumentBlocks} **prunes instead of refusing** — one bad block must not make a
|
|
18
|
+
* whole document disappear — and reports *every* drop, because silent pruning makes "why is my
|
|
19
|
+
* table missing?" unanswerable;
|
|
20
|
+
* - unknown block types are skipped, so a newer writer can coexist with an older renderer;
|
|
21
|
+
* - semantic tones only, no free colours, so dark mode keeps working.
|
|
22
|
+
*
|
|
23
|
+
* **The inert boundary.** An artifact is stored data, not a program, and that is a property of the
|
|
24
|
+
* *model* rather than the outcome of a filter afterwards. Block texts are **markdown, never
|
|
25
|
+
* HTML**: the renderer hands them to ui-kit's `RichText` with `as="markdown"`, and that branch
|
|
26
|
+
* (react-markdown + remark-gfm) emits no raw HTML, so a `<script>` in the text reaches the screen
|
|
27
|
+
* as literal characters. Hence no tag stripping here — that would break "a < b" and win nothing.
|
|
28
|
+
* What *is* stripped is the only thing markdown itself makes dangerous: a link to a scheme outside
|
|
29
|
+
* {@link ALLOWED_LINK_SCHEMES}, and inline images. See {@link sanitizeInline}.
|
|
30
|
+
*/
|
|
31
|
+
/** Semantic colour. No free colours, so dark mode and the tokens keep working. */
|
|
32
|
+
export type DocumentTone = "info" | "success" | "warning" | "destructive";
|
|
33
|
+
/**
|
|
34
|
+
* One line in a list.
|
|
35
|
+
*
|
|
36
|
+
* `lead` exists because a findings list puts its point up front in bold ("**Yealink outages
|
|
37
|
+
* dominate.** 14 of the 184 calls..."). Without a field of its own the writer has to invent
|
|
38
|
+
* markdown asterisks in a place where the formatting is supposed to be fixed — and then the text
|
|
39
|
+
* shows whether someone forgot.
|
|
40
|
+
*/
|
|
41
|
+
export interface DocumentListItem {
|
|
42
|
+
lead?: string;
|
|
43
|
+
text: string;
|
|
44
|
+
}
|
|
45
|
+
export interface DocumentTableColumn {
|
|
46
|
+
label: string;
|
|
47
|
+
/** Numbers right. Left by default. */
|
|
48
|
+
align?: "left" | "right";
|
|
49
|
+
}
|
|
50
|
+
export interface DocumentKpiItem {
|
|
51
|
+
/** Already formatted by the writer ("1u 12m", "184", "-12%"). */
|
|
52
|
+
value: string;
|
|
53
|
+
label: string;
|
|
54
|
+
/** Only set when this number stands out; a tint on every tile is wallpaper. */
|
|
55
|
+
tone?: DocumentTone;
|
|
56
|
+
}
|
|
57
|
+
/** One line of a {@link DocumentBlock} letterhead: `{ label: "Nummer", value: "2026-0412" }`. */
|
|
58
|
+
export interface DocumentLetterheadField {
|
|
59
|
+
/** Machine key, so a template can address one field without matching on its label. */
|
|
60
|
+
key: string;
|
|
61
|
+
label?: string;
|
|
62
|
+
value: string;
|
|
63
|
+
}
|
|
64
|
+
/** One line in a totals block. Amounts arrive **already formatted**; this layer never computes. */
|
|
65
|
+
export interface DocumentTotalsRow {
|
|
66
|
+
label: string;
|
|
67
|
+
amount: string;
|
|
68
|
+
/** The line that carries the eye — a subtotal or the final total. At most one is usual. */
|
|
69
|
+
emphasis?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export type DocumentBlock = {
|
|
72
|
+
block_id?: string;
|
|
73
|
+
type: "heading";
|
|
74
|
+
level: 1 | 2 | 3;
|
|
75
|
+
text: string;
|
|
76
|
+
} | {
|
|
77
|
+
block_id?: string;
|
|
78
|
+
type: "paragraph";
|
|
79
|
+
text: string;
|
|
80
|
+
} | {
|
|
81
|
+
block_id?: string;
|
|
82
|
+
type: "list";
|
|
83
|
+
style: "bulleted" | "numbered";
|
|
84
|
+
items: DocumentListItem[];
|
|
85
|
+
} | {
|
|
86
|
+
block_id?: string;
|
|
87
|
+
type: "quote";
|
|
88
|
+
text: string;
|
|
89
|
+
} | {
|
|
90
|
+
block_id?: string;
|
|
91
|
+
type: "code";
|
|
92
|
+
lang?: string;
|
|
93
|
+
text: string;
|
|
94
|
+
} | {
|
|
95
|
+
block_id?: string;
|
|
96
|
+
type: "divider";
|
|
97
|
+
} | {
|
|
98
|
+
block_id?: string;
|
|
99
|
+
type: "table";
|
|
100
|
+
columns: DocumentTableColumn[];
|
|
101
|
+
rows: string[][];
|
|
102
|
+
caption?: string;
|
|
103
|
+
} | {
|
|
104
|
+
block_id?: string;
|
|
105
|
+
type: "kpi";
|
|
106
|
+
items: DocumentKpiItem[];
|
|
107
|
+
} | {
|
|
108
|
+
block_id?: string;
|
|
109
|
+
type: "callout";
|
|
110
|
+
tone: DocumentTone;
|
|
111
|
+
title?: string;
|
|
112
|
+
text: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A picture, by URL.
|
|
116
|
+
*
|
|
117
|
+
* Only an explicitly placed block — inline `` in text stays stripped by
|
|
118
|
+
* {@link sanitizeInline}, and for the reason given there: a URL inside written text is a back
|
|
119
|
+
* door that tells an external host who opened the document. Here the author put it there, and
|
|
120
|
+
* the scheme is checked on the way in.
|
|
121
|
+
*
|
|
122
|
+
* The URL has to be one the reader can actually load. The storage app has no publicly readable
|
|
123
|
+
* URL, so an uploaded file cannot be shown to a reader without a session — the help centre
|
|
124
|
+
* already hits that wall (`renderBlocks.tsx:212`).
|
|
125
|
+
*/
|
|
126
|
+
| {
|
|
127
|
+
block_id?: string;
|
|
128
|
+
type: "image";
|
|
129
|
+
url: string;
|
|
130
|
+
alt?: string;
|
|
131
|
+
caption?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The head of a letter: who it is to, and the handful of facts that identify it.
|
|
135
|
+
*
|
|
136
|
+
* Fields rather than named columns because no two documents carry the same set — a work order
|
|
137
|
+
* has no due date, a letter has no number. The sender and the house style are **not** here:
|
|
138
|
+
* those belong to the organisation and are drawn around the document, not in it.
|
|
139
|
+
*/
|
|
140
|
+
| {
|
|
141
|
+
block_id?: string;
|
|
142
|
+
type: "letterhead";
|
|
143
|
+
fields: DocumentLetterheadField[];
|
|
144
|
+
/** Address lines, already formatted; the renderer sets them, it does not compose them. */
|
|
145
|
+
recipient?: string[];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The money block: subtotal, tax per rate, total.
|
|
149
|
+
*
|
|
150
|
+
* Not a `table`, and the difference matters. A table is text in cells; this aligns on the
|
|
151
|
+
* decimal, keeps the tax rows visually between subtotal and total, and never breaks across a
|
|
152
|
+
* page. Amounts are strings the writer formatted — see {@link DocumentKpiItem.value}.
|
|
153
|
+
*/
|
|
154
|
+
| {
|
|
155
|
+
block_id?: string;
|
|
156
|
+
type: "totals";
|
|
157
|
+
rows: DocumentTotalsRow[];
|
|
158
|
+
taxRows?: DocumentTotalsRow[];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Break the page here when printing; a thin marker on screen.
|
|
162
|
+
*
|
|
163
|
+
* The only pagination an author gets. Everything else is the renderer's job — see
|
|
164
|
+
* `plans/DOCUMENTS-2026-09-14.md` §6 for what browser print can and cannot do.
|
|
165
|
+
*/
|
|
166
|
+
| {
|
|
167
|
+
block_id?: string;
|
|
168
|
+
type: "pagebreak";
|
|
169
|
+
};
|
|
170
|
+
export type DocumentBlockType = DocumentBlock["type"];
|
|
171
|
+
/**
|
|
172
|
+
* Ceilings. Wider than the timeline (which sits at 10 blocks) because this is a document, but not
|
|
173
|
+
* unlimited: the body travels as JSON through the same invoke channel as everything else and ends
|
|
174
|
+
* up in a single column.
|
|
175
|
+
*/
|
|
176
|
+
export declare const DOCUMENT_MAX_BLOCKS = 200;
|
|
177
|
+
export declare const DOCUMENT_MAX_LIST_ITEMS = 100;
|
|
178
|
+
export declare const DOCUMENT_MAX_TABLE_ROWS = 200;
|
|
179
|
+
export declare const DOCUMENT_MAX_TABLE_COLUMNS = 12;
|
|
180
|
+
export declare const DOCUMENT_MAX_KPI_ITEMS = 4;
|
|
181
|
+
export declare const DOCUMENT_MAX_LETTERHEAD_FIELDS = 10;
|
|
182
|
+
export declare const DOCUMENT_MAX_RECIPIENT_LINES = 8;
|
|
183
|
+
export declare const DOCUMENT_MAX_TOTALS_ROWS = 12;
|
|
184
|
+
export declare const DOCUMENT_MAX_TEXT_LEN = 4000;
|
|
185
|
+
export declare const DOCUMENT_MAX_CELL_LEN = 500;
|
|
186
|
+
/**
|
|
187
|
+
* Hard upper bound on the serialized body.
|
|
188
|
+
*
|
|
189
|
+
* A megabyte is some eighty thousand words — nobody writes that, and the headroom is what lets a
|
|
190
|
+
* knowledge base article be stored as blocks instead of as markdown (blocks run roughly one and a
|
|
191
|
+
* half to two times the bytes). One ceiling for every writer: a second constant is more code than
|
|
192
|
+
* a bigger number.
|
|
193
|
+
*
|
|
194
|
+
* Keep it **below** whatever the invoke channel itself accepts, so an oversized document fails
|
|
195
|
+
* with this readable error rather than as a truncated payload three layers down.
|
|
196
|
+
*/
|
|
197
|
+
export declare const DOCUMENT_MAX_BODY_BYTES: number;
|
|
198
|
+
/**
|
|
199
|
+
* Schemes a link in an artifact may carry.
|
|
200
|
+
*
|
|
201
|
+
* `javascript:` and `data:` are not among them, and that is the entire reason this list exists:
|
|
202
|
+
* those two are the only way markdown gets something executable into the document.
|
|
203
|
+
*/
|
|
204
|
+
export declare const ALLOWED_LINK_SCHEMES: readonly ["http:", "https:", "mailto:", "tel:"];
|
|
205
|
+
/**
|
|
206
|
+
* Makes one piece of inline markdown safe while keeping it readable.
|
|
207
|
+
*
|
|
208
|
+
* Three interventions, in this order:
|
|
209
|
+
*
|
|
210
|
+
* 1. **Inline images disappear**, with their alt text as replacement. This version has no `image`
|
|
211
|
+
* block precisely because the product has no publicly loadable URL (bytes come through the
|
|
212
|
+
* invoke channel as base64). An inline `` would open that gap through the back door and
|
|
213
|
+
* tell an external host who opens the document.
|
|
214
|
+
* 2. **Links to a forbidden scheme become plain text** — the label stays. Dropping the label would
|
|
215
|
+
* break the sentence over the link.
|
|
216
|
+
* 3. **Reference definitions to a forbidden scheme disappear.** Without this step `[click][x]`
|
|
217
|
+
* with `[x]: javascript:…` below it escapes step 2.
|
|
218
|
+
*/
|
|
219
|
+
export declare function sanitizeInline(text: string): string;
|
|
220
|
+
/**
|
|
221
|
+
* Prunes a written block list down to something a renderer can safely draw.
|
|
222
|
+
*
|
|
223
|
+
* Always returns **new** objects: the input comes from a model or an API client, and the sanitized
|
|
224
|
+
* text must not leak back into the original.
|
|
225
|
+
*
|
|
226
|
+
* `onDrop` should log or report back to the writer — a tool losing its table because it sent zero
|
|
227
|
+
* columns has to be able to hear that.
|
|
228
|
+
*/
|
|
229
|
+
export declare function normalizeDocumentBlocks(blocks: unknown, onDrop?: (reason: string) => void): DocumentBlock[];
|
|
230
|
+
/** How many bytes this body costs on the wire. */
|
|
231
|
+
export declare function documentBodyBytes(blocks: DocumentBlock[]): number;
|
|
232
|
+
/**
|
|
233
|
+
* A short summary of what is inside, for the thumbnail on a card and for what the assistant sees
|
|
234
|
+
* when it wants to update an artifact without reading the whole body.
|
|
235
|
+
*/
|
|
236
|
+
export declare function documentOutline(blocks: DocumentBlock[]): {
|
|
237
|
+
blocks: number;
|
|
238
|
+
types: DocumentBlockType[];
|
|
239
|
+
headings: string[];
|
|
240
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DocumentBlock } from './document-blocks';
|
|
2
|
+
/**
|
|
3
|
+
* The whole document as markdown. `title` goes on top as an H1 when the blocks carry none
|
|
4
|
+
* themselves — an export without a title cannot be found back in a downloads folder.
|
|
5
|
+
*/
|
|
6
|
+
export declare function documentToMarkdown(blocks: DocumentBlock[], title?: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DocumentBlock } from './document-blocks';
|
|
2
|
+
/**
|
|
3
|
+
* The document as plain text: what an embedding and a keyword search should see.
|
|
4
|
+
*
|
|
5
|
+
* Markdown and plain text are not the same job. `**Yealink**` embeds as three tokens where two of
|
|
6
|
+
* them are punctuation, a table's pipes are noise in a cosine, and a search for "reset" should
|
|
7
|
+
* match `## Reset` without the reader having typed a hash. So this strips the markers markdown put
|
|
8
|
+
* there and keeps the words they wrapped.
|
|
9
|
+
*
|
|
10
|
+
* Built on top of {@link documentToMarkdown} rather than beside it: one place decides what a block
|
|
11
|
+
* says and in which order, and a second walk over the union would drift from it on the first new
|
|
12
|
+
* block type.
|
|
13
|
+
*/
|
|
14
|
+
export declare function documentToText(blocks: DocumentBlock[], title?: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|