@daihum/record-formats 0.1.0 → 0.1.1
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/README.md +21 -0
- package/dist/archive-description/index.d.ts +163 -0
- package/dist/archive-description/index.d.ts.map +1 -0
- package/dist/archive-description/index.js +1256 -0
- package/dist/archive-description/index.js.map +1 -0
- package/dist/ead3/index.d.ts +4 -158
- package/dist/ead3/index.js +4 -519
- package/dist/ead3-DM2in-ck.js +592 -0
- package/dist/ead3-DM2in-ck.js.map +1 -0
- package/dist/{index-90ZPmZ85.d.ts → index-BNudmNFx.d.ts} +2 -2
- package/dist/{index-90ZPmZ85.d.ts.map → index-BNudmNFx.d.ts.map} +1 -1
- package/dist/index-DxjnI5KZ.d.ts +168 -0
- package/dist/index-DxjnI5KZ.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{result-YrPe5I9D.js → result-Cl7IY6PN.js} +1 -1
- package/dist/{result-YrPe5I9D.js.map → result-Cl7IY6PN.js.map} +1 -1
- package/dist/{result-Dd9q1uJU.d.ts → result-D7vcrlFh.d.ts} +1 -1
- package/dist/{result-Dd9q1uJU.d.ts.map → result-D7vcrlFh.d.ts.map} +1 -1
- package/dist/xml/index.d.ts +2 -2
- package/dist/xml/index.js +2 -2
- package/dist/{xml-6kdXnoBh.js → xml-DXtayJiw.js} +2 -2
- package/dist/{xml-6kdXnoBh.js.map → xml-DXtayJiw.js.map} +1 -1
- package/package.json +9 -1
- package/dist/ead3/index.d.ts.map +0 -1
- package/dist/ead3/index.js.map +0 -1
|
@@ -0,0 +1,1256 @@
|
|
|
1
|
+
import { i as hasFormatErrors, n as formatFail, r as formatOk, t as createFormatIssue } from "../result-Cl7IY6PN.js";
|
|
2
|
+
import "../xml-DXtayJiw.js";
|
|
3
|
+
import { h as validateEad3 } from "../ead3-DM2in-ck.js";
|
|
4
|
+
|
|
5
|
+
//#region src/archive-description/types.ts
|
|
6
|
+
const ARCHIVE_DESCRIPTION_SCHEMA = "archive-description.v1";
|
|
7
|
+
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/archive-description/identity.ts
|
|
10
|
+
const ARCHIVE_DESCRIPTION_SCHEMA_ID = "https://daihum.dev/schema/record-formats/archive-description.v1.json";
|
|
11
|
+
const ARCHIVE_DESCRIPTION_SCHEMA_TITLE = "DAIHUM Archive Description";
|
|
12
|
+
const ARCHIVE_DESCRIPTION_SCHEMA_VERSION = ARCHIVE_DESCRIPTION_SCHEMA;
|
|
13
|
+
function canonicalJson(value) {
|
|
14
|
+
return JSON.stringify(value, (_key, item) => {
|
|
15
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
16
|
+
const record = item;
|
|
17
|
+
return Object.keys(record).sort().reduce((result, key) => {
|
|
18
|
+
result[key] = record[key];
|
|
19
|
+
return result;
|
|
20
|
+
}, {});
|
|
21
|
+
}
|
|
22
|
+
return item;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function contentHash(input) {
|
|
26
|
+
let h1 = 3735928559;
|
|
27
|
+
let h2 = 1103547991;
|
|
28
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
29
|
+
const code = input.charCodeAt(index);
|
|
30
|
+
h1 = Math.imul(h1 ^ code, 2654435761);
|
|
31
|
+
h2 = Math.imul(h2 ^ code, 1597334677);
|
|
32
|
+
}
|
|
33
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
|
|
34
|
+
h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
35
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
|
|
36
|
+
h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
37
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, "0");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/archive-description/rfc3339.ts
|
|
42
|
+
const RFC3339_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:[Zz]|([+-])(\d{2}):(\d{2}))$/;
|
|
43
|
+
function isLeapYear(year) {
|
|
44
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
45
|
+
}
|
|
46
|
+
function daysInMonth(year, month) {
|
|
47
|
+
if (month === 2) return isLeapYear(year) ? 29 : 28;
|
|
48
|
+
return [
|
|
49
|
+
4,
|
|
50
|
+
6,
|
|
51
|
+
9,
|
|
52
|
+
11
|
|
53
|
+
].includes(month) ? 30 : 31;
|
|
54
|
+
}
|
|
55
|
+
function isValidLeapSecond(year, month, day, hour, minute, offsetSign, offsetHour, offsetMinute) {
|
|
56
|
+
const signedOffset = offsetSign === "-" ? -1 : 1;
|
|
57
|
+
const utc = /* @__PURE__ */ new Date(Date.UTC(year, month - 1, day, hour, minute, 59) - signedOffset * (offsetHour * 60 + offsetMinute) * 6e4);
|
|
58
|
+
return utc.getUTCHours() === 23 && utc.getUTCMinutes() === 59 && (utc.getUTCMonth() === 5 && utc.getUTCDate() === 30 || utc.getUTCMonth() === 11 && utc.getUTCDate() === 31);
|
|
59
|
+
}
|
|
60
|
+
/** Strict RFC 3339 date-time validation, including calendar and leap-second checks. */
|
|
61
|
+
function isRfc3339DateTime(value) {
|
|
62
|
+
const match = RFC3339_DATE_TIME.exec(value);
|
|
63
|
+
if (!match) return false;
|
|
64
|
+
const year = Number(match[1]);
|
|
65
|
+
const month = Number(match[2]);
|
|
66
|
+
const day = Number(match[3]);
|
|
67
|
+
const hour = Number(match[4]);
|
|
68
|
+
const minute = Number(match[5]);
|
|
69
|
+
const second = Number(match[6]);
|
|
70
|
+
const offsetHour = Number(match[8] ?? 0);
|
|
71
|
+
const offsetMinute = Number(match[9] ?? 0);
|
|
72
|
+
if (year < 1 || month < 1 || month > 12) return false;
|
|
73
|
+
if (day < 1 || day > daysInMonth(year, month)) return false;
|
|
74
|
+
if (hour > 23 || minute > 59 || offsetHour > 23 || offsetMinute > 59) return false;
|
|
75
|
+
if (second < 60) return true;
|
|
76
|
+
if (second > 60) return false;
|
|
77
|
+
return isValidLeapSecond(year, month, day, hour, minute, match[7], offsetHour, offsetMinute);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/archive-description/archive-description.ts
|
|
82
|
+
const ARCHIVE_DESCRIPTION_FORMAT_ID = "archive-description";
|
|
83
|
+
function isRecord(value) {
|
|
84
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
85
|
+
}
|
|
86
|
+
function isNonEmptyString(value) {
|
|
87
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
88
|
+
}
|
|
89
|
+
function isDateTimeString(value) {
|
|
90
|
+
return typeof value === "string" && isRfc3339DateTime(value);
|
|
91
|
+
}
|
|
92
|
+
function validateAllowedKeys(value, allowed, path, issues) {
|
|
93
|
+
for (const key of Object.keys(value)) if (!allowed.has(key)) issues.push(issue({
|
|
94
|
+
code: "archive.property.unknown",
|
|
95
|
+
path: path ? `${path}.${key}` : key,
|
|
96
|
+
message: `Unknown archive-description property "${key}".`
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
function validateOptionalStrings(value, fields, path, issues) {
|
|
100
|
+
for (const field of fields) if (value[field] !== void 0 && typeof value[field] !== "string") issues.push(issue({
|
|
101
|
+
code: "archive.string.invalid",
|
|
102
|
+
path: path ? `${path}.${field}` : field,
|
|
103
|
+
message: `${path ? `${path}.` : ""}${field} must be a string.`
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
function issue(params) {
|
|
107
|
+
return createFormatIssue({
|
|
108
|
+
severity: params.severity ?? "error",
|
|
109
|
+
format: ARCHIVE_DESCRIPTION_FORMAT_ID,
|
|
110
|
+
code: params.code,
|
|
111
|
+
path: params.path,
|
|
112
|
+
message: params.message,
|
|
113
|
+
...params.cause !== void 0 ? { cause: params.cause } : {}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function validateStringArray(value, path, issues) {
|
|
117
|
+
if (value === void 0) return;
|
|
118
|
+
if (!Array.isArray(value)) {
|
|
119
|
+
issues.push(issue({
|
|
120
|
+
code: "archive.array.required",
|
|
121
|
+
path,
|
|
122
|
+
message: `${path} must be an array.`
|
|
123
|
+
}));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
value.forEach((item, index) => {
|
|
127
|
+
if (!isNonEmptyString(item)) issues.push(issue({
|
|
128
|
+
code: "archive.string.required",
|
|
129
|
+
path: `${path}.${index}`,
|
|
130
|
+
message: `${path}.${index} must be a non-empty string.`
|
|
131
|
+
}));
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function validateProvenance(value, path, issues) {
|
|
135
|
+
if (!isRecord(value)) {
|
|
136
|
+
issues.push(issue({
|
|
137
|
+
code: "archive.provenance.required",
|
|
138
|
+
path,
|
|
139
|
+
message: `${path} is required.`
|
|
140
|
+
}));
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
validateAllowedKeys(value, new Set([
|
|
144
|
+
"createdBy",
|
|
145
|
+
"createdAt",
|
|
146
|
+
"method",
|
|
147
|
+
"rules",
|
|
148
|
+
"tool",
|
|
149
|
+
"model",
|
|
150
|
+
"reviewStatus",
|
|
151
|
+
"reviewedBy",
|
|
152
|
+
"reviewedAt"
|
|
153
|
+
]), path, issues);
|
|
154
|
+
for (const field of [
|
|
155
|
+
"createdBy",
|
|
156
|
+
"createdAt",
|
|
157
|
+
"method"
|
|
158
|
+
]) if (!isNonEmptyString(value[field])) issues.push(issue({
|
|
159
|
+
code: `archive.provenance.${field}.required`,
|
|
160
|
+
path: `${path}.${field}`,
|
|
161
|
+
message: `${path}.${field} is required.`
|
|
162
|
+
}));
|
|
163
|
+
if (isNonEmptyString(value["createdAt"]) && !isDateTimeString(value["createdAt"])) issues.push(issue({
|
|
164
|
+
code: "archive.provenance.createdAt.invalid",
|
|
165
|
+
path: `${path}.createdAt`,
|
|
166
|
+
message: `${path}.createdAt must be an RFC 3339 date-time.`
|
|
167
|
+
}));
|
|
168
|
+
validateStringArray(value["rules"], `${path}.rules`, issues);
|
|
169
|
+
validateOptionalStrings(value, [
|
|
170
|
+
"tool",
|
|
171
|
+
"model",
|
|
172
|
+
"reviewedBy",
|
|
173
|
+
"reviewedAt"
|
|
174
|
+
], path, issues);
|
|
175
|
+
if (value["reviewedAt"] !== void 0 && !isDateTimeString(value["reviewedAt"])) issues.push(issue({
|
|
176
|
+
code: "archive.provenance.reviewedAt.invalid",
|
|
177
|
+
path: `${path}.reviewedAt`,
|
|
178
|
+
message: `${path}.reviewedAt must be an RFC 3339 date-time.`
|
|
179
|
+
}));
|
|
180
|
+
if (value["reviewStatus"] !== void 0 && ![
|
|
181
|
+
"unreviewed",
|
|
182
|
+
"needs_review",
|
|
183
|
+
"accepted",
|
|
184
|
+
"rejected"
|
|
185
|
+
].includes(String(value["reviewStatus"]))) issues.push(issue({
|
|
186
|
+
code: "archive.provenance.reviewStatus.invalid",
|
|
187
|
+
path: `${path}.reviewStatus`,
|
|
188
|
+
message: "Provenance reviewStatus is invalid."
|
|
189
|
+
}));
|
|
190
|
+
if (isNonEmptyString(value["method"]) && value["method"].startsWith("model:") && !isNonEmptyString(value["model"])) issues.push(issue({
|
|
191
|
+
severity: "warning",
|
|
192
|
+
code: "archive.provenance.model.recommended",
|
|
193
|
+
path: `${path}.model`,
|
|
194
|
+
message: "Machine-generated descriptions should identify the model."
|
|
195
|
+
}));
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
function validateExtensions(value, path, issues) {
|
|
199
|
+
if (value === void 0) return;
|
|
200
|
+
if (!isRecord(value)) {
|
|
201
|
+
issues.push(issue({
|
|
202
|
+
code: "archive.extensions.invalid",
|
|
203
|
+
path,
|
|
204
|
+
message: `${path} must be an object.`
|
|
205
|
+
}));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
validateAllowedKeys(value, new Set(["ead3", "vendor"]), path, issues);
|
|
209
|
+
if (value["ead3"] !== void 0 && !isRecord(value["ead3"])) issues.push(issue({
|
|
210
|
+
code: "archive.extensions.value.invalid",
|
|
211
|
+
path: `${path}.ead3`,
|
|
212
|
+
message: `${path}.ead3 must be an object.`
|
|
213
|
+
}));
|
|
214
|
+
if (value["vendor"] !== void 0) if (!Array.isArray(value["vendor"])) issues.push(issue({
|
|
215
|
+
code: "archive.extensions.value.invalid",
|
|
216
|
+
path: `${path}.vendor`,
|
|
217
|
+
message: `${path}.vendor must be an array.`
|
|
218
|
+
}));
|
|
219
|
+
else value["vendor"].forEach((extension, index) => {
|
|
220
|
+
const extensionPath = `${path}.vendor.${index}`;
|
|
221
|
+
if (!isRecord(extension)) {
|
|
222
|
+
issues.push(issue({
|
|
223
|
+
code: "archive.vendorExtension.invalid",
|
|
224
|
+
path: extensionPath,
|
|
225
|
+
message: "Vendor extension must be an object."
|
|
226
|
+
}));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
validateAllowedKeys(extension, new Set([
|
|
230
|
+
"namespace",
|
|
231
|
+
"name",
|
|
232
|
+
"value"
|
|
233
|
+
]), extensionPath, issues);
|
|
234
|
+
for (const field of ["namespace", "name"]) if (!isNonEmptyString(extension[field])) issues.push(issue({
|
|
235
|
+
code: `archive.vendorExtension.${field}.required`,
|
|
236
|
+
path: `${extensionPath}.${field}`,
|
|
237
|
+
message: `Vendor extension ${field} is required.`
|
|
238
|
+
}));
|
|
239
|
+
if (!Object.prototype.hasOwnProperty.call(extension, "value")) issues.push(issue({
|
|
240
|
+
code: "archive.vendorExtension.value.required",
|
|
241
|
+
path: `${extensionPath}.value`,
|
|
242
|
+
message: "Vendor extension value is required."
|
|
243
|
+
}));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function validateSourceRecords(value, path, issues) {
|
|
247
|
+
if (value === void 0) return;
|
|
248
|
+
if (!Array.isArray(value)) {
|
|
249
|
+
issues.push(issue({
|
|
250
|
+
code: "archive.sourceRecords.invalid",
|
|
251
|
+
path,
|
|
252
|
+
message: `${path} must be an array.`
|
|
253
|
+
}));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
value.forEach((sourceRecord, index) => {
|
|
257
|
+
const itemPath = `${path}.${index}`;
|
|
258
|
+
if (!isRecord(sourceRecord)) {
|
|
259
|
+
issues.push(issue({
|
|
260
|
+
code: "archive.sourceRecord.invalid",
|
|
261
|
+
path: itemPath,
|
|
262
|
+
message: `${itemPath} must be an object.`
|
|
263
|
+
}));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
validateAllowedKeys(sourceRecord, new Set([
|
|
267
|
+
"id",
|
|
268
|
+
"producer",
|
|
269
|
+
"format",
|
|
270
|
+
"source",
|
|
271
|
+
"retrievedAt",
|
|
272
|
+
"raw",
|
|
273
|
+
"mappedFields",
|
|
274
|
+
"unmappedFields",
|
|
275
|
+
"diagnostics"
|
|
276
|
+
]), itemPath, issues);
|
|
277
|
+
for (const field of ["id", "producer"]) if (!isNonEmptyString(sourceRecord[field])) issues.push(issue({
|
|
278
|
+
code: `archive.sourceRecord.${field}.required`,
|
|
279
|
+
path: `${itemPath}.${field}`,
|
|
280
|
+
message: `${itemPath}.${field} is required.`
|
|
281
|
+
}));
|
|
282
|
+
validateOptionalStrings(sourceRecord, [
|
|
283
|
+
"format",
|
|
284
|
+
"source",
|
|
285
|
+
"retrievedAt"
|
|
286
|
+
], itemPath, issues);
|
|
287
|
+
if (sourceRecord["retrievedAt"] !== void 0 && !isDateTimeString(sourceRecord["retrievedAt"])) issues.push(issue({
|
|
288
|
+
code: "archive.sourceRecord.retrievedAt.invalid",
|
|
289
|
+
path: `${itemPath}.retrievedAt`,
|
|
290
|
+
message: `${itemPath}.retrievedAt must be an RFC 3339 date-time.`
|
|
291
|
+
}));
|
|
292
|
+
validateStringArray(sourceRecord["mappedFields"], `${itemPath}.mappedFields`, issues);
|
|
293
|
+
if (sourceRecord["diagnostics"] !== void 0) if (!Array.isArray(sourceRecord["diagnostics"])) issues.push(issue({
|
|
294
|
+
code: "archive.sourceRecord.diagnostics.invalid",
|
|
295
|
+
path: `${itemPath}.diagnostics`,
|
|
296
|
+
message: "Source diagnostics must be an array."
|
|
297
|
+
}));
|
|
298
|
+
else sourceRecord["diagnostics"].forEach((diagnostic, diagnosticIndex) => {
|
|
299
|
+
const diagnosticPath = `${itemPath}.diagnostics.${diagnosticIndex}`;
|
|
300
|
+
if (!isRecord(diagnostic)) {
|
|
301
|
+
issues.push(issue({
|
|
302
|
+
code: "archive.sourceDiagnostic.invalid",
|
|
303
|
+
path: diagnosticPath,
|
|
304
|
+
message: "Source diagnostic must be an object."
|
|
305
|
+
}));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
validateAllowedKeys(diagnostic, new Set([
|
|
309
|
+
"severity",
|
|
310
|
+
"code",
|
|
311
|
+
"message",
|
|
312
|
+
"path"
|
|
313
|
+
]), diagnosticPath, issues);
|
|
314
|
+
if (![
|
|
315
|
+
"error",
|
|
316
|
+
"warning",
|
|
317
|
+
"info"
|
|
318
|
+
].includes(String(diagnostic["severity"]))) issues.push(issue({
|
|
319
|
+
code: "archive.sourceDiagnostic.severity.invalid",
|
|
320
|
+
path: `${diagnosticPath}.severity`,
|
|
321
|
+
message: "Source diagnostic severity is invalid."
|
|
322
|
+
}));
|
|
323
|
+
for (const field of ["code", "message"]) if (!isNonEmptyString(diagnostic[field])) issues.push(issue({
|
|
324
|
+
code: `archive.sourceDiagnostic.${field}.required`,
|
|
325
|
+
path: `${diagnosticPath}.${field}`,
|
|
326
|
+
message: `Source diagnostic ${field} is required.`
|
|
327
|
+
}));
|
|
328
|
+
validateStringArray(diagnostic["path"], `${diagnosticPath}.path`, issues);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function validateNode(value, path, issues, nodeIds, seenObjects) {
|
|
333
|
+
if (!isRecord(value)) {
|
|
334
|
+
issues.push(issue({
|
|
335
|
+
code: "archive.node.required",
|
|
336
|
+
path,
|
|
337
|
+
message: `${path} must be an object.`
|
|
338
|
+
}));
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
if (seenObjects.has(value)) {
|
|
342
|
+
issues.push(issue({
|
|
343
|
+
code: "archive.node.cycle",
|
|
344
|
+
path,
|
|
345
|
+
message: "Archive description nodes must not form cycles."
|
|
346
|
+
}));
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
seenObjects.add(value);
|
|
350
|
+
validateAllowedKeys(value, new Set([
|
|
351
|
+
"id",
|
|
352
|
+
"level",
|
|
353
|
+
"otherLevel",
|
|
354
|
+
"title",
|
|
355
|
+
"referenceCode",
|
|
356
|
+
"repository",
|
|
357
|
+
"dates",
|
|
358
|
+
"extent",
|
|
359
|
+
"creator",
|
|
360
|
+
"creatorStatus",
|
|
361
|
+
"abstract",
|
|
362
|
+
"scopeContent",
|
|
363
|
+
"arrangement",
|
|
364
|
+
"accessRestriction",
|
|
365
|
+
"useRestriction",
|
|
366
|
+
"language",
|
|
367
|
+
"biographicalHistory",
|
|
368
|
+
"custodialHistory",
|
|
369
|
+
"acquisitionInformation",
|
|
370
|
+
"appraisal",
|
|
371
|
+
"processingInformation",
|
|
372
|
+
"relatedMaterial",
|
|
373
|
+
"separatedMaterial",
|
|
374
|
+
"preferredCitation",
|
|
375
|
+
"notes",
|
|
376
|
+
"accessPoints",
|
|
377
|
+
"targets",
|
|
378
|
+
"provenance",
|
|
379
|
+
"subItems",
|
|
380
|
+
"extensions"
|
|
381
|
+
]), path, issues);
|
|
382
|
+
validateOptionalStrings(value, [
|
|
383
|
+
"otherLevel",
|
|
384
|
+
"referenceCode",
|
|
385
|
+
"repository",
|
|
386
|
+
"extent",
|
|
387
|
+
"creator",
|
|
388
|
+
"abstract",
|
|
389
|
+
"scopeContent",
|
|
390
|
+
"arrangement",
|
|
391
|
+
"accessRestriction",
|
|
392
|
+
"useRestriction",
|
|
393
|
+
"language",
|
|
394
|
+
"biographicalHistory",
|
|
395
|
+
"custodialHistory",
|
|
396
|
+
"acquisitionInformation",
|
|
397
|
+
"appraisal",
|
|
398
|
+
"processingInformation",
|
|
399
|
+
"relatedMaterial",
|
|
400
|
+
"separatedMaterial",
|
|
401
|
+
"preferredCitation"
|
|
402
|
+
], path, issues);
|
|
403
|
+
const id = value["id"];
|
|
404
|
+
if (!isNonEmptyString(id)) issues.push(issue({
|
|
405
|
+
code: "archive.node.id.required",
|
|
406
|
+
path: `${path}.id`,
|
|
407
|
+
message: "Node id is required."
|
|
408
|
+
}));
|
|
409
|
+
else if (nodeIds.has(id)) issues.push(issue({
|
|
410
|
+
code: "archive.node.id.duplicate",
|
|
411
|
+
path: `${path}.id`,
|
|
412
|
+
message: `Node id "${id}" is duplicated.`
|
|
413
|
+
}));
|
|
414
|
+
else nodeIds.add(id);
|
|
415
|
+
const levels = new Set([
|
|
416
|
+
"collection",
|
|
417
|
+
"series",
|
|
418
|
+
"subseries",
|
|
419
|
+
"file",
|
|
420
|
+
"item",
|
|
421
|
+
"otherlevel"
|
|
422
|
+
]);
|
|
423
|
+
if (!isNonEmptyString(value["level"]) || !levels.has(value["level"])) issues.push(issue({
|
|
424
|
+
code: "archive.node.level.invalid",
|
|
425
|
+
path: `${path}.level`,
|
|
426
|
+
message: "Node level must be a supported archival description level."
|
|
427
|
+
}));
|
|
428
|
+
if (value["level"] === "otherlevel" && !isNonEmptyString(value["otherLevel"])) issues.push(issue({
|
|
429
|
+
code: "archive.node.otherLevel.required",
|
|
430
|
+
path: `${path}.otherLevel`,
|
|
431
|
+
message: "otherLevel is required when level is otherlevel."
|
|
432
|
+
}));
|
|
433
|
+
if (value["creatorStatus"] !== void 0 && ![
|
|
434
|
+
"known",
|
|
435
|
+
"unknown",
|
|
436
|
+
"not_applicable"
|
|
437
|
+
].includes(String(value["creatorStatus"]))) issues.push(issue({
|
|
438
|
+
code: "archive.node.creatorStatus.invalid",
|
|
439
|
+
path: `${path}.creatorStatus`,
|
|
440
|
+
message: "Node creatorStatus is invalid."
|
|
441
|
+
}));
|
|
442
|
+
if (!isNonEmptyString(value["title"])) issues.push(issue({
|
|
443
|
+
code: "archive.node.title.required",
|
|
444
|
+
path: `${path}.title`,
|
|
445
|
+
message: "Node title is required."
|
|
446
|
+
}));
|
|
447
|
+
if (value["dates"] !== void 0) if (!Array.isArray(value["dates"])) issues.push(issue({
|
|
448
|
+
code: "archive.node.dates.invalid",
|
|
449
|
+
path: `${path}.dates`,
|
|
450
|
+
message: "Node dates must be an array."
|
|
451
|
+
}));
|
|
452
|
+
else value["dates"].forEach((date, index) => {
|
|
453
|
+
if (!isRecord(date) || !isNonEmptyString(date["expression"])) issues.push(issue({
|
|
454
|
+
code: "archive.node.date.expression.required",
|
|
455
|
+
path: `${path}.dates.${index}.expression`,
|
|
456
|
+
message: "Each date must have a non-empty expression."
|
|
457
|
+
}));
|
|
458
|
+
else {
|
|
459
|
+
validateAllowedKeys(date, new Set([
|
|
460
|
+
"expression",
|
|
461
|
+
"normalized",
|
|
462
|
+
"type",
|
|
463
|
+
"certainty"
|
|
464
|
+
]), `${path}.dates.${index}`, issues);
|
|
465
|
+
validateOptionalStrings(date, [
|
|
466
|
+
"normalized",
|
|
467
|
+
"type",
|
|
468
|
+
"certainty"
|
|
469
|
+
], `${path}.dates.${index}`, issues);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
if (value["accessPoints"] !== void 0) if (!Array.isArray(value["accessPoints"])) issues.push(issue({
|
|
473
|
+
code: "archive.node.accessPoints.invalid",
|
|
474
|
+
path: `${path}.accessPoints`,
|
|
475
|
+
message: "Node accessPoints must be an array."
|
|
476
|
+
}));
|
|
477
|
+
else value["accessPoints"].forEach((accessPoint, index) => {
|
|
478
|
+
if (!isRecord(accessPoint) || !isNonEmptyString(accessPoint["type"]) || !isNonEmptyString(accessPoint["label"])) issues.push(issue({
|
|
479
|
+
code: "archive.node.accessPoint.invalid",
|
|
480
|
+
path: `${path}.accessPoints.${index}`,
|
|
481
|
+
message: "Each access point must have a non-empty type and label."
|
|
482
|
+
}));
|
|
483
|
+
else {
|
|
484
|
+
validateAllowedKeys(accessPoint, new Set([
|
|
485
|
+
"type",
|
|
486
|
+
"label",
|
|
487
|
+
"identifier",
|
|
488
|
+
"source"
|
|
489
|
+
]), `${path}.accessPoints.${index}`, issues);
|
|
490
|
+
validateOptionalStrings(accessPoint, ["identifier", "source"], `${path}.accessPoints.${index}`, issues);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
if (value["targets"] !== void 0) if (!Array.isArray(value["targets"])) issues.push(issue({
|
|
494
|
+
code: "archive.node.targets.invalid",
|
|
495
|
+
path: `${path}.targets`,
|
|
496
|
+
message: "Node targets must be an array."
|
|
497
|
+
}));
|
|
498
|
+
else value["targets"].forEach((target, index) => {
|
|
499
|
+
if (!isRecord(target) || !isNonEmptyString(target["kind"]) || !isNonEmptyString(target["id"])) issues.push(issue({
|
|
500
|
+
code: "archive.node.target.invalid",
|
|
501
|
+
path: `${path}.targets.${index}`,
|
|
502
|
+
message: "Each target must have a non-empty kind and id."
|
|
503
|
+
}));
|
|
504
|
+
else {
|
|
505
|
+
validateAllowedKeys(target, new Set([
|
|
506
|
+
"kind",
|
|
507
|
+
"id",
|
|
508
|
+
"href",
|
|
509
|
+
"label",
|
|
510
|
+
"role",
|
|
511
|
+
"locator",
|
|
512
|
+
"metadata"
|
|
513
|
+
]), `${path}.targets.${index}`, issues);
|
|
514
|
+
validateOptionalStrings(target, [
|
|
515
|
+
"href",
|
|
516
|
+
"label",
|
|
517
|
+
"role"
|
|
518
|
+
], `${path}.targets.${index}`, issues);
|
|
519
|
+
for (const field of ["locator", "metadata"]) if (target[field] !== void 0 && !isRecord(target[field])) issues.push(issue({
|
|
520
|
+
code: "archive.node.target.object.invalid",
|
|
521
|
+
path: `${path}.targets.${index}.${field}`,
|
|
522
|
+
message: `Target ${field} must be an object.`
|
|
523
|
+
}));
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
if (value["provenance"] !== void 0) validateProvenance(value["provenance"], `${path}.provenance`, issues);
|
|
527
|
+
validateStringArray(value["notes"], `${path}.notes`, issues);
|
|
528
|
+
validateExtensions(value["extensions"], `${path}.extensions`, issues);
|
|
529
|
+
if (value["subItems"] !== void 0) if (!Array.isArray(value["subItems"])) issues.push(issue({
|
|
530
|
+
code: "archive.node.subItems.invalid",
|
|
531
|
+
path: `${path}.subItems`,
|
|
532
|
+
message: "Node subItems must be an array."
|
|
533
|
+
}));
|
|
534
|
+
else value["subItems"].forEach((child, index) => {
|
|
535
|
+
validateNode(child, `${path}.subItems.${index}`, issues, nodeIds, seenObjects);
|
|
536
|
+
});
|
|
537
|
+
seenObjects.delete(value);
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
function validateArchiveDescription(value) {
|
|
541
|
+
const issues = [];
|
|
542
|
+
if (!isRecord(value)) return [issue({
|
|
543
|
+
code: "archive.record.required",
|
|
544
|
+
path: "",
|
|
545
|
+
message: "Archive description record must be an object."
|
|
546
|
+
})];
|
|
547
|
+
validateAllowedKeys(value, new Set([
|
|
548
|
+
"schema",
|
|
549
|
+
"recordId",
|
|
550
|
+
"title",
|
|
551
|
+
"maintenanceStatus",
|
|
552
|
+
"language",
|
|
553
|
+
"agencyName",
|
|
554
|
+
"description",
|
|
555
|
+
"provenance",
|
|
556
|
+
"sourceRecords",
|
|
557
|
+
"extensions"
|
|
558
|
+
]), "", issues);
|
|
559
|
+
if (value["schema"] !== ARCHIVE_DESCRIPTION_SCHEMA) issues.push(issue({
|
|
560
|
+
code: "archive.schema.invalid",
|
|
561
|
+
path: "schema",
|
|
562
|
+
message: `Archive description schema must be ${ARCHIVE_DESCRIPTION_SCHEMA}.`
|
|
563
|
+
}));
|
|
564
|
+
if (!isNonEmptyString(value["recordId"])) issues.push(issue({
|
|
565
|
+
code: "archive.recordId.required",
|
|
566
|
+
path: "recordId",
|
|
567
|
+
message: "Record id is required."
|
|
568
|
+
}));
|
|
569
|
+
if (!isNonEmptyString(value["title"])) issues.push(issue({
|
|
570
|
+
code: "archive.title.required",
|
|
571
|
+
path: "title",
|
|
572
|
+
message: "Finding-aid title is required."
|
|
573
|
+
}));
|
|
574
|
+
validateOptionalStrings(value, ["language", "agencyName"], "", issues);
|
|
575
|
+
if (value["maintenanceStatus"] !== void 0 && ![
|
|
576
|
+
"new",
|
|
577
|
+
"revised",
|
|
578
|
+
"deleted",
|
|
579
|
+
"derived",
|
|
580
|
+
"unknown"
|
|
581
|
+
].includes(String(value["maintenanceStatus"]))) issues.push(issue({
|
|
582
|
+
code: "archive.maintenanceStatus.invalid",
|
|
583
|
+
path: "maintenanceStatus",
|
|
584
|
+
message: "Maintenance status is invalid."
|
|
585
|
+
}));
|
|
586
|
+
validateProvenance(value["provenance"], "provenance", issues);
|
|
587
|
+
validateSourceRecords(value["sourceRecords"], "sourceRecords", issues);
|
|
588
|
+
validateExtensions(value["extensions"], "extensions", issues);
|
|
589
|
+
validateNode(value["description"], "description", issues, /* @__PURE__ */ new Set(), /* @__PURE__ */ new WeakSet());
|
|
590
|
+
if (isRecord(value["description"]) && value["description"]["level"] !== "collection") issues.push(issue({
|
|
591
|
+
code: "archive.root.level.collection.required",
|
|
592
|
+
path: "description.level",
|
|
593
|
+
message: "The root description node must have collection level."
|
|
594
|
+
}));
|
|
595
|
+
return issues;
|
|
596
|
+
}
|
|
597
|
+
function parseArchiveDescriptionJson(input) {
|
|
598
|
+
let value;
|
|
599
|
+
try {
|
|
600
|
+
value = JSON.parse(input);
|
|
601
|
+
} catch (cause) {
|
|
602
|
+
return formatFail([issue({
|
|
603
|
+
code: "archive.json.invalid",
|
|
604
|
+
path: "",
|
|
605
|
+
message: "Archive description JSON is invalid.",
|
|
606
|
+
cause
|
|
607
|
+
})]);
|
|
608
|
+
}
|
|
609
|
+
const issues = validateArchiveDescription(value);
|
|
610
|
+
return hasFormatErrors(issues) ? formatFail(issues, value) : formatOk(value, issues);
|
|
611
|
+
}
|
|
612
|
+
function canonicalize(value) {
|
|
613
|
+
return JSON.parse(canonicalJson(value));
|
|
614
|
+
}
|
|
615
|
+
function serializeArchiveDescriptionJson(record, options = {}) {
|
|
616
|
+
const issues = validateArchiveDescription(record);
|
|
617
|
+
if (hasFormatErrors(issues)) return formatFail(issues);
|
|
618
|
+
const canonical = canonicalize(record);
|
|
619
|
+
const format = options.format ?? true;
|
|
620
|
+
return formatOk(JSON.stringify(canonical, null, format ? 2 : void 0), issues);
|
|
621
|
+
}
|
|
622
|
+
const archiveDescriptionAdapter = {
|
|
623
|
+
format: ARCHIVE_DESCRIPTION_FORMAT_ID,
|
|
624
|
+
parse: parseArchiveDescriptionJson,
|
|
625
|
+
serialize: serializeArchiveDescriptionJson,
|
|
626
|
+
validate: validateArchiveDescription,
|
|
627
|
+
detect(input) {
|
|
628
|
+
try {
|
|
629
|
+
return JSON.parse(input)?.schema === ARCHIVE_DESCRIPTION_SCHEMA;
|
|
630
|
+
} catch {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/archive-description/dacs.ts
|
|
638
|
+
const DACS_PROFILE_ID = "daihum.record-formats.archive-description.dacs.v1";
|
|
639
|
+
function text(value) {
|
|
640
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
641
|
+
}
|
|
642
|
+
function dacsIssue(path, code, message) {
|
|
643
|
+
return createFormatIssue({
|
|
644
|
+
severity: "error",
|
|
645
|
+
format: DACS_PROFILE_ID,
|
|
646
|
+
path,
|
|
647
|
+
code,
|
|
648
|
+
message
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function validateMinimum(node, path, repositoryFallback) {
|
|
652
|
+
const issues = [];
|
|
653
|
+
if (!text(node.referenceCode)) issues.push(dacsIssue(`${path}.referenceCode`, "dacs.referenceCode.required", "DACS minimum description requires a reference code."));
|
|
654
|
+
if (!text(node.repository) && !text(repositoryFallback)) issues.push(dacsIssue(`${path}.repository`, "dacs.repository.required", "DACS minimum description requires repository identification."));
|
|
655
|
+
if (!text(node.title)) issues.push(dacsIssue(`${path}.title`, "dacs.title.required", "DACS minimum description requires a title."));
|
|
656
|
+
if (!node.dates?.some((date) => text(date.expression))) issues.push(dacsIssue(`${path}.dates`, "dacs.dates.required", "DACS minimum description requires a date expression."));
|
|
657
|
+
if (!text(node.extent)) issues.push(dacsIssue(`${path}.extent`, "dacs.extent.required", "DACS minimum description requires extent."));
|
|
658
|
+
if (!text(node.creator) && node.creatorStatus !== "unknown" && node.creatorStatus !== "not_applicable") issues.push(dacsIssue(`${path}.creator`, "dacs.creator.required_or_unknown", "DACS minimum description requires a creator or an explicit unknown/not-applicable status."));
|
|
659
|
+
if (!text(node.scopeContent)) issues.push(dacsIssue(`${path}.scopeContent`, "dacs.scopeContent.required", "DACS minimum description requires scope and content."));
|
|
660
|
+
if (!text(node.accessRestriction)) issues.push(dacsIssue(`${path}.accessRestriction`, "dacs.accessRestriction.required", "DACS minimum description requires conditions governing access, including an explicit unrestricted statement."));
|
|
661
|
+
if (!text(node.language)) issues.push(dacsIssue(`${path}.language`, "dacs.language.required", "DACS minimum description requires language/script information."));
|
|
662
|
+
return issues;
|
|
663
|
+
}
|
|
664
|
+
function walk(node, path, issues, options, repositoryFallback) {
|
|
665
|
+
if (options.requireMinimumAtEveryLevel) issues.push(...validateMinimum(node, path, repositoryFallback));
|
|
666
|
+
node.subItems?.forEach((child, index) => {
|
|
667
|
+
walk(child, `${path}.subItems.${index}`, issues, options, repositoryFallback);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
function validateDacsArchiveDescription(record, options = {}) {
|
|
671
|
+
const issues = validateMinimum(record.description, "description", void 0);
|
|
672
|
+
if (record.description.subItems?.length && record.description.level !== "collection") issues.push(dacsIssue("description.level", "dacs.multilevel.root.collection.required", "A multilevel finding aid must begin with a collection-level root."));
|
|
673
|
+
record.description.subItems?.forEach((child, index) => {
|
|
674
|
+
walk(child, `description.subItems.${index}`, issues, options, record.description.repository);
|
|
675
|
+
});
|
|
676
|
+
return issues;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/archive-description/ead3-projection.ts
|
|
681
|
+
const ARCHIVE_DESCRIPTION_EAD3_PROJECTION_ID = "daihum.record-formats.archive-description.ead3.v1";
|
|
682
|
+
const ACCESS_POINT_TYPES = new Set([
|
|
683
|
+
"person",
|
|
684
|
+
"family",
|
|
685
|
+
"organization",
|
|
686
|
+
"place",
|
|
687
|
+
"subject",
|
|
688
|
+
"documentaryForm",
|
|
689
|
+
"occupation",
|
|
690
|
+
"function"
|
|
691
|
+
]);
|
|
692
|
+
function projectionIssue(params) {
|
|
693
|
+
return createFormatIssue({
|
|
694
|
+
severity: params.severity ?? "warning",
|
|
695
|
+
format: ARCHIVE_DESCRIPTION_EAD3_PROJECTION_ID,
|
|
696
|
+
code: params.code,
|
|
697
|
+
path: params.path,
|
|
698
|
+
message: params.message
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
function ead3Extensions(value) {
|
|
702
|
+
return value;
|
|
703
|
+
}
|
|
704
|
+
function dateToEad3(date, path, issues) {
|
|
705
|
+
if (date.certainty) issues.push(projectionIssue({
|
|
706
|
+
code: "archive.ead3.date.certainty.loss",
|
|
707
|
+
path: `${path}.certainty`,
|
|
708
|
+
message: "EAD3 common-element projection does not model date certainty."
|
|
709
|
+
}));
|
|
710
|
+
return {
|
|
711
|
+
expression: date.expression,
|
|
712
|
+
...date.normalized ? { normalized: date.normalized } : {},
|
|
713
|
+
...date.type ? { type: date.type } : {}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function accessPointToEad3(accessPoint, path, issues) {
|
|
717
|
+
if (!ACCESS_POINT_TYPES.has(accessPoint.type)) {
|
|
718
|
+
issues.push(projectionIssue({
|
|
719
|
+
code: "archive.ead3.accessPoint.type.unsupported",
|
|
720
|
+
path: `${path}.type`,
|
|
721
|
+
message: `Access point type "${accessPoint.type}" is not represented by the EAD3 projection.`
|
|
722
|
+
}));
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
type: accessPoint.type,
|
|
727
|
+
label: accessPoint.label,
|
|
728
|
+
...accessPoint.identifier ? { identifier: accessPoint.identifier } : {},
|
|
729
|
+
...accessPoint.source ? { source: accessPoint.source } : {}
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function targetToEad3(target, path, issues) {
|
|
733
|
+
if (!target.href) {
|
|
734
|
+
issues.push(projectionIssue({
|
|
735
|
+
code: "archive.ead3.target.href.required",
|
|
736
|
+
path: `${path}.href`,
|
|
737
|
+
message: "Target has no href and cannot be projected to an EAD3 digital object."
|
|
738
|
+
}));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
href: target.href,
|
|
743
|
+
...target.label ? { label: target.label } : {},
|
|
744
|
+
...target.role ? { role: target.role } : {},
|
|
745
|
+
targetKind: target.kind,
|
|
746
|
+
sourceRef: {
|
|
747
|
+
kind: target.kind,
|
|
748
|
+
id: target.id,
|
|
749
|
+
...target.label ? { label: target.label } : {},
|
|
750
|
+
...target.locator ? { locator: target.locator } : {},
|
|
751
|
+
...target.metadata ? { metadata: target.metadata } : {}
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
function nodeToEad3(node, path, issues) {
|
|
756
|
+
if (node.provenance) issues.push(projectionIssue({
|
|
757
|
+
code: "archive.ead3.node.provenance.loss",
|
|
758
|
+
path: `${path}.provenance`,
|
|
759
|
+
message: "Node provenance remains canonical metadata and is not represented in EAD3."
|
|
760
|
+
}));
|
|
761
|
+
if (node.creatorStatus && node.creatorStatus !== "known") issues.push(projectionIssue({
|
|
762
|
+
code: "archive.ead3.creatorStatus.loss",
|
|
763
|
+
path: `${path}.creatorStatus`,
|
|
764
|
+
message: "Explicit unknown/not-applicable creator status is not represented in EAD3."
|
|
765
|
+
}));
|
|
766
|
+
if (node.extensions?.vendor) issues.push(projectionIssue({
|
|
767
|
+
code: "archive.ead3.vendorExtension.loss",
|
|
768
|
+
path: `${path}.extensions.vendor`,
|
|
769
|
+
message: "Vendor extension facts remain canonical and are not represented in EAD3."
|
|
770
|
+
}));
|
|
771
|
+
const accessPoints = node.accessPoints?.map((value, index) => accessPointToEad3(value, `${path}.accessPoints.${index}`, issues)).filter((value) => Boolean(value));
|
|
772
|
+
const digitalObjects = node.targets?.map((value, index) => targetToEad3(value, `${path}.targets.${index}`, issues)).filter((value) => Boolean(value));
|
|
773
|
+
const extensions = ead3Extensions(node.extensions?.ead3);
|
|
774
|
+
return {
|
|
775
|
+
id: node.id,
|
|
776
|
+
level: node.level,
|
|
777
|
+
...node.otherLevel ? { otherLevel: node.otherLevel } : {},
|
|
778
|
+
title: node.title,
|
|
779
|
+
...node.referenceCode ? { referenceCode: node.referenceCode } : {},
|
|
780
|
+
...node.repository ? { repository: node.repository } : {},
|
|
781
|
+
...node.dates?.length ? { dates: node.dates.map((date, index) => dateToEad3(date, `${path}.dates.${index}`, issues)) } : {},
|
|
782
|
+
...node.extent ? { extent: node.extent } : {},
|
|
783
|
+
...node.creator ? { creator: node.creator } : {},
|
|
784
|
+
...node.abstract ? { abstract: node.abstract } : {},
|
|
785
|
+
...node.scopeContent ? { scopeContent: node.scopeContent } : {},
|
|
786
|
+
...node.arrangement ? { arrangement: node.arrangement } : {},
|
|
787
|
+
...node.accessRestriction ? { accessRestriction: node.accessRestriction } : {},
|
|
788
|
+
...node.useRestriction ? { useRestriction: node.useRestriction } : {},
|
|
789
|
+
...node.language ? { language: node.language } : {},
|
|
790
|
+
...node.biographicalHistory ? { biographicalHistory: node.biographicalHistory } : {},
|
|
791
|
+
...node.custodialHistory ? { custodialHistory: node.custodialHistory } : {},
|
|
792
|
+
...node.acquisitionInformation ? { acquisitionInformation: node.acquisitionInformation } : {},
|
|
793
|
+
...node.appraisal ? { appraisal: node.appraisal } : {},
|
|
794
|
+
...node.processingInformation ? { processingInformation: node.processingInformation } : {},
|
|
795
|
+
...node.relatedMaterial ? { relatedMaterial: node.relatedMaterial } : {},
|
|
796
|
+
...node.separatedMaterial ? { separatedMaterial: node.separatedMaterial } : {},
|
|
797
|
+
...node.preferredCitation ? { preferredCitation: node.preferredCitation } : {},
|
|
798
|
+
...node.notes?.length ? { notes: node.notes } : {},
|
|
799
|
+
...accessPoints?.length ? { accessPoints } : {},
|
|
800
|
+
...digitalObjects?.length ? { digitalObjects } : {},
|
|
801
|
+
...node.subItems?.length ? { components: node.subItems.map((child, index) => nodeToEad3(child, `${path}.subItems.${index}`, issues)) } : {},
|
|
802
|
+
...extensions ? { extensions } : {}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function archiveDescriptionToEad3(record) {
|
|
806
|
+
const issues = [...validateArchiveDescription(record)];
|
|
807
|
+
if (hasFormatErrors(issues)) return formatFail(issues);
|
|
808
|
+
issues.push(projectionIssue({
|
|
809
|
+
code: "archive.ead3.provenance.loss",
|
|
810
|
+
path: "provenance",
|
|
811
|
+
message: "Record provenance remains canonical metadata and is not represented in EAD3."
|
|
812
|
+
}));
|
|
813
|
+
if (record.sourceRecords?.length) issues.push(projectionIssue({
|
|
814
|
+
code: "archive.ead3.sourceRecords.loss",
|
|
815
|
+
path: "sourceRecords",
|
|
816
|
+
message: "Source records remain canonical provenance and are not represented in EAD3."
|
|
817
|
+
}));
|
|
818
|
+
if (record.extensions?.vendor) issues.push(projectionIssue({
|
|
819
|
+
code: "archive.ead3.vendorExtension.loss",
|
|
820
|
+
path: "extensions.vendor",
|
|
821
|
+
message: "Vendor extension facts remain canonical and are not represented in EAD3."
|
|
822
|
+
}));
|
|
823
|
+
const extensions = ead3Extensions(record.extensions?.ead3);
|
|
824
|
+
const findingAid = {
|
|
825
|
+
recordId: record.recordId,
|
|
826
|
+
title: record.title,
|
|
827
|
+
...record.maintenanceStatus ? { maintenanceStatus: record.maintenanceStatus } : {},
|
|
828
|
+
...record.language ? { language: record.language } : {},
|
|
829
|
+
...record.agencyName ? { agencyName: record.agencyName } : {},
|
|
830
|
+
description: nodeToEad3(record.description, "description", issues),
|
|
831
|
+
...extensions ? { extensions } : {}
|
|
832
|
+
};
|
|
833
|
+
issues.push(...validateEad3(findingAid));
|
|
834
|
+
return hasFormatErrors(issues) ? formatFail(issues, findingAid) : formatOk(findingAid, issues);
|
|
835
|
+
}
|
|
836
|
+
function targetFromEad3(target, path, issues) {
|
|
837
|
+
const label = target.label ?? target.sourceRef?.label;
|
|
838
|
+
if (target.hrefPolicy) issues.push(projectionIssue({
|
|
839
|
+
code: "ead3.archive.target.hrefPolicy.loss",
|
|
840
|
+
path: `${path}.hrefPolicy`,
|
|
841
|
+
message: "EAD3 href policy is not modeled by the canonical target reference."
|
|
842
|
+
}));
|
|
843
|
+
if (target.provenance) issues.push(projectionIssue({
|
|
844
|
+
code: "ead3.archive.target.provenance.loss",
|
|
845
|
+
path: `${path}.provenance`,
|
|
846
|
+
message: "EAD3 target provenance is not modeled by the canonical target reference."
|
|
847
|
+
}));
|
|
848
|
+
if (target.extensions) issues.push(projectionIssue({
|
|
849
|
+
code: "ead3.archive.target.extensions.loss",
|
|
850
|
+
path: `${path}.extensions`,
|
|
851
|
+
message: "EAD3 target XML extensions are not modeled by the canonical target reference."
|
|
852
|
+
}));
|
|
853
|
+
if (target.sourceRef?.label && target.label && target.sourceRef.label !== target.label) issues.push(projectionIssue({
|
|
854
|
+
code: "ead3.archive.target.sourceLabel.loss",
|
|
855
|
+
path: `${path}.sourceRef.label`,
|
|
856
|
+
message: "Distinct EAD3 digital-object and source-reference labels cannot both occupy the canonical target label."
|
|
857
|
+
}));
|
|
858
|
+
if (target.sourceRef && target.targetKind && target.sourceRef.kind !== target.targetKind) issues.push(projectionIssue({
|
|
859
|
+
code: "ead3.archive.target.targetKind.loss",
|
|
860
|
+
path: `${path}.targetKind`,
|
|
861
|
+
message: "Distinct EAD3 targetKind and sourceRef.kind cannot both occupy the canonical target kind."
|
|
862
|
+
}));
|
|
863
|
+
return {
|
|
864
|
+
kind: target.sourceRef?.kind ?? target.targetKind ?? "external-uri",
|
|
865
|
+
id: target.sourceRef?.id ?? target.href,
|
|
866
|
+
href: target.href,
|
|
867
|
+
...label ? { label } : {},
|
|
868
|
+
...target.role ? { role: target.role } : {},
|
|
869
|
+
...target.sourceRef?.locator ? { locator: target.sourceRef.locator } : {},
|
|
870
|
+
...target.sourceRef?.metadata ? { metadata: target.sourceRef.metadata } : {}
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function dateFromEad3(date, path, issues) {
|
|
874
|
+
if (date.extensions) issues.push(projectionIssue({
|
|
875
|
+
code: "ead3.archive.date.extension.loss",
|
|
876
|
+
path: `${path}.extensions`,
|
|
877
|
+
message: "EAD3 date XML extensions are not modeled by the canonical date."
|
|
878
|
+
}));
|
|
879
|
+
return {
|
|
880
|
+
expression: date.expression,
|
|
881
|
+
...date.normalized ? { normalized: date.normalized } : {},
|
|
882
|
+
...date.type ? { type: date.type } : {}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function accessPointFromEad3(value, path, issues) {
|
|
886
|
+
if (value.extensions) issues.push(projectionIssue({
|
|
887
|
+
code: "ead3.archive.accessPoint.extension.loss",
|
|
888
|
+
path: `${path}.extensions`,
|
|
889
|
+
message: "EAD3 access-point XML extensions are not modeled by the canonical access point."
|
|
890
|
+
}));
|
|
891
|
+
return {
|
|
892
|
+
type: value.type,
|
|
893
|
+
label: value.label,
|
|
894
|
+
...value.identifier ? { identifier: value.identifier } : {},
|
|
895
|
+
...value.source ? { source: value.source } : {}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function resolveNodeId(component, path, options, issues) {
|
|
899
|
+
const id = component.id ?? component.referenceCode ?? options.nodeId?.(component, path);
|
|
900
|
+
if (id) return id;
|
|
901
|
+
issues.push(projectionIssue({
|
|
902
|
+
severity: "error",
|
|
903
|
+
code: "ead3.archive.nodeId.required",
|
|
904
|
+
path: `${path}.id`,
|
|
905
|
+
message: "EAD3 component needs an id, a reference code, or an explicit stable nodeId resolver."
|
|
906
|
+
}));
|
|
907
|
+
return "";
|
|
908
|
+
}
|
|
909
|
+
function nodeFromEad3(component, path, options, issues) {
|
|
910
|
+
return {
|
|
911
|
+
id: resolveNodeId(component, path, options, issues),
|
|
912
|
+
level: component.level,
|
|
913
|
+
...component.otherLevel ? { otherLevel: component.otherLevel } : {},
|
|
914
|
+
title: component.title,
|
|
915
|
+
...component.referenceCode ? { referenceCode: component.referenceCode } : {},
|
|
916
|
+
...component.repository ? { repository: component.repository } : {},
|
|
917
|
+
...component.dates?.length ? { dates: component.dates.map((date, index) => dateFromEad3(date, `${path}.dates.${index}`, issues)) } : {},
|
|
918
|
+
...component.extent ? { extent: component.extent } : {},
|
|
919
|
+
...component.creator ? {
|
|
920
|
+
creator: component.creator,
|
|
921
|
+
creatorStatus: "known"
|
|
922
|
+
} : {},
|
|
923
|
+
...component.abstract ? { abstract: component.abstract } : {},
|
|
924
|
+
...component.scopeContent ? { scopeContent: component.scopeContent } : {},
|
|
925
|
+
...component.arrangement ? { arrangement: component.arrangement } : {},
|
|
926
|
+
...component.accessRestriction ? { accessRestriction: component.accessRestriction } : {},
|
|
927
|
+
...component.useRestriction ? { useRestriction: component.useRestriction } : {},
|
|
928
|
+
...component.language ? { language: component.language } : {},
|
|
929
|
+
...component.biographicalHistory ? { biographicalHistory: component.biographicalHistory } : {},
|
|
930
|
+
...component.custodialHistory ? { custodialHistory: component.custodialHistory } : {},
|
|
931
|
+
...component.acquisitionInformation ? { acquisitionInformation: component.acquisitionInformation } : {},
|
|
932
|
+
...component.appraisal ? { appraisal: component.appraisal } : {},
|
|
933
|
+
...component.processingInformation ? { processingInformation: component.processingInformation } : {},
|
|
934
|
+
...component.relatedMaterial ? { relatedMaterial: component.relatedMaterial } : {},
|
|
935
|
+
...component.separatedMaterial ? { separatedMaterial: component.separatedMaterial } : {},
|
|
936
|
+
...component.preferredCitation ? { preferredCitation: component.preferredCitation } : {},
|
|
937
|
+
...component.notes?.length ? { notes: component.notes } : {},
|
|
938
|
+
...component.accessPoints?.length ? { accessPoints: component.accessPoints.map((value, index) => accessPointFromEad3(value, `${path}.accessPoints.${index}`, issues)) } : {},
|
|
939
|
+
...component.digitalObjects?.length ? { targets: component.digitalObjects.map((target, index) => targetFromEad3(target, `${path}.digitalObjects.${index}`, issues)) } : {},
|
|
940
|
+
...component.components?.length ? { subItems: component.components.map((child, index) => nodeFromEad3(child, `${path}.components.${index}`, options, issues)) } : {},
|
|
941
|
+
...component.extensions ? { extensions: { ead3: component.extensions } } : {}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function archiveDescriptionFromEad3(findingAid, options) {
|
|
945
|
+
const issues = [...validateEad3(findingAid)];
|
|
946
|
+
const record = {
|
|
947
|
+
schema: ARCHIVE_DESCRIPTION_SCHEMA,
|
|
948
|
+
recordId: findingAid.recordId,
|
|
949
|
+
title: findingAid.title,
|
|
950
|
+
...findingAid.maintenanceStatus ? { maintenanceStatus: findingAid.maintenanceStatus } : {},
|
|
951
|
+
...findingAid.language ? { language: findingAid.language } : {},
|
|
952
|
+
...findingAid.agencyName ? { agencyName: findingAid.agencyName } : {},
|
|
953
|
+
description: nodeFromEad3(findingAid.description, "description", options, issues),
|
|
954
|
+
provenance: options.provenance,
|
|
955
|
+
...options.sourceRecord ? { sourceRecords: [options.sourceRecord] } : {},
|
|
956
|
+
...findingAid.extensions ? { extensions: { ead3: findingAid.extensions } } : {}
|
|
957
|
+
};
|
|
958
|
+
issues.push(...validateArchiveDescription(record));
|
|
959
|
+
return hasFormatErrors(issues) ? formatFail(issues, record) : formatOk(record, issues);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
//#endregion
|
|
963
|
+
//#region src/archive-description/json-schema.ts
|
|
964
|
+
const nonEmptyStringSchema = {
|
|
965
|
+
type: "string",
|
|
966
|
+
minLength: 1,
|
|
967
|
+
pattern: "\\S"
|
|
968
|
+
};
|
|
969
|
+
const provenanceSchema = {
|
|
970
|
+
type: "object",
|
|
971
|
+
required: [
|
|
972
|
+
"createdBy",
|
|
973
|
+
"createdAt",
|
|
974
|
+
"method"
|
|
975
|
+
],
|
|
976
|
+
properties: {
|
|
977
|
+
createdBy: nonEmptyStringSchema,
|
|
978
|
+
createdAt: {
|
|
979
|
+
type: "string",
|
|
980
|
+
format: "date-time"
|
|
981
|
+
},
|
|
982
|
+
method: nonEmptyStringSchema,
|
|
983
|
+
rules: {
|
|
984
|
+
type: "array",
|
|
985
|
+
items: nonEmptyStringSchema
|
|
986
|
+
},
|
|
987
|
+
tool: { type: "string" },
|
|
988
|
+
model: { type: "string" },
|
|
989
|
+
reviewStatus: {
|
|
990
|
+
type: "string",
|
|
991
|
+
enum: [
|
|
992
|
+
"unreviewed",
|
|
993
|
+
"needs_review",
|
|
994
|
+
"accepted",
|
|
995
|
+
"rejected"
|
|
996
|
+
]
|
|
997
|
+
},
|
|
998
|
+
reviewedBy: { type: "string" },
|
|
999
|
+
reviewedAt: {
|
|
1000
|
+
type: "string",
|
|
1001
|
+
format: "date-time"
|
|
1002
|
+
}
|
|
1003
|
+
},
|
|
1004
|
+
additionalProperties: false
|
|
1005
|
+
};
|
|
1006
|
+
const extensionsSchema = {
|
|
1007
|
+
type: "object",
|
|
1008
|
+
properties: {
|
|
1009
|
+
ead3: {
|
|
1010
|
+
type: "object",
|
|
1011
|
+
additionalProperties: true
|
|
1012
|
+
},
|
|
1013
|
+
vendor: {
|
|
1014
|
+
type: "array",
|
|
1015
|
+
items: {
|
|
1016
|
+
type: "object",
|
|
1017
|
+
required: [
|
|
1018
|
+
"namespace",
|
|
1019
|
+
"name",
|
|
1020
|
+
"value"
|
|
1021
|
+
],
|
|
1022
|
+
properties: {
|
|
1023
|
+
namespace: nonEmptyStringSchema,
|
|
1024
|
+
name: nonEmptyStringSchema,
|
|
1025
|
+
value: {}
|
|
1026
|
+
},
|
|
1027
|
+
additionalProperties: false
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
additionalProperties: false
|
|
1032
|
+
};
|
|
1033
|
+
const dateSchema = {
|
|
1034
|
+
type: "object",
|
|
1035
|
+
required: ["expression"],
|
|
1036
|
+
properties: {
|
|
1037
|
+
expression: nonEmptyStringSchema,
|
|
1038
|
+
normalized: { type: "string" },
|
|
1039
|
+
type: { type: "string" },
|
|
1040
|
+
certainty: { type: "string" }
|
|
1041
|
+
},
|
|
1042
|
+
additionalProperties: false
|
|
1043
|
+
};
|
|
1044
|
+
const accessPointSchema = {
|
|
1045
|
+
type: "object",
|
|
1046
|
+
required: ["type", "label"],
|
|
1047
|
+
properties: {
|
|
1048
|
+
type: nonEmptyStringSchema,
|
|
1049
|
+
label: nonEmptyStringSchema,
|
|
1050
|
+
identifier: { type: "string" },
|
|
1051
|
+
source: { type: "string" }
|
|
1052
|
+
},
|
|
1053
|
+
additionalProperties: false
|
|
1054
|
+
};
|
|
1055
|
+
const targetRefSchema = {
|
|
1056
|
+
type: "object",
|
|
1057
|
+
required: ["kind", "id"],
|
|
1058
|
+
properties: {
|
|
1059
|
+
kind: nonEmptyStringSchema,
|
|
1060
|
+
id: nonEmptyStringSchema,
|
|
1061
|
+
href: { type: "string" },
|
|
1062
|
+
label: { type: "string" },
|
|
1063
|
+
role: { type: "string" },
|
|
1064
|
+
locator: {
|
|
1065
|
+
type: "object",
|
|
1066
|
+
additionalProperties: true
|
|
1067
|
+
},
|
|
1068
|
+
metadata: {
|
|
1069
|
+
type: "object",
|
|
1070
|
+
additionalProperties: true
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
additionalProperties: false
|
|
1074
|
+
};
|
|
1075
|
+
const sourceRecordSchema = {
|
|
1076
|
+
type: "object",
|
|
1077
|
+
required: ["id", "producer"],
|
|
1078
|
+
properties: {
|
|
1079
|
+
id: nonEmptyStringSchema,
|
|
1080
|
+
producer: nonEmptyStringSchema,
|
|
1081
|
+
format: { type: "string" },
|
|
1082
|
+
source: { type: "string" },
|
|
1083
|
+
retrievedAt: {
|
|
1084
|
+
type: "string",
|
|
1085
|
+
format: "date-time"
|
|
1086
|
+
},
|
|
1087
|
+
raw: {},
|
|
1088
|
+
mappedFields: {
|
|
1089
|
+
type: "array",
|
|
1090
|
+
items: nonEmptyStringSchema
|
|
1091
|
+
},
|
|
1092
|
+
unmappedFields: {},
|
|
1093
|
+
diagnostics: {
|
|
1094
|
+
type: "array",
|
|
1095
|
+
items: {
|
|
1096
|
+
type: "object",
|
|
1097
|
+
required: [
|
|
1098
|
+
"severity",
|
|
1099
|
+
"code",
|
|
1100
|
+
"message"
|
|
1101
|
+
],
|
|
1102
|
+
properties: {
|
|
1103
|
+
severity: {
|
|
1104
|
+
type: "string",
|
|
1105
|
+
enum: [
|
|
1106
|
+
"error",
|
|
1107
|
+
"warning",
|
|
1108
|
+
"info"
|
|
1109
|
+
]
|
|
1110
|
+
},
|
|
1111
|
+
code: nonEmptyStringSchema,
|
|
1112
|
+
message: nonEmptyStringSchema,
|
|
1113
|
+
path: {
|
|
1114
|
+
type: "array",
|
|
1115
|
+
items: nonEmptyStringSchema
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
additionalProperties: false
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
},
|
|
1122
|
+
additionalProperties: false
|
|
1123
|
+
};
|
|
1124
|
+
const nodeProperties = {
|
|
1125
|
+
id: nonEmptyStringSchema,
|
|
1126
|
+
level: {
|
|
1127
|
+
type: "string",
|
|
1128
|
+
enum: [
|
|
1129
|
+
"collection",
|
|
1130
|
+
"series",
|
|
1131
|
+
"subseries",
|
|
1132
|
+
"file",
|
|
1133
|
+
"item",
|
|
1134
|
+
"otherlevel"
|
|
1135
|
+
]
|
|
1136
|
+
},
|
|
1137
|
+
otherLevel: { type: "string" },
|
|
1138
|
+
title: nonEmptyStringSchema,
|
|
1139
|
+
referenceCode: { type: "string" },
|
|
1140
|
+
repository: { type: "string" },
|
|
1141
|
+
dates: {
|
|
1142
|
+
type: "array",
|
|
1143
|
+
items: dateSchema
|
|
1144
|
+
},
|
|
1145
|
+
extent: { type: "string" },
|
|
1146
|
+
creator: { type: "string" },
|
|
1147
|
+
creatorStatus: {
|
|
1148
|
+
type: "string",
|
|
1149
|
+
enum: [
|
|
1150
|
+
"known",
|
|
1151
|
+
"unknown",
|
|
1152
|
+
"not_applicable"
|
|
1153
|
+
]
|
|
1154
|
+
},
|
|
1155
|
+
abstract: { type: "string" },
|
|
1156
|
+
scopeContent: { type: "string" },
|
|
1157
|
+
arrangement: { type: "string" },
|
|
1158
|
+
accessRestriction: { type: "string" },
|
|
1159
|
+
useRestriction: { type: "string" },
|
|
1160
|
+
language: { type: "string" },
|
|
1161
|
+
biographicalHistory: { type: "string" },
|
|
1162
|
+
custodialHistory: { type: "string" },
|
|
1163
|
+
acquisitionInformation: { type: "string" },
|
|
1164
|
+
appraisal: { type: "string" },
|
|
1165
|
+
processingInformation: { type: "string" },
|
|
1166
|
+
relatedMaterial: { type: "string" },
|
|
1167
|
+
separatedMaterial: { type: "string" },
|
|
1168
|
+
preferredCitation: { type: "string" },
|
|
1169
|
+
notes: {
|
|
1170
|
+
type: "array",
|
|
1171
|
+
items: nonEmptyStringSchema
|
|
1172
|
+
},
|
|
1173
|
+
accessPoints: {
|
|
1174
|
+
type: "array",
|
|
1175
|
+
items: accessPointSchema
|
|
1176
|
+
},
|
|
1177
|
+
targets: {
|
|
1178
|
+
type: "array",
|
|
1179
|
+
items: targetRefSchema
|
|
1180
|
+
},
|
|
1181
|
+
provenance: provenanceSchema,
|
|
1182
|
+
subItems: {
|
|
1183
|
+
type: "array",
|
|
1184
|
+
items: { $ref: "#/$defs/node" }
|
|
1185
|
+
},
|
|
1186
|
+
extensions: extensionsSchema
|
|
1187
|
+
};
|
|
1188
|
+
function buildArchiveDescriptionJsonSchema() {
|
|
1189
|
+
return {
|
|
1190
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1191
|
+
$id: ARCHIVE_DESCRIPTION_SCHEMA_ID,
|
|
1192
|
+
title: ARCHIVE_DESCRIPTION_SCHEMA_TITLE,
|
|
1193
|
+
type: "object",
|
|
1194
|
+
required: [
|
|
1195
|
+
"schema",
|
|
1196
|
+
"recordId",
|
|
1197
|
+
"title",
|
|
1198
|
+
"description",
|
|
1199
|
+
"provenance"
|
|
1200
|
+
],
|
|
1201
|
+
properties: {
|
|
1202
|
+
schema: { const: ARCHIVE_DESCRIPTION_SCHEMA },
|
|
1203
|
+
recordId: nonEmptyStringSchema,
|
|
1204
|
+
title: nonEmptyStringSchema,
|
|
1205
|
+
maintenanceStatus: {
|
|
1206
|
+
type: "string",
|
|
1207
|
+
enum: [
|
|
1208
|
+
"new",
|
|
1209
|
+
"revised",
|
|
1210
|
+
"deleted",
|
|
1211
|
+
"derived",
|
|
1212
|
+
"unknown"
|
|
1213
|
+
]
|
|
1214
|
+
},
|
|
1215
|
+
language: { type: "string" },
|
|
1216
|
+
agencyName: { type: "string" },
|
|
1217
|
+
description: { allOf: [{ $ref: "#/$defs/node" }, {
|
|
1218
|
+
type: "object",
|
|
1219
|
+
properties: { level: { const: "collection" } }
|
|
1220
|
+
}] },
|
|
1221
|
+
provenance: provenanceSchema,
|
|
1222
|
+
sourceRecords: {
|
|
1223
|
+
type: "array",
|
|
1224
|
+
items: sourceRecordSchema
|
|
1225
|
+
},
|
|
1226
|
+
extensions: extensionsSchema
|
|
1227
|
+
},
|
|
1228
|
+
$defs: { node: {
|
|
1229
|
+
type: "object",
|
|
1230
|
+
required: [
|
|
1231
|
+
"id",
|
|
1232
|
+
"level",
|
|
1233
|
+
"title"
|
|
1234
|
+
],
|
|
1235
|
+
properties: nodeProperties,
|
|
1236
|
+
allOf: [{
|
|
1237
|
+
if: {
|
|
1238
|
+
properties: { level: { const: "otherlevel" } },
|
|
1239
|
+
required: ["level"]
|
|
1240
|
+
},
|
|
1241
|
+
then: {
|
|
1242
|
+
required: ["otherLevel"],
|
|
1243
|
+
properties: { otherLevel: nonEmptyStringSchema }
|
|
1244
|
+
}
|
|
1245
|
+
}],
|
|
1246
|
+
additionalProperties: false
|
|
1247
|
+
} },
|
|
1248
|
+
additionalProperties: false
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
const archiveDescriptionJsonSchema = buildArchiveDescriptionJsonSchema();
|
|
1252
|
+
const ARCHIVE_DESCRIPTION_SCHEMA_REVISION = contentHash(canonicalJson(archiveDescriptionJsonSchema));
|
|
1253
|
+
|
|
1254
|
+
//#endregion
|
|
1255
|
+
export { ARCHIVE_DESCRIPTION_EAD3_PROJECTION_ID, ARCHIVE_DESCRIPTION_FORMAT_ID, ARCHIVE_DESCRIPTION_SCHEMA, ARCHIVE_DESCRIPTION_SCHEMA_ID, ARCHIVE_DESCRIPTION_SCHEMA_REVISION, ARCHIVE_DESCRIPTION_SCHEMA_TITLE, ARCHIVE_DESCRIPTION_SCHEMA_VERSION, DACS_PROFILE_ID, archiveDescriptionAdapter, archiveDescriptionFromEad3, archiveDescriptionJsonSchema, archiveDescriptionToEad3, buildArchiveDescriptionJsonSchema, canonicalJson, contentHash, parseArchiveDescriptionJson, serializeArchiveDescriptionJson, validateArchiveDescription, validateDacsArchiveDescription };
|
|
1256
|
+
//# sourceMappingURL=index.js.map
|