@echovisionlab/geul-telemetry 0.1.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.
Files changed (58) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +38 -0
  3. package/fixtures/audit-records.json +316 -0
  4. package/fixtures/collaboration-checkpoint-entity-types.json +15 -0
  5. package/fixtures/domain-audit-actions.json +132 -0
  6. package/fixtures/domain-audit-wire-parity.json +1751 -0
  7. package/fixtures/http-request-results.json +21 -0
  8. package/fixtures/job-catalog.json +23 -0
  9. package/fixtures/locale-content-audit.json +126 -0
  10. package/fixtures/log-redaction.json +72 -0
  11. package/fixtures/request-record.json +14 -0
  12. package/fixtures/security-access-records.json +61 -0
  13. package/fixtures/service-identities.json +11 -0
  14. package/fixtures/system-catalog.json +28 -0
  15. package/fixtures/translation-job-terminal-catalog.json +45 -0
  16. package/fixtures/translation-settings-fields.json +1 -0
  17. package/package.json +61 -0
  18. package/policy/log-redaction.json +113 -0
  19. package/src/actor.ts +81 -0
  20. package/src/audit/account.ts +206 -0
  21. package/src/audit/actor-policy.ts +63 -0
  22. package/src/audit/artist-label.ts +312 -0
  23. package/src/audit/attributes.ts +107 -0
  24. package/src/audit/builder.ts +25 -0
  25. package/src/audit/catalog.ts +116 -0
  26. package/src/audit/content.ts +723 -0
  27. package/src/audit/email-authoring.ts +220 -0
  28. package/src/audit/file.ts +168 -0
  29. package/src/audit/form.ts +229 -0
  30. package/src/audit/integrations.ts +207 -0
  31. package/src/audit/locale-code.ts +6 -0
  32. package/src/audit/locale-content.ts +358 -0
  33. package/src/audit/member-account.ts +313 -0
  34. package/src/audit/member.ts +171 -0
  35. package/src/audit/program.ts +339 -0
  36. package/src/audit/reference-data.ts +230 -0
  37. package/src/audit/reference-entities.ts +332 -0
  38. package/src/audit/relation-download-policy.ts +292 -0
  39. package/src/audit/release-campaign.ts +422 -0
  40. package/src/audit/series-type.ts +298 -0
  41. package/src/audit/settings.ts +306 -0
  42. package/src/audit/source-locale.ts +337 -0
  43. package/src/audit/types.ts +9 -0
  44. package/src/audit.ts +21 -0
  45. package/src/authentication.ts +183 -0
  46. package/src/error-metadata.ts +11 -0
  47. package/src/index.ts +11 -0
  48. package/src/job.ts +56 -0
  49. package/src/log-attribute-policy.ts +21 -0
  50. package/src/otel.ts +64 -0
  51. package/src/records.ts +1244 -0
  52. package/src/redaction.ts +35 -0
  53. package/src/request-context.ts +73 -0
  54. package/src/request-id.ts +13 -0
  55. package/src/request-result.ts +73 -0
  56. package/src/request.ts +60 -0
  57. package/src/system.ts +355 -0
  58. package/src/trace.ts +13 -0
@@ -0,0 +1,339 @@
1
+ import type {
2
+ AuditCollectionOperation,
3
+ AuditItemOperation,
4
+ AuditRecord,
5
+ AuditState,
6
+ } from "../records.ts";
7
+ import {
8
+ assertOnlyAuditAttributes,
9
+ canonicalAuditValues,
10
+ } from "./attributes.ts";
11
+ import { buildAuditRecord } from "./builder.ts";
12
+ import {
13
+ AUDIT_ACTIONS,
14
+ AUDIT_CATALOG,
15
+ requireCatalogTarget,
16
+ } from "./catalog.ts";
17
+ import {
18
+ isRelationDownloadPolicyAuditRecord,
19
+ validateRelationDownloadPolicyAuditRecord,
20
+ } from "./relation-download-policy.ts";
21
+ import type { AuditMetadata } from "./types.ts";
22
+
23
+ type ProgramTarget = "program_event" | "program_event_series";
24
+ type ProgramAction = Extract<
25
+ (typeof AUDIT_ACTIONS)[number],
26
+ | "program_event.created"
27
+ | "program_event.updated"
28
+ | "program_event.deleted"
29
+ | "program_event_series.created"
30
+ | "program_event_series.updated"
31
+ | "program_event_series.deleted"
32
+ >;
33
+ export type ProgramEventMetadataField =
34
+ | "all_day"
35
+ | "artists"
36
+ | "clients"
37
+ | "ends_at"
38
+ | "external_url"
39
+ | "labels"
40
+ | "location_mode"
41
+ | "map_place_id"
42
+ | "series"
43
+ | "slug"
44
+ | "starts_at"
45
+ | "stream_url"
46
+ | "ticket_url"
47
+ | "timezone"
48
+ | "type";
49
+ export type ProgramEventSeriesMetadataField =
50
+ "description" | "slug" | "summary" | "title";
51
+ type ProgramEventLifecycleState = Extract<
52
+ AuditState,
53
+ "draft" | "published" | "archived"
54
+ >;
55
+ type ProgramEventSeriesLifecycleState = Extract<
56
+ AuditState,
57
+ "draft" | "published"
58
+ >;
59
+
60
+ const targetTypes = new Set<ProgramTarget>([
61
+ "program_event",
62
+ "program_event_series",
63
+ ]);
64
+ const programActions = new Set<ProgramAction>(
65
+ AUDIT_ACTIONS.filter((action) =>
66
+ targetTypes.has(AUDIT_CATALOG[action] as ProgramTarget),
67
+ ) as ProgramAction[],
68
+ );
69
+ const eventMetadataFields = new Set<ProgramEventMetadataField>([
70
+ "all_day",
71
+ "artists",
72
+ "clients",
73
+ "ends_at",
74
+ "external_url",
75
+ "labels",
76
+ "location_mode",
77
+ "map_place_id",
78
+ "series",
79
+ "slug",
80
+ "starts_at",
81
+ "stream_url",
82
+ "ticket_url",
83
+ "timezone",
84
+ "type",
85
+ ]);
86
+ const seriesMetadataFields = new Set<ProgramEventSeriesMetadataField>([
87
+ "description",
88
+ "slug",
89
+ "summary",
90
+ "title",
91
+ ]);
92
+ function buildProgramAuditRecord(
93
+ metadata: AuditMetadata,
94
+ action: ProgramAction,
95
+ targetId: string,
96
+ attributes: Parameters<typeof buildAuditRecord>[2] = {},
97
+ ): AuditRecord {
98
+ return buildAuditRecord(
99
+ metadata,
100
+ { action, target_type: AUDIT_CATALOG[action], target_id: targetId },
101
+ attributes,
102
+ );
103
+ }
104
+ function requireNoExtra(record: AuditRecord, allowed: readonly string[]): void {
105
+ assertOnlyAuditAttributes(record, allowed);
106
+ }
107
+ function requireIdentifier(name: string, value: string | undefined): void {
108
+ if (!value || value.length > 255 || value.trim() !== value)
109
+ throw new TypeError(`${name} requires an identifier`);
110
+ }
111
+ function requireFields(
112
+ record: AuditRecord,
113
+ allowed: ReadonlySet<string>,
114
+ ): void {
115
+ const fields = record.changed_fields;
116
+ if (
117
+ !fields?.length ||
118
+ fields.some(
119
+ (field, i) => !allowed.has(field) || (i > 0 && fields[i - 1] >= field),
120
+ )
121
+ )
122
+ throw new TypeError("invalid or non-canonical changed_fields");
123
+ }
124
+ function requireChild(record: AuditRecord): void {
125
+ if (record.item_ids !== undefined) {
126
+ for (const itemId of record.item_ids) requireIdentifier("item_ids", itemId);
127
+ return requireNoExtra(record, ["changed_fields", "item_ids"]);
128
+ }
129
+ if (
130
+ !record.item_id ||
131
+ !["created", "updated", "deleted"].includes(record.item_operation ?? "")
132
+ )
133
+ throw new TypeError("child requires operation and item");
134
+ requireIdentifier("item_id", record.item_id);
135
+ requireNoExtra(record, ["changed_fields", "item_operation", "item_id"]);
136
+ }
137
+ function requireEventLifecycle(record: AuditRecord): void {
138
+ if (
139
+ !(["draft", "published", "archived"] as const).includes(
140
+ record.previous_state as ProgramEventLifecycleState,
141
+ ) ||
142
+ !(["draft", "published", "archived"] as const).includes(
143
+ record.new_state as ProgramEventLifecycleState,
144
+ ) ||
145
+ record.previous_state === record.new_state
146
+ )
147
+ throw new TypeError(
148
+ "program event lifecycle requires distinct valid states",
149
+ );
150
+ requireNoExtra(record, ["changed_fields", "previous_state", "new_state"]);
151
+ }
152
+ function requireSeriesLifecycle(record: AuditRecord): void {
153
+ if (
154
+ !(["draft", "published"] as const).includes(
155
+ record.previous_state as ProgramEventSeriesLifecycleState,
156
+ ) ||
157
+ !(["draft", "published"] as const).includes(
158
+ record.new_state as ProgramEventSeriesLifecycleState,
159
+ ) ||
160
+ record.previous_state === record.new_state
161
+ )
162
+ throw new TypeError(
163
+ "program event series lifecycle requires draft/published transition",
164
+ );
165
+ requireNoExtra(record, ["changed_fields", "previous_state", "new_state"]);
166
+ }
167
+ function requirePoster(record: AuditRecord): void {
168
+ if (
169
+ record.collection_operation !== "added" &&
170
+ record.collection_operation !== "removed"
171
+ )
172
+ throw new TypeError("poster requires collection operation");
173
+ requireIdentifier("file_id", record.file_id);
174
+ requireNoExtra(record, ["changed_fields", "collection_operation", "file_id"]);
175
+ }
176
+ function validateEventUpdate(record: AuditRecord): void {
177
+ if (isRelationDownloadPolicyAuditRecord(record))
178
+ return validateRelationDownloadPolicyAuditRecord(record);
179
+ if (record.changed_fields?.length !== 1) {
180
+ if (record.actor_kind === "system")
181
+ throw new TypeError("program event does not allow system actor");
182
+ return requireFields(record, eventMetadataFields);
183
+ }
184
+ if (record.actor_kind === "system")
185
+ throw new TypeError("program event does not allow system actor");
186
+ switch (record.changed_fields[0]) {
187
+ case "poster":
188
+ return requirePoster(record);
189
+ case "media":
190
+ case "credits":
191
+ return requireChild(record);
192
+ case "status":
193
+ return requireEventLifecycle(record);
194
+ default:
195
+ return requireFields(record, eventMetadataFields);
196
+ }
197
+ }
198
+ function validateSeriesUpdate(record: AuditRecord): void {
199
+ if (
200
+ record.changed_fields?.length === 1 &&
201
+ record.changed_fields[0] === "poster"
202
+ )
203
+ return requirePoster(record);
204
+ if (
205
+ record.changed_fields?.length === 1 &&
206
+ record.changed_fields[0] === "status"
207
+ )
208
+ return requireSeriesLifecycle(record);
209
+ requireFields(record, seriesMetadataFields);
210
+ requireNoExtra(record, ["changed_fields"]);
211
+ }
212
+
213
+ /** Returns true only for Program Event and Program Event Series catalog actions. */
214
+ export function validateProgramAuditRecord(record: AuditRecord): boolean {
215
+ if (!programActions.has(record.action as ProgramAction)) return false;
216
+ requireCatalogTarget(record.action, record.target_type, record.target_id);
217
+ if (!record.action.endsWith(".updated")) {
218
+ requireNoExtra(record, []);
219
+ return true;
220
+ }
221
+ if (record.action === "program_event.updated") validateEventUpdate(record);
222
+ else validateSeriesUpdate(record);
223
+ return true;
224
+ }
225
+
226
+ export function buildProgramEventCreatedAuditRecord(
227
+ metadata: AuditMetadata,
228
+ id: string,
229
+ ): AuditRecord {
230
+ return buildProgramAuditRecord(metadata, "program_event.created", id);
231
+ }
232
+ export function buildProgramEventDeletedAuditRecord(
233
+ metadata: AuditMetadata,
234
+ id: string,
235
+ ): AuditRecord {
236
+ return buildProgramAuditRecord(metadata, "program_event.deleted", id);
237
+ }
238
+ export function buildProgramEventMetadataAuditRecord(
239
+ metadata: AuditMetadata,
240
+ id: string,
241
+ fields: readonly ProgramEventMetadataField[],
242
+ ): AuditRecord {
243
+ return buildProgramAuditRecord(metadata, "program_event.updated", id, {
244
+ changed_fields: canonicalAuditValues(fields),
245
+ });
246
+ }
247
+ export function buildProgramEventPosterAuditRecord(
248
+ metadata: AuditMetadata,
249
+ id: string,
250
+ collectionOperation: AuditCollectionOperation,
251
+ fileId: string,
252
+ ): AuditRecord {
253
+ return buildProgramAuditRecord(metadata, "program_event.updated", id, {
254
+ changed_fields: ["poster"],
255
+ collection_operation: collectionOperation,
256
+ file_id: fileId,
257
+ });
258
+ }
259
+ export function buildProgramEventChildAuditRecord(
260
+ metadata: AuditMetadata,
261
+ id: string,
262
+ kind: "media" | "credits",
263
+ itemId: string,
264
+ itemOperation: AuditItemOperation,
265
+ ): AuditRecord {
266
+ return buildProgramAuditRecord(metadata, "program_event.updated", id, {
267
+ changed_fields: [kind],
268
+ item_id: itemId,
269
+ item_operation: itemOperation,
270
+ });
271
+ }
272
+ export function buildProgramEventChildOrderAuditRecord(
273
+ metadata: AuditMetadata,
274
+ id: string,
275
+ kind: "media" | "credits",
276
+ itemIds: readonly string[],
277
+ ): AuditRecord {
278
+ return buildProgramAuditRecord(metadata, "program_event.updated", id, {
279
+ changed_fields: [kind],
280
+ item_ids: [...itemIds],
281
+ });
282
+ }
283
+ export function buildProgramEventLifecycleAuditRecord(
284
+ metadata: AuditMetadata,
285
+ id: string,
286
+ previousState: ProgramEventLifecycleState,
287
+ newState: ProgramEventLifecycleState,
288
+ ): AuditRecord {
289
+ return buildProgramAuditRecord(metadata, "program_event.updated", id, {
290
+ changed_fields: ["status"],
291
+ previous_state: previousState,
292
+ new_state: newState,
293
+ });
294
+ }
295
+ export function buildProgramEventSeriesCreatedAuditRecord(
296
+ metadata: AuditMetadata,
297
+ id: string,
298
+ ): AuditRecord {
299
+ return buildProgramAuditRecord(metadata, "program_event_series.created", id);
300
+ }
301
+ export function buildProgramEventSeriesDeletedAuditRecord(
302
+ metadata: AuditMetadata,
303
+ id: string,
304
+ ): AuditRecord {
305
+ return buildProgramAuditRecord(metadata, "program_event_series.deleted", id);
306
+ }
307
+ export function buildProgramEventSeriesMetadataAuditRecord(
308
+ metadata: AuditMetadata,
309
+ id: string,
310
+ fields: readonly ProgramEventSeriesMetadataField[],
311
+ ): AuditRecord {
312
+ return buildProgramAuditRecord(metadata, "program_event_series.updated", id, {
313
+ changed_fields: canonicalAuditValues(fields),
314
+ });
315
+ }
316
+ export function buildProgramEventSeriesPosterAuditRecord(
317
+ metadata: AuditMetadata,
318
+ id: string,
319
+ collectionOperation: AuditCollectionOperation,
320
+ fileId: string,
321
+ ): AuditRecord {
322
+ return buildProgramAuditRecord(metadata, "program_event_series.updated", id, {
323
+ changed_fields: ["poster"],
324
+ collection_operation: collectionOperation,
325
+ file_id: fileId,
326
+ });
327
+ }
328
+ export function buildProgramEventSeriesLifecycleAuditRecord(
329
+ metadata: AuditMetadata,
330
+ id: string,
331
+ previousState: ProgramEventSeriesLifecycleState,
332
+ newState: ProgramEventSeriesLifecycleState,
333
+ ): AuditRecord {
334
+ return buildProgramAuditRecord(metadata, "program_event_series.updated", id, {
335
+ changed_fields: ["status"],
336
+ previous_state: previousState,
337
+ new_state: newState,
338
+ });
339
+ }
@@ -0,0 +1,230 @@
1
+ import type { AuditRecord } from "../records.ts";
2
+ import {
3
+ assertOnlyAuditAttributes,
4
+ canonicalAuditValues,
5
+ } from "./attributes.ts";
6
+ import { buildAuditRecord } from "./builder.ts";
7
+ import { AUDIT_CATALOG, requireCatalogTarget } from "./catalog.ts";
8
+ import type { AuditMetadata } from "./types.ts";
9
+
10
+ type ReferenceDataTarget = "category" | "tag" | "genre" | "style" | "format";
11
+ type ReferenceDataAction =
12
+ | "category.created"
13
+ | "category.updated"
14
+ | "category.deleted"
15
+ | "tag.created"
16
+ | "tag.updated"
17
+ | "tag.deleted"
18
+ | "genre.created"
19
+ | "genre.updated"
20
+ | "genre.deleted"
21
+ | "style.created"
22
+ | "style.updated"
23
+ | "style.deleted"
24
+ | "format.created"
25
+ | "format.updated"
26
+ | "format.deleted";
27
+ type ReferenceDataUpdateAction = Extract<
28
+ ReferenceDataAction,
29
+ `${string}.updated`
30
+ >;
31
+ type DescribedMetadataField = "description" | "name" | "slug";
32
+ type NamedMetadataField = "name" | "slug";
33
+
34
+ const describedTargets = new Set<ReferenceDataTarget>([
35
+ "category",
36
+ "genre",
37
+ "style",
38
+ ]);
39
+ const referenceDataActions = new Set<ReferenceDataAction>([
40
+ "category.created",
41
+ "category.updated",
42
+ "category.deleted",
43
+ "tag.created",
44
+ "tag.updated",
45
+ "tag.deleted",
46
+ "genre.created",
47
+ "genre.updated",
48
+ "genre.deleted",
49
+ "style.created",
50
+ "style.updated",
51
+ "style.deleted",
52
+ "format.created",
53
+ "format.updated",
54
+ "format.deleted",
55
+ ]);
56
+ function buildReferenceDataAuditRecord(
57
+ metadata: AuditMetadata,
58
+ action: ReferenceDataAction,
59
+ targetId: string,
60
+ attributes: Parameters<typeof buildAuditRecord>[2] = {},
61
+ ): AuditRecord {
62
+ return buildAuditRecord(
63
+ metadata,
64
+ { action, target_type: AUDIT_CATALOG[action], target_id: targetId },
65
+ attributes,
66
+ );
67
+ }
68
+
69
+ function buildReferenceDataMetadataUpdatedAuditRecord(
70
+ metadata: AuditMetadata,
71
+ action: ReferenceDataUpdateAction,
72
+ targetId: string,
73
+ changedFields: readonly string[],
74
+ ): AuditRecord {
75
+ return buildReferenceDataAuditRecord(metadata, action, targetId, {
76
+ changed_fields: canonicalAuditValues(changedFields),
77
+ });
78
+ }
79
+
80
+ function requireNoExtra(record: AuditRecord, allowed: readonly string[]): void {
81
+ assertOnlyAuditAttributes(record, allowed);
82
+ }
83
+
84
+ /** Returns true only for the reviewed Category, Tag, Genre, Style, and Format actions. */
85
+ export function validateReferenceDataAuditRecord(record: AuditRecord): boolean {
86
+ if (!referenceDataActions.has(record.action as ReferenceDataAction))
87
+ return false;
88
+ requireCatalogTarget(record.action, record.target_type, record.target_id);
89
+ if (!record.action.endsWith(".updated")) {
90
+ requireNoExtra(record, []);
91
+ return true;
92
+ }
93
+ const target = AUDIT_CATALOG[record.action] as ReferenceDataTarget;
94
+ const allowed = describedTargets.has(target)
95
+ ? new Set<DescribedMetadataField>(["description", "name", "slug"])
96
+ : new Set<NamedMetadataField>(["name", "slug"]);
97
+ const fields = record.changed_fields;
98
+ if (
99
+ !fields?.length ||
100
+ fields.some(
101
+ (field, index) =>
102
+ !allowed.has(field as never) ||
103
+ (index > 0 && fields[index - 1] >= field),
104
+ )
105
+ )
106
+ throw new TypeError("invalid or non-canonical changed_fields");
107
+ requireNoExtra(record, ["changed_fields"]);
108
+ return true;
109
+ }
110
+
111
+ export function buildCategoryCreatedAuditRecord(
112
+ metadata: AuditMetadata,
113
+ id: string,
114
+ ): AuditRecord {
115
+ return buildReferenceDataAuditRecord(metadata, "category.created", id);
116
+ }
117
+ export function buildCategoryMetadataUpdatedAuditRecord(
118
+ metadata: AuditMetadata,
119
+ id: string,
120
+ fields: readonly DescribedMetadataField[],
121
+ ): AuditRecord {
122
+ return buildReferenceDataMetadataUpdatedAuditRecord(
123
+ metadata,
124
+ "category.updated",
125
+ id,
126
+ fields,
127
+ );
128
+ }
129
+ export function buildCategoryDeletedAuditRecord(
130
+ metadata: AuditMetadata,
131
+ id: string,
132
+ ): AuditRecord {
133
+ return buildReferenceDataAuditRecord(metadata, "category.deleted", id);
134
+ }
135
+ export function buildTagCreatedAuditRecord(
136
+ metadata: AuditMetadata,
137
+ id: string,
138
+ ): AuditRecord {
139
+ return buildReferenceDataAuditRecord(metadata, "tag.created", id);
140
+ }
141
+ export function buildTagMetadataUpdatedAuditRecord(
142
+ metadata: AuditMetadata,
143
+ id: string,
144
+ fields: readonly NamedMetadataField[],
145
+ ): AuditRecord {
146
+ return buildReferenceDataMetadataUpdatedAuditRecord(
147
+ metadata,
148
+ "tag.updated",
149
+ id,
150
+ fields,
151
+ );
152
+ }
153
+ export function buildTagDeletedAuditRecord(
154
+ metadata: AuditMetadata,
155
+ id: string,
156
+ ): AuditRecord {
157
+ return buildReferenceDataAuditRecord(metadata, "tag.deleted", id);
158
+ }
159
+ export function buildGenreCreatedAuditRecord(
160
+ metadata: AuditMetadata,
161
+ id: string,
162
+ ): AuditRecord {
163
+ return buildReferenceDataAuditRecord(metadata, "genre.created", id);
164
+ }
165
+ export function buildGenreMetadataUpdatedAuditRecord(
166
+ metadata: AuditMetadata,
167
+ id: string,
168
+ fields: readonly DescribedMetadataField[],
169
+ ): AuditRecord {
170
+ return buildReferenceDataMetadataUpdatedAuditRecord(
171
+ metadata,
172
+ "genre.updated",
173
+ id,
174
+ fields,
175
+ );
176
+ }
177
+ export function buildGenreDeletedAuditRecord(
178
+ metadata: AuditMetadata,
179
+ id: string,
180
+ ): AuditRecord {
181
+ return buildReferenceDataAuditRecord(metadata, "genre.deleted", id);
182
+ }
183
+ export function buildStyleCreatedAuditRecord(
184
+ metadata: AuditMetadata,
185
+ id: string,
186
+ ): AuditRecord {
187
+ return buildReferenceDataAuditRecord(metadata, "style.created", id);
188
+ }
189
+ export function buildStyleMetadataUpdatedAuditRecord(
190
+ metadata: AuditMetadata,
191
+ id: string,
192
+ fields: readonly DescribedMetadataField[],
193
+ ): AuditRecord {
194
+ return buildReferenceDataMetadataUpdatedAuditRecord(
195
+ metadata,
196
+ "style.updated",
197
+ id,
198
+ fields,
199
+ );
200
+ }
201
+ export function buildStyleDeletedAuditRecord(
202
+ metadata: AuditMetadata,
203
+ id: string,
204
+ ): AuditRecord {
205
+ return buildReferenceDataAuditRecord(metadata, "style.deleted", id);
206
+ }
207
+ export function buildFormatCreatedAuditRecord(
208
+ metadata: AuditMetadata,
209
+ id: string,
210
+ ): AuditRecord {
211
+ return buildReferenceDataAuditRecord(metadata, "format.created", id);
212
+ }
213
+ export function buildFormatMetadataUpdatedAuditRecord(
214
+ metadata: AuditMetadata,
215
+ id: string,
216
+ fields: readonly NamedMetadataField[],
217
+ ): AuditRecord {
218
+ return buildReferenceDataMetadataUpdatedAuditRecord(
219
+ metadata,
220
+ "format.updated",
221
+ id,
222
+ fields,
223
+ );
224
+ }
225
+ export function buildFormatDeletedAuditRecord(
226
+ metadata: AuditMetadata,
227
+ id: string,
228
+ ): AuditRecord {
229
+ return buildReferenceDataAuditRecord(metadata, "format.deleted", id);
230
+ }