@company-semantics/contracts 62.8.0 → 62.10.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/package.json +1 -1
- package/src/ingestion/README.md +13 -0
- package/src/ingestion/__tests__/registry.test.ts +2 -2
- package/src/ingestion/__tests__/surfaces.test.ts +65 -0
- package/src/ingestion/index.ts +18 -3
- package/src/ingestion/registry.ts +59 -0
- package/src/ingestion/surfaces.ts +91 -0
- package/src/ingestion/types.ts +36 -0
package/package.json
CHANGED
package/src/ingestion/README.md
CHANGED
|
@@ -19,18 +19,31 @@ attribute and supported-formats copy from the same data.
|
|
|
19
19
|
hand-maintained — adding a format updates both automatically.
|
|
20
20
|
- No normalizer-routing fields here (`canonicalKind`, `normalizerKey` are
|
|
21
21
|
backend-only). This package stays pure vocabulary.
|
|
22
|
+
- Every entry carries a `family`; `INGESTION_SURFACES` declares which families
|
|
23
|
+
each upload surface admits, and `formatsFor` / `acceptFor` /
|
|
24
|
+
`supportedLabelFor` / `maxBytesFor` are **derived** from that declaration
|
|
25
|
+
(ADR-CONTRACTS-159). A surface never re-spells a MIME.
|
|
22
26
|
|
|
23
27
|
<!-- BEGIN GENERATED: readme-public-api — derived from code by `pnpm readme-api`. Do not edit. -->
|
|
24
28
|
|
|
25
29
|
## Public API
|
|
26
30
|
|
|
27
31
|
- `INGESTION_FORMATS` — The canonical public format registry.
|
|
32
|
+
- `INGESTION_SURFACES` — The canonical surface declarations. - `company-md`: context-bank and goals extraction.
|
|
28
33
|
- `INGEST_ACCEPT` — Comma-joined extension string for a file input's `accept` attribute.
|
|
34
|
+
- `IngestionFamily` _(type)_ — The semantic family a format belongs to.
|
|
29
35
|
- `IngestionFormat` _(type)_ — One accepted upload format, keyed by its canonical MIME type.
|
|
36
|
+
- `IngestionSurface` _(type)_ — What one upload surface consumes: the families it admits and, optionally, a byte ceiling tighter than the…
|
|
37
|
+
- `IngestionSurfaceKey` _(type)_ — The upload surfaces that consume the registry.
|
|
30
38
|
- `SUPPORTED_LABEL` — Human-readable supported-formats summary, derived from {@link humanLabels}.
|
|
31
39
|
- `acceptExtensions` — All accepted filename extensions, in registry order.
|
|
40
|
+
- `acceptFor` — Comma-joined extension string for the surface's file input `accept`.
|
|
32
41
|
- `byMime` — Look up the entry for a canonical MIME; undefined means unsupported.
|
|
42
|
+
- `formatsFor` — The registry formats a surface admits, in registry order.
|
|
33
43
|
- `humanLabels` — Unique group labels for human display, in registry order.
|
|
44
|
+
- `maxBytesFor` — The transport byte ceiling for a surface: the largest registry cap among its admitted formats, tightened to…
|
|
45
|
+
- `supportedLabelFor` — "Supported: …" copy naming every label group the surface admits.
|
|
46
|
+
- `surfaceAdmits` — True when the surface admits this registry format.
|
|
34
47
|
|
|
35
48
|
<!-- END GENERATED: readme-public-api -->
|
|
36
49
|
|
|
@@ -31,14 +31,14 @@ describe("ingestion registry", () => {
|
|
|
31
31
|
it("derives INGEST_ACCEPT from every extension in registry order", () => {
|
|
32
32
|
expect(INGEST_ACCEPT).toBe(acceptExtensions().join(","));
|
|
33
33
|
expect(INGEST_ACCEPT).toBe(
|
|
34
|
-
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.xlsx,.odt,.pdf,.png,.jpg,.jpeg,.webp,.gif,.heic,.svg,.drawio,.mp3,.m4a,.wav,.aac,.mp4,.m4v,.mov",
|
|
34
|
+
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf,.png,.jpg,.jpeg,.webp,.gif,.heic,.svg,.drawio,.mp3,.m4a,.wav,.aac,.mp4,.m4v,.mov",
|
|
35
35
|
);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
it("derives SUPPORTED_LABEL from the unique group labels", () => {
|
|
39
39
|
expect(SUPPORTED_LABEL).toBe(`Supported: ${humanLabels().join(", ")}`);
|
|
40
40
|
expect(SUPPORTED_LABEL).toBe(
|
|
41
|
-
"Supported: Text, Markdown, HTML, CSV, RTF, Word, PowerPoint, Excel, OpenDocument, PDF, Image, Diagram, Audio, Video",
|
|
41
|
+
"Supported: Text, Markdown, HTML, CSV, RTF, Word, PowerPoint, Excel, OpenDocument, EPUB, PDF, Image, Diagram, Audio, Video",
|
|
42
42
|
);
|
|
43
43
|
});
|
|
44
44
|
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { INGESTION_FORMATS, byMime } from "../registry";
|
|
3
|
+
import {
|
|
4
|
+
INGESTION_SURFACES,
|
|
5
|
+
formatsFor,
|
|
6
|
+
surfaceAdmits,
|
|
7
|
+
acceptFor,
|
|
8
|
+
supportedLabelFor,
|
|
9
|
+
maxBytesFor,
|
|
10
|
+
} from "../surfaces";
|
|
11
|
+
|
|
12
|
+
describe("ingestion surfaces", () => {
|
|
13
|
+
it("every registry entry carries a family", () => {
|
|
14
|
+
for (const entry of INGESTION_FORMATS) {
|
|
15
|
+
expect(entry.family).toMatch(
|
|
16
|
+
/^(text|document|image|diagram|audio|video)$/,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("every surface admits at least one format, in registry order", () => {
|
|
22
|
+
for (const key of Object.keys(INGESTION_SURFACES) as Array<
|
|
23
|
+
keyof typeof INGESTION_SURFACES
|
|
24
|
+
>) {
|
|
25
|
+
const formats = formatsFor(key);
|
|
26
|
+
expect(formats.length).toBeGreaterThan(0);
|
|
27
|
+
const order = formats.map((f) => INGESTION_FORMATS.indexOf(f));
|
|
28
|
+
expect(order).toEqual([...order].sort((a, b) => a - b));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("company-md admits text and documents only", () => {
|
|
33
|
+
expect(surfaceAdmits("company-md", byMime("application/pdf")!)).toBe(true);
|
|
34
|
+
expect(surfaceAdmits("company-md", byMime("text/csv")!)).toBe(true);
|
|
35
|
+
expect(surfaceAdmits("company-md", byMime("image/png")!)).toBe(false);
|
|
36
|
+
expect(surfaceAdmits("company-md", byMime("audio/mpeg")!)).toBe(false);
|
|
37
|
+
expect(acceptFor("company-md")).toBe(
|
|
38
|
+
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf",
|
|
39
|
+
);
|
|
40
|
+
expect(supportedLabelFor("company-md")).toBe(
|
|
41
|
+
"Supported: Text, Markdown, HTML, CSV, RTF, Word, PowerPoint, Excel, OpenDocument, EPUB, PDF",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("org-chart adds images and diagrams but never media", () => {
|
|
46
|
+
expect(surfaceAdmits("org-chart", byMime("image/heic")!)).toBe(true);
|
|
47
|
+
expect(surfaceAdmits("org-chart", byMime("image/svg+xml")!)).toBe(true);
|
|
48
|
+
expect(surfaceAdmits("org-chart", byMime("video/mp4")!)).toBe(false);
|
|
49
|
+
expect(acceptFor("org-chart")).toBe(
|
|
50
|
+
".txt,.md,.html,.htm,.csv,.rtf,.docx,.doc,.pptx,.ppt,.xlsx,.xls,.odt,.ods,.odp,.epub,.pdf,.png,.jpg,.jpeg,.webp,.gif,.heic,.svg,.drawio",
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("derives the byte ceiling from the admitted formats and the surface cap", () => {
|
|
55
|
+
expect(maxBytesFor("company-md")).toBe(50_000_000);
|
|
56
|
+
expect(maxBytesFor("org-chart")).toBe(10 * 1024 * 1024);
|
|
57
|
+
for (const key of ["company-md", "org-chart"] as const) {
|
|
58
|
+
const ceiling = INGESTION_SURFACES[key].maxBytes ?? Infinity;
|
|
59
|
+
expect(maxBytesFor(key)).toBeLessThanOrEqual(ceiling);
|
|
60
|
+
expect(maxBytesFor(key)).toBeLessThanOrEqual(
|
|
61
|
+
Math.max(...formatsFor(key).map((f) => f.maxBytes)),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
package/src/ingestion/index.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ingestion Domain Barrel
|
|
3
3
|
*
|
|
4
|
-
* Re-exports the public document-ingestion format vocabulary
|
|
4
|
+
* Re-exports the public document-ingestion format vocabulary and the
|
|
5
|
+
* per-surface views derived from it.
|
|
5
6
|
* Import from '@company-semantics/contracts/ingestion'.
|
|
6
7
|
*
|
|
7
|
-
* @see ADR-
|
|
8
|
+
* @see ADR-CONTRACTS-075 (registry), ADR-CONTRACTS-159 (surfaces)
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
export type {
|
|
11
|
+
export type {
|
|
12
|
+
IngestionFamily,
|
|
13
|
+
IngestionFormat,
|
|
14
|
+
IngestionSurface,
|
|
15
|
+
IngestionSurfaceKey,
|
|
16
|
+
} from "./types";
|
|
11
17
|
|
|
12
18
|
export {
|
|
13
19
|
INGESTION_FORMATS,
|
|
@@ -17,3 +23,12 @@ export {
|
|
|
17
23
|
INGEST_ACCEPT,
|
|
18
24
|
SUPPORTED_LABEL,
|
|
19
25
|
} from "./registry";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
INGESTION_SURFACES,
|
|
29
|
+
formatsFor,
|
|
30
|
+
surfaceAdmits,
|
|
31
|
+
acceptFor,
|
|
32
|
+
supportedLabelFor,
|
|
33
|
+
maxBytesFor,
|
|
34
|
+
} from "./surfaces";
|
|
@@ -21,144 +21,203 @@ import type { IngestionFormat } from "./types";
|
|
|
21
21
|
export const INGESTION_FORMATS: readonly IngestionFormat[] = [
|
|
22
22
|
{
|
|
23
23
|
mime: "text/plain",
|
|
24
|
+
family: "text",
|
|
24
25
|
extensions: [".txt"],
|
|
25
26
|
label: "Text",
|
|
26
27
|
maxBytes: 10_000_000,
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
mime: "text/markdown",
|
|
31
|
+
family: "text",
|
|
30
32
|
extensions: [".md"],
|
|
31
33
|
label: "Markdown",
|
|
32
34
|
maxBytes: 10_000_000,
|
|
33
35
|
},
|
|
34
36
|
{
|
|
35
37
|
mime: "text/html",
|
|
38
|
+
family: "text",
|
|
36
39
|
extensions: [".html", ".htm"],
|
|
37
40
|
label: "HTML",
|
|
38
41
|
maxBytes: 10_000_000,
|
|
39
42
|
},
|
|
40
43
|
{
|
|
41
44
|
mime: "text/csv",
|
|
45
|
+
family: "text",
|
|
42
46
|
extensions: [".csv"],
|
|
43
47
|
label: "CSV",
|
|
44
48
|
maxBytes: 25_000_000,
|
|
45
49
|
},
|
|
46
50
|
{
|
|
47
51
|
mime: "application/rtf",
|
|
52
|
+
family: "document",
|
|
48
53
|
extensions: [".rtf"],
|
|
49
54
|
label: "RTF",
|
|
50
55
|
maxBytes: 25_000_000,
|
|
51
56
|
},
|
|
52
57
|
{
|
|
53
58
|
mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
59
|
+
family: "document",
|
|
54
60
|
extensions: [".docx"],
|
|
55
61
|
label: "Word",
|
|
56
62
|
maxBytes: 25_000_000,
|
|
57
63
|
},
|
|
58
64
|
{
|
|
59
65
|
mime: "application/msword",
|
|
66
|
+
family: "document",
|
|
60
67
|
extensions: [".doc"],
|
|
61
68
|
label: "Word",
|
|
62
69
|
maxBytes: 25_000_000,
|
|
63
70
|
},
|
|
64
71
|
{
|
|
65
72
|
mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
73
|
+
family: "document",
|
|
66
74
|
extensions: [".pptx"],
|
|
67
75
|
label: "PowerPoint",
|
|
68
76
|
maxBytes: 50_000_000,
|
|
69
77
|
},
|
|
78
|
+
{
|
|
79
|
+
mime: "application/vnd.ms-powerpoint",
|
|
80
|
+
family: "document",
|
|
81
|
+
extensions: [".ppt"],
|
|
82
|
+
label: "PowerPoint",
|
|
83
|
+
maxBytes: 50_000_000,
|
|
84
|
+
},
|
|
70
85
|
{
|
|
71
86
|
mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
87
|
+
family: "document",
|
|
72
88
|
extensions: [".xlsx"],
|
|
73
89
|
label: "Excel",
|
|
74
90
|
maxBytes: 50_000_000,
|
|
75
91
|
},
|
|
92
|
+
{
|
|
93
|
+
mime: "application/vnd.ms-excel",
|
|
94
|
+
family: "document",
|
|
95
|
+
extensions: [".xls"],
|
|
96
|
+
label: "Excel",
|
|
97
|
+
maxBytes: 50_000_000,
|
|
98
|
+
},
|
|
76
99
|
{
|
|
77
100
|
mime: "application/vnd.oasis.opendocument.text",
|
|
101
|
+
family: "document",
|
|
78
102
|
extensions: [".odt"],
|
|
79
103
|
label: "OpenDocument",
|
|
80
104
|
maxBytes: 25_000_000,
|
|
81
105
|
},
|
|
106
|
+
{
|
|
107
|
+
mime: "application/vnd.oasis.opendocument.spreadsheet",
|
|
108
|
+
family: "document",
|
|
109
|
+
extensions: [".ods"],
|
|
110
|
+
label: "OpenDocument",
|
|
111
|
+
maxBytes: 50_000_000,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
mime: "application/vnd.oasis.opendocument.presentation",
|
|
115
|
+
family: "document",
|
|
116
|
+
extensions: [".odp"],
|
|
117
|
+
label: "OpenDocument",
|
|
118
|
+
maxBytes: 50_000_000,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
mime: "application/epub+zip",
|
|
122
|
+
family: "document",
|
|
123
|
+
extensions: [".epub"],
|
|
124
|
+
label: "EPUB",
|
|
125
|
+
maxBytes: 25_000_000,
|
|
126
|
+
},
|
|
82
127
|
{
|
|
83
128
|
mime: "application/pdf",
|
|
129
|
+
family: "document",
|
|
84
130
|
extensions: [".pdf"],
|
|
85
131
|
label: "PDF",
|
|
86
132
|
maxBytes: 50_000_000,
|
|
87
133
|
},
|
|
88
134
|
{
|
|
89
135
|
mime: "image/png",
|
|
136
|
+
family: "image",
|
|
90
137
|
extensions: [".png"],
|
|
91
138
|
label: "Image",
|
|
92
139
|
maxBytes: 25_000_000,
|
|
93
140
|
},
|
|
94
141
|
{
|
|
95
142
|
mime: "image/jpeg",
|
|
143
|
+
family: "image",
|
|
96
144
|
extensions: [".jpg", ".jpeg"],
|
|
97
145
|
label: "Image",
|
|
98
146
|
maxBytes: 25_000_000,
|
|
99
147
|
},
|
|
100
148
|
{
|
|
101
149
|
mime: "image/webp",
|
|
150
|
+
family: "image",
|
|
102
151
|
extensions: [".webp"],
|
|
103
152
|
label: "Image",
|
|
104
153
|
maxBytes: 25_000_000,
|
|
105
154
|
},
|
|
106
155
|
{
|
|
107
156
|
mime: "image/gif",
|
|
157
|
+
family: "image",
|
|
108
158
|
extensions: [".gif"],
|
|
109
159
|
label: "Image",
|
|
110
160
|
maxBytes: 25_000_000,
|
|
111
161
|
},
|
|
112
162
|
{
|
|
113
163
|
mime: "image/heic",
|
|
164
|
+
family: "image",
|
|
114
165
|
extensions: [".heic"],
|
|
115
166
|
label: "Image",
|
|
116
167
|
maxBytes: 25_000_000,
|
|
117
168
|
},
|
|
118
169
|
{
|
|
119
170
|
mime: "image/svg+xml",
|
|
171
|
+
family: "diagram",
|
|
120
172
|
extensions: [".svg"],
|
|
121
173
|
label: "Diagram",
|
|
122
174
|
maxBytes: 10_000_000,
|
|
123
175
|
},
|
|
124
176
|
{
|
|
125
177
|
mime: "application/vnd.jgraph.mxfile",
|
|
178
|
+
family: "diagram",
|
|
126
179
|
extensions: [".drawio"],
|
|
127
180
|
label: "Diagram",
|
|
128
181
|
maxBytes: 10_000_000,
|
|
129
182
|
},
|
|
130
183
|
{
|
|
131
184
|
mime: "audio/mpeg",
|
|
185
|
+
family: "audio",
|
|
132
186
|
extensions: [".mp3"],
|
|
133
187
|
label: "Audio",
|
|
134
188
|
maxBytes: 500_000_000,
|
|
135
189
|
},
|
|
136
190
|
{
|
|
137
191
|
mime: "audio/x-m4a",
|
|
192
|
+
family: "audio",
|
|
138
193
|
extensions: [".m4a"],
|
|
139
194
|
label: "Audio",
|
|
140
195
|
maxBytes: 500_000_000,
|
|
141
196
|
},
|
|
142
197
|
{
|
|
143
198
|
mime: "audio/wav",
|
|
199
|
+
family: "audio",
|
|
144
200
|
extensions: [".wav"],
|
|
145
201
|
label: "Audio",
|
|
146
202
|
maxBytes: 500_000_000,
|
|
147
203
|
},
|
|
148
204
|
{
|
|
149
205
|
mime: "audio/aac",
|
|
206
|
+
family: "audio",
|
|
150
207
|
extensions: [".aac"],
|
|
151
208
|
label: "Audio",
|
|
152
209
|
maxBytes: 500_000_000,
|
|
153
210
|
},
|
|
154
211
|
{
|
|
155
212
|
mime: "video/mp4",
|
|
213
|
+
family: "video",
|
|
156
214
|
extensions: [".mp4", ".m4v"],
|
|
157
215
|
label: "Video",
|
|
158
216
|
maxBytes: 500_000_000,
|
|
159
217
|
},
|
|
160
218
|
{
|
|
161
219
|
mime: "video/quicktime",
|
|
220
|
+
family: "video",
|
|
162
221
|
extensions: [".mov"],
|
|
163
222
|
label: "Video",
|
|
164
223
|
maxBytes: 500_000_000,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingestion Surfaces
|
|
3
|
+
*
|
|
4
|
+
* Per-surface views of the single format registry. A surface names the
|
|
5
|
+
* families it consumes; everything else — the formats, the file-input `accept`
|
|
6
|
+
* string, the "Supported: …" copy, the transport byte ceiling — is DERIVED from
|
|
7
|
+
* `INGESTION_FORMATS` through that declaration. No surface re-spells a MIME.
|
|
8
|
+
*
|
|
9
|
+
* Invariants:
|
|
10
|
+
* - Every surface admits at least one registry format.
|
|
11
|
+
* - `maxBytesFor` never exceeds the registry cap of any admitted format, and
|
|
12
|
+
* never exceeds the surface ceiling when one is declared.
|
|
13
|
+
*
|
|
14
|
+
* @see ADR-CONTRACTS-159 for design rationale
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { INGESTION_FORMATS } from "./registry";
|
|
18
|
+
import type {
|
|
19
|
+
IngestionFormat,
|
|
20
|
+
IngestionSurface,
|
|
21
|
+
IngestionSurfaceKey,
|
|
22
|
+
} from "./types";
|
|
23
|
+
|
|
24
|
+
/** 10 MiB — the org-chart ceiling (see the surface JSDoc below). */
|
|
25
|
+
const ORG_CHART_MAX_BYTES = 10 * 1024 * 1024;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The canonical surface declarations.
|
|
29
|
+
*
|
|
30
|
+
* - `company-md`: context-bank and goals extraction. Consumes text and
|
|
31
|
+
* documents; images, diagrams and media are not extracted on this surface
|
|
32
|
+
* yet, so the picker must not advertise them.
|
|
33
|
+
* - `org-chart`: the HRIS / org-chart import. Consumes text and documents plus
|
|
34
|
+
* images and diagrams (a photographed or drawn chart goes to a vision-capable
|
|
35
|
+
* model natively). The 10 MiB ceiling bounds the bytes handed to that model
|
|
36
|
+
* per upload; the registry caps for its families would otherwise reach 50 MB.
|
|
37
|
+
*/
|
|
38
|
+
export const INGESTION_SURFACES: Readonly<
|
|
39
|
+
Record<IngestionSurfaceKey, IngestionSurface>
|
|
40
|
+
> = {
|
|
41
|
+
"company-md": {
|
|
42
|
+
key: "company-md",
|
|
43
|
+
families: ["text", "document"],
|
|
44
|
+
},
|
|
45
|
+
"org-chart": {
|
|
46
|
+
key: "org-chart",
|
|
47
|
+
families: ["text", "document", "image", "diagram"],
|
|
48
|
+
maxBytes: ORG_CHART_MAX_BYTES,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** The registry formats a surface admits, in registry order. */
|
|
53
|
+
export function formatsFor(surface: IngestionSurfaceKey): IngestionFormat[] {
|
|
54
|
+
const { families } = INGESTION_SURFACES[surface];
|
|
55
|
+
return INGESTION_FORMATS.filter((entry) => families.includes(entry.family));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** True when the surface admits this registry format. */
|
|
59
|
+
export function surfaceAdmits(
|
|
60
|
+
surface: IngestionSurfaceKey,
|
|
61
|
+
format: IngestionFormat,
|
|
62
|
+
): boolean {
|
|
63
|
+
return INGESTION_SURFACES[surface].families.includes(format.family);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Comma-joined extension string for the surface's file input `accept`. */
|
|
67
|
+
export function acceptFor(surface: IngestionSurfaceKey): string {
|
|
68
|
+
return formatsFor(surface)
|
|
69
|
+
.flatMap((entry) => [...entry.extensions])
|
|
70
|
+
.join(",");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** "Supported: …" copy naming every label group the surface admits. */
|
|
74
|
+
export function supportedLabelFor(surface: IngestionSurfaceKey): string {
|
|
75
|
+
const labels = [...new Set(formatsFor(surface).map((entry) => entry.label))];
|
|
76
|
+
return `Supported: ${labels.join(", ")}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The transport byte ceiling for a surface: the largest registry cap among
|
|
81
|
+
* its admitted formats, tightened to the surface ceiling when one is declared.
|
|
82
|
+
* The per-format registry cap still applies underneath it.
|
|
83
|
+
*/
|
|
84
|
+
export function maxBytesFor(surface: IngestionSurfaceKey): number {
|
|
85
|
+
const registryMax = formatsFor(surface).reduce(
|
|
86
|
+
(max, entry) => Math.max(max, entry.maxBytes),
|
|
87
|
+
0,
|
|
88
|
+
);
|
|
89
|
+
const ceiling = INGESTION_SURFACES[surface].maxBytes;
|
|
90
|
+
return ceiling === undefined ? registryMax : Math.min(registryMax, ceiling);
|
|
91
|
+
}
|
package/src/ingestion/types.ts
CHANGED
|
@@ -15,6 +15,18 @@
|
|
|
15
15
|
* @see ADR-CONT-075 for design rationale
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* The semantic family a format belongs to. A SURFACE (an upload picker and the
|
|
20
|
+
* route behind it) declares which families it consumes; the accept-set and the
|
|
21
|
+
* byte ceiling for that surface derive from the registry through this field.
|
|
22
|
+
* Public and semantic — NOT the backend normalizer dispatch (`canonicalKind` /
|
|
23
|
+
* `normalizerKey` stay backend-local, ADR-CONTRACTS-075).
|
|
24
|
+
*
|
|
25
|
+
* @see ADR-CONTRACTS-159
|
|
26
|
+
*/
|
|
27
|
+
export type IngestionFamily =
|
|
28
|
+
"text" | "document" | "image" | "diagram" | "audio" | "video";
|
|
29
|
+
|
|
18
30
|
/**
|
|
19
31
|
* One accepted upload format, keyed by its canonical MIME type.
|
|
20
32
|
*
|
|
@@ -24,10 +36,13 @@
|
|
|
24
36
|
* - `label` is the human display GROUP and is intentionally shared across
|
|
25
37
|
* related formats (`.docx` + `.doc` are both `Word`; the rasters are all
|
|
26
38
|
* `Image`; `.svg` + `.drawio` are `Diagram`).
|
|
39
|
+
* - `family` is the semantic family a surface selects on; every entry has one.
|
|
27
40
|
*/
|
|
28
41
|
export interface IngestionFormat {
|
|
29
42
|
/** Canonical MIME type (the registry key). */
|
|
30
43
|
mime: string;
|
|
44
|
+
/** Semantic family the format belongs to (what a surface selects on). */
|
|
45
|
+
family: IngestionFamily;
|
|
31
46
|
/** Lowercase, dot-prefixed filename extensions for this format. */
|
|
32
47
|
extensions: readonly string[];
|
|
33
48
|
/** Human display group label (e.g. `Word`, `Image`, `Audio`). */
|
|
@@ -35,3 +50,24 @@ export interface IngestionFormat {
|
|
|
35
50
|
/** Per-format upload byte cap. */
|
|
36
51
|
maxBytes: number;
|
|
37
52
|
}
|
|
53
|
+
|
|
54
|
+
/** The upload surfaces that consume the registry. */
|
|
55
|
+
export type IngestionSurfaceKey = "company-md" | "org-chart";
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* What one upload surface consumes: the families it admits and, optionally, a
|
|
59
|
+
* byte ceiling tighter than the registry caps of those families.
|
|
60
|
+
*
|
|
61
|
+
* @see ADR-CONTRACTS-159
|
|
62
|
+
*/
|
|
63
|
+
export interface IngestionSurface {
|
|
64
|
+
/** The surface this declaration describes. */
|
|
65
|
+
key: IngestionSurfaceKey;
|
|
66
|
+
/** Families the surface admits; a format outside them is refused (415). */
|
|
67
|
+
families: readonly IngestionFamily[];
|
|
68
|
+
/**
|
|
69
|
+
* Surface-level byte ceiling. When set it caps every admitted format below
|
|
70
|
+
* its registry `maxBytes`; absent means the registry caps apply as-is.
|
|
71
|
+
*/
|
|
72
|
+
maxBytes?: number;
|
|
73
|
+
}
|