@company-semantics/contracts 62.9.0 → 62.11.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/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +7 -5
- package/src/ingestion/README.md +13 -0
- package/src/ingestion/__tests__/surfaces.test.ts +65 -0
- package/src/ingestion/index.ts +18 -3
- package/src/ingestion/registry.ts +29 -0
- package/src/ingestion/surfaces.ts +91 -0
- package/src/ingestion/types.ts +36 -0
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '9829bca605e4' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '9829bca605e4a464d73b067d1d063d2a68c5871b9fae570470a43987e481bb3a' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -2714,8 +2714,8 @@ export interface paths {
|
|
|
2714
2714
|
get?: never;
|
|
2715
2715
|
put?: never;
|
|
2716
2716
|
/**
|
|
2717
|
-
* Upload an org-chart artifact (PDF
|
|
2718
|
-
* @description Accepts
|
|
2717
|
+
* Upload an org-chart artifact (text, office documents, PDF, images, diagrams) for ingestion
|
|
2718
|
+
* @description Accepts one multipart file admitted by the org-chart ingestion surface (contracts INGESTION_SURFACES: text, document, image and diagram families; 10 MiB ceiling). With `?dryRun=true` the operation only reaches the validated state and writes no live rows. Returns 202 with the operationId so the caller can poll.
|
|
2719
2719
|
*/
|
|
2720
2720
|
post: operations["importOrgChart"];
|
|
2721
2721
|
delete?: never;
|
|
@@ -2826,7 +2826,7 @@ export interface paths {
|
|
|
2826
2826
|
path?: never;
|
|
2827
2827
|
cookie?: never;
|
|
2828
2828
|
};
|
|
2829
|
-
/** Get extracted text content from a Google Drive file */
|
|
2829
|
+
/** Get extracted text content from a Google Drive file (fetched in a registry format and parsed like an upload) */
|
|
2830
2830
|
get: operations["getDriveFileContent"];
|
|
2831
2831
|
put?: never;
|
|
2832
2832
|
post?: never;
|
|
@@ -4762,6 +4762,8 @@ export interface components {
|
|
|
4762
4762
|
destinationDocId: string;
|
|
4763
4763
|
title: string;
|
|
4764
4764
|
rawText: string;
|
|
4765
|
+
/** Format: uuid */
|
|
4766
|
+
sourceOperationId?: string;
|
|
4765
4767
|
chatId: string;
|
|
4766
4768
|
};
|
|
4767
4769
|
RoleCatalogResponse: {
|
|
@@ -11686,14 +11688,14 @@ export interface operations {
|
|
|
11686
11688
|
};
|
|
11687
11689
|
content?: never;
|
|
11688
11690
|
};
|
|
11689
|
-
/** @description Upload exceeds the 10
|
|
11691
|
+
/** @description Upload exceeds the surface ceiling (10 MiB) or the format cap */
|
|
11690
11692
|
413: {
|
|
11691
11693
|
headers: {
|
|
11692
11694
|
[name: string]: unknown;
|
|
11693
11695
|
};
|
|
11694
11696
|
content?: never;
|
|
11695
11697
|
};
|
|
11696
|
-
/** @description
|
|
11698
|
+
/** @description Bytes match no registered format, or a format the org-chart surface does not admit */
|
|
11697
11699
|
415: {
|
|
11698
11700
|
headers: {
|
|
11699
11701
|
[name: string]: unknown;
|
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
|
|
|
@@ -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,174 +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
|
},
|
|
70
78
|
{
|
|
71
79
|
mime: "application/vnd.ms-powerpoint",
|
|
80
|
+
family: "document",
|
|
72
81
|
extensions: [".ppt"],
|
|
73
82
|
label: "PowerPoint",
|
|
74
83
|
maxBytes: 50_000_000,
|
|
75
84
|
},
|
|
76
85
|
{
|
|
77
86
|
mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
87
|
+
family: "document",
|
|
78
88
|
extensions: [".xlsx"],
|
|
79
89
|
label: "Excel",
|
|
80
90
|
maxBytes: 50_000_000,
|
|
81
91
|
},
|
|
82
92
|
{
|
|
83
93
|
mime: "application/vnd.ms-excel",
|
|
94
|
+
family: "document",
|
|
84
95
|
extensions: [".xls"],
|
|
85
96
|
label: "Excel",
|
|
86
97
|
maxBytes: 50_000_000,
|
|
87
98
|
},
|
|
88
99
|
{
|
|
89
100
|
mime: "application/vnd.oasis.opendocument.text",
|
|
101
|
+
family: "document",
|
|
90
102
|
extensions: [".odt"],
|
|
91
103
|
label: "OpenDocument",
|
|
92
104
|
maxBytes: 25_000_000,
|
|
93
105
|
},
|
|
94
106
|
{
|
|
95
107
|
mime: "application/vnd.oasis.opendocument.spreadsheet",
|
|
108
|
+
family: "document",
|
|
96
109
|
extensions: [".ods"],
|
|
97
110
|
label: "OpenDocument",
|
|
98
111
|
maxBytes: 50_000_000,
|
|
99
112
|
},
|
|
100
113
|
{
|
|
101
114
|
mime: "application/vnd.oasis.opendocument.presentation",
|
|
115
|
+
family: "document",
|
|
102
116
|
extensions: [".odp"],
|
|
103
117
|
label: "OpenDocument",
|
|
104
118
|
maxBytes: 50_000_000,
|
|
105
119
|
},
|
|
106
120
|
{
|
|
107
121
|
mime: "application/epub+zip",
|
|
122
|
+
family: "document",
|
|
108
123
|
extensions: [".epub"],
|
|
109
124
|
label: "EPUB",
|
|
110
125
|
maxBytes: 25_000_000,
|
|
111
126
|
},
|
|
112
127
|
{
|
|
113
128
|
mime: "application/pdf",
|
|
129
|
+
family: "document",
|
|
114
130
|
extensions: [".pdf"],
|
|
115
131
|
label: "PDF",
|
|
116
132
|
maxBytes: 50_000_000,
|
|
117
133
|
},
|
|
118
134
|
{
|
|
119
135
|
mime: "image/png",
|
|
136
|
+
family: "image",
|
|
120
137
|
extensions: [".png"],
|
|
121
138
|
label: "Image",
|
|
122
139
|
maxBytes: 25_000_000,
|
|
123
140
|
},
|
|
124
141
|
{
|
|
125
142
|
mime: "image/jpeg",
|
|
143
|
+
family: "image",
|
|
126
144
|
extensions: [".jpg", ".jpeg"],
|
|
127
145
|
label: "Image",
|
|
128
146
|
maxBytes: 25_000_000,
|
|
129
147
|
},
|
|
130
148
|
{
|
|
131
149
|
mime: "image/webp",
|
|
150
|
+
family: "image",
|
|
132
151
|
extensions: [".webp"],
|
|
133
152
|
label: "Image",
|
|
134
153
|
maxBytes: 25_000_000,
|
|
135
154
|
},
|
|
136
155
|
{
|
|
137
156
|
mime: "image/gif",
|
|
157
|
+
family: "image",
|
|
138
158
|
extensions: [".gif"],
|
|
139
159
|
label: "Image",
|
|
140
160
|
maxBytes: 25_000_000,
|
|
141
161
|
},
|
|
142
162
|
{
|
|
143
163
|
mime: "image/heic",
|
|
164
|
+
family: "image",
|
|
144
165
|
extensions: [".heic"],
|
|
145
166
|
label: "Image",
|
|
146
167
|
maxBytes: 25_000_000,
|
|
147
168
|
},
|
|
148
169
|
{
|
|
149
170
|
mime: "image/svg+xml",
|
|
171
|
+
family: "diagram",
|
|
150
172
|
extensions: [".svg"],
|
|
151
173
|
label: "Diagram",
|
|
152
174
|
maxBytes: 10_000_000,
|
|
153
175
|
},
|
|
154
176
|
{
|
|
155
177
|
mime: "application/vnd.jgraph.mxfile",
|
|
178
|
+
family: "diagram",
|
|
156
179
|
extensions: [".drawio"],
|
|
157
180
|
label: "Diagram",
|
|
158
181
|
maxBytes: 10_000_000,
|
|
159
182
|
},
|
|
160
183
|
{
|
|
161
184
|
mime: "audio/mpeg",
|
|
185
|
+
family: "audio",
|
|
162
186
|
extensions: [".mp3"],
|
|
163
187
|
label: "Audio",
|
|
164
188
|
maxBytes: 500_000_000,
|
|
165
189
|
},
|
|
166
190
|
{
|
|
167
191
|
mime: "audio/x-m4a",
|
|
192
|
+
family: "audio",
|
|
168
193
|
extensions: [".m4a"],
|
|
169
194
|
label: "Audio",
|
|
170
195
|
maxBytes: 500_000_000,
|
|
171
196
|
},
|
|
172
197
|
{
|
|
173
198
|
mime: "audio/wav",
|
|
199
|
+
family: "audio",
|
|
174
200
|
extensions: [".wav"],
|
|
175
201
|
label: "Audio",
|
|
176
202
|
maxBytes: 500_000_000,
|
|
177
203
|
},
|
|
178
204
|
{
|
|
179
205
|
mime: "audio/aac",
|
|
206
|
+
family: "audio",
|
|
180
207
|
extensions: [".aac"],
|
|
181
208
|
label: "Audio",
|
|
182
209
|
maxBytes: 500_000_000,
|
|
183
210
|
},
|
|
184
211
|
{
|
|
185
212
|
mime: "video/mp4",
|
|
213
|
+
family: "video",
|
|
186
214
|
extensions: [".mp4", ".m4v"],
|
|
187
215
|
label: "Video",
|
|
188
216
|
maxBytes: 500_000_000,
|
|
189
217
|
},
|
|
190
218
|
{
|
|
191
219
|
mime: "video/quicktime",
|
|
220
|
+
family: "video",
|
|
192
221
|
extensions: [".mov"],
|
|
193
222
|
label: "Video",
|
|
194
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
|
+
}
|