@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,207 @@
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 {
8
+ AUDIT_ACTIONS,
9
+ AUDIT_CATALOG,
10
+ requireCatalogTarget,
11
+ } from "./catalog.ts";
12
+ import type { AuditMetadata } from "./types.ts";
13
+
14
+ type IntegrationTarget =
15
+ | "mail_adapter"
16
+ | "email_suppression"
17
+ | "translation_settings"
18
+ | "translation_provider";
19
+ type IntegrationAction = Extract<
20
+ (typeof AUDIT_ACTIONS)[number],
21
+ | "mail_adapter.created"
22
+ | "mail_adapter.updated"
23
+ | "mail_adapter.deleted"
24
+ | "email_suppression.updated"
25
+ | "translation_settings.updated"
26
+ | "translation_provider.created"
27
+ | "translation_provider.updated"
28
+ | "translation_provider.deleted"
29
+ >;
30
+ export type AdapterConfigField =
31
+ "active" | "config" | "name" | "priority" | "type";
32
+ export type TranslationSettingsField = "default_locale" | "protected_terms";
33
+
34
+ export const TRANSLATION_SETTINGS_FIELDS = [
35
+ "default_locale",
36
+ "protected_terms",
37
+ ] as const satisfies readonly TranslationSettingsField[];
38
+
39
+ const targetTypes = new Set<IntegrationTarget>([
40
+ "mail_adapter",
41
+ "email_suppression",
42
+ "translation_settings",
43
+ "translation_provider",
44
+ ]);
45
+ // Module ownership derives from the immutable catalog, not a duplicate map.
46
+ const integrationActions = new Set<IntegrationAction>(
47
+ AUDIT_ACTIONS.filter((action) =>
48
+ targetTypes.has(AUDIT_CATALOG[action] as IntegrationTarget),
49
+ ) as IntegrationAction[],
50
+ );
51
+ const adapterConfigFields = new Set<AdapterConfigField>([
52
+ "active",
53
+ "config",
54
+ "name",
55
+ "priority",
56
+ "type",
57
+ ]);
58
+ const translationSettingsFields = new Set<TranslationSettingsField>(
59
+ TRANSLATION_SETTINGS_FIELDS,
60
+ );
61
+ function buildIntegrationAuditRecord(
62
+ metadata: AuditMetadata,
63
+ action: IntegrationAction,
64
+ targetId: string,
65
+ attributes: Parameters<typeof buildAuditRecord>[2] = {},
66
+ ): AuditRecord {
67
+ return buildAuditRecord(
68
+ metadata,
69
+ { action, target_type: AUDIT_CATALOG[action], target_id: targetId },
70
+ attributes,
71
+ );
72
+ }
73
+ function requireFields(
74
+ record: AuditRecord,
75
+ allowed: ReadonlySet<string>,
76
+ ): void {
77
+ const fields = record.changed_fields;
78
+ if (
79
+ !fields?.length ||
80
+ fields.some(
81
+ (field, index) =>
82
+ !allowed.has(field) || (index > 0 && fields[index - 1] >= field),
83
+ )
84
+ )
85
+ throw new TypeError("invalid or non-canonical changed_fields");
86
+ }
87
+ function requireNoExtra(record: AuditRecord, allowed: readonly string[]): void {
88
+ assertOnlyAuditAttributes(record, allowed);
89
+ }
90
+ /** Returns true only for the reviewed Platform Integrations catalog actions. */
91
+ export function validateIntegrationAuditRecord(record: AuditRecord): boolean {
92
+ if (!integrationActions.has(record.action as IntegrationAction)) return false;
93
+ requireCatalogTarget(record.action, record.target_type, record.target_id);
94
+ switch (record.action) {
95
+ case "mail_adapter.created":
96
+ case "mail_adapter.deleted":
97
+ case "translation_provider.created":
98
+ case "translation_provider.deleted":
99
+ requireNoExtra(record, []);
100
+ return true;
101
+ case "mail_adapter.updated":
102
+ case "translation_provider.updated":
103
+ requireFields(record, adapterConfigFields);
104
+ requireNoExtra(record, ["changed_fields"]);
105
+ return true;
106
+ case "email_suppression.updated":
107
+ if (
108
+ record.changed_fields?.length !== 1 ||
109
+ record.changed_fields[0] !== "status" ||
110
+ record.previous_state !== "active" ||
111
+ record.new_state !== "released"
112
+ )
113
+ throw new TypeError(
114
+ "email suppression requires active to released status",
115
+ );
116
+ requireNoExtra(record, ["changed_fields", "previous_state", "new_state"]);
117
+ return true;
118
+ case "translation_settings.updated":
119
+ if (record.target_id !== "1")
120
+ throw new TypeError("translation settings target must be 1");
121
+ requireFields(record, translationSettingsFields);
122
+ requireNoExtra(record, ["changed_fields"]);
123
+ return true;
124
+ }
125
+ /* v8 ignore next -- IntegrationAction is exhaustive above. */
126
+ throw new TypeError(`unsupported integration audit action ${record.action}`);
127
+ }
128
+
129
+ export function buildMailAdapterCreatedAuditRecord(
130
+ metadata: AuditMetadata,
131
+ id: string,
132
+ ): AuditRecord {
133
+ return buildIntegrationAuditRecord(metadata, "mail_adapter.created", id);
134
+ }
135
+ export function buildMailAdapterConfigUpdatedAuditRecord(
136
+ metadata: AuditMetadata,
137
+ id: string,
138
+ fields: readonly AdapterConfigField[],
139
+ ): AuditRecord {
140
+ return buildIntegrationAuditRecord(metadata, "mail_adapter.updated", id, {
141
+ changed_fields: canonicalAuditValues(fields),
142
+ });
143
+ }
144
+ export function buildMailAdapterDeletedAuditRecord(
145
+ metadata: AuditMetadata,
146
+ id: string,
147
+ ): AuditRecord {
148
+ return buildIntegrationAuditRecord(metadata, "mail_adapter.deleted", id);
149
+ }
150
+ export function buildEmailSuppressionReleasedAuditRecord(
151
+ metadata: AuditMetadata,
152
+ id: string,
153
+ ): AuditRecord {
154
+ return buildIntegrationAuditRecord(
155
+ metadata,
156
+ "email_suppression.updated",
157
+ id,
158
+ {
159
+ changed_fields: ["status"],
160
+ previous_state: "active",
161
+ new_state: "released",
162
+ },
163
+ );
164
+ }
165
+ export function buildTranslationSettingsUpdatedAuditRecord(
166
+ metadata: AuditMetadata,
167
+ fields: readonly TranslationSettingsField[],
168
+ ): AuditRecord {
169
+ return buildIntegrationAuditRecord(
170
+ metadata,
171
+ "translation_settings.updated",
172
+ "1",
173
+ { changed_fields: canonicalAuditValues(fields) },
174
+ );
175
+ }
176
+ export function buildTranslationProviderCreatedAuditRecord(
177
+ metadata: AuditMetadata,
178
+ id: string,
179
+ ): AuditRecord {
180
+ return buildIntegrationAuditRecord(
181
+ metadata,
182
+ "translation_provider.created",
183
+ id,
184
+ );
185
+ }
186
+ export function buildTranslationProviderConfigUpdatedAuditRecord(
187
+ metadata: AuditMetadata,
188
+ id: string,
189
+ fields: readonly AdapterConfigField[],
190
+ ): AuditRecord {
191
+ return buildIntegrationAuditRecord(
192
+ metadata,
193
+ "translation_provider.updated",
194
+ id,
195
+ { changed_fields: canonicalAuditValues(fields) },
196
+ );
197
+ }
198
+ export function buildTranslationProviderDeletedAuditRecord(
199
+ metadata: AuditMetadata,
200
+ id: string,
201
+ ): AuditRecord {
202
+ return buildIntegrationAuditRecord(
203
+ metadata,
204
+ "translation_provider.deleted",
205
+ id,
206
+ );
207
+ }
@@ -0,0 +1,6 @@
1
+ export function isCanonicalAuditLocale(value: string | undefined): boolean {
2
+ const code = value ?? "";
3
+ return (
4
+ code.length <= 64 && /^[A-Za-z0-9]{1,8}(-[A-Za-z0-9]{1,8})*$/.test(code)
5
+ );
6
+ }
@@ -0,0 +1,358 @@
1
+ import type {
2
+ AuditAction,
3
+ AuditItemOperation,
4
+ AuditRecord,
5
+ } from "../records.ts";
6
+ import { assertOnlyAuditAttributes } from "./attributes.ts";
7
+ import { buildAuditRecord } from "./builder.ts";
8
+ import { AUDIT_CATALOG, requireCatalogTarget } from "./catalog.ts";
9
+ import { isCanonicalAuditLocale } from "./locale-code.ts";
10
+ import type { AuditMetadata } from "./types.ts";
11
+
12
+ type LocaleContentAction = Extract<
13
+ AuditAction,
14
+ | "post.updated"
15
+ | "page.updated"
16
+ | "work.updated"
17
+ | "post_series.updated"
18
+ | "program_event.updated"
19
+ | "release.updated"
20
+ | "artist.updated"
21
+ | "label.updated"
22
+ | "menu.updated"
23
+ | "campaign.updated"
24
+ | "form.updated"
25
+ | "email_template.updated"
26
+ | "email_layout.updated"
27
+ | "legal_policy.updated"
28
+ >;
29
+
30
+ const localeContentActions = new Set<LocaleContentAction>([
31
+ "post.updated",
32
+ "page.updated",
33
+ "work.updated",
34
+ "post_series.updated",
35
+ "program_event.updated",
36
+ "release.updated",
37
+ "artist.updated",
38
+ "label.updated",
39
+ "menu.updated",
40
+ "campaign.updated",
41
+ "form.updated",
42
+ "email_template.updated",
43
+ "email_layout.updated",
44
+ "legal_policy.updated",
45
+ ]);
46
+
47
+ /** Recognizes exactly one locale-document create, update, or delete. */
48
+ export function validateLocaleContentAuditRecord(record: AuditRecord): boolean {
49
+ if (
50
+ record.changed_fields?.length !== 1 ||
51
+ record.changed_fields[0] !== "locale_content"
52
+ )
53
+ return false;
54
+ if (!localeContentActions.has(record.action as LocaleContentAction))
55
+ throw new TypeError(
56
+ `audit action ${record.action} does not support locale_content`,
57
+ );
58
+ requireCatalogTarget(record.action, record.target_type, record.target_id);
59
+ if (record.actor_kind !== "member")
60
+ throw new TypeError("locale_content requires a member actor");
61
+ if (!isCanonicalAuditLocale(record.locale))
62
+ throw new TypeError("locale_content requires a bounded locale");
63
+ if (
64
+ record.item_operation !== "created" &&
65
+ record.item_operation !== "updated" &&
66
+ record.item_operation !== "deleted"
67
+ )
68
+ throw new TypeError(
69
+ "locale_content requires created, updated, or deleted item operation",
70
+ );
71
+ const allowed = ["changed_fields", "locale", "item_operation"];
72
+ if (record.action === "legal_policy.updated") {
73
+ if (
74
+ (record.policy_type !== "terms" && record.policy_type !== "privacy") ||
75
+ record.version_number === undefined ||
76
+ !Number.isSafeInteger(record.version_number) ||
77
+ record.version_number < 1
78
+ )
79
+ throw new TypeError(
80
+ "legal policy requires policy_type and version_number",
81
+ );
82
+ allowed.push("policy_type", "version_number");
83
+ }
84
+ assertOnlyAuditAttributes(record, allowed);
85
+ return true;
86
+ }
87
+
88
+ function buildLocaleContentAuditRecord(
89
+ metadata: AuditMetadata,
90
+ action: Exclude<LocaleContentAction, "legal_policy.updated">,
91
+ id: string,
92
+ locale: string,
93
+ operation: AuditItemOperation,
94
+ ): AuditRecord {
95
+ return buildAuditRecord(
96
+ metadata,
97
+ { action, target_type: AUDIT_CATALOG[action], target_id: id },
98
+ {
99
+ changed_fields: ["locale_content"],
100
+ locale,
101
+ item_operation: operation,
102
+ },
103
+ );
104
+ }
105
+
106
+ export function buildPostLocaleContentAuditRecord(
107
+ m: AuditMetadata,
108
+ id: string,
109
+ locale: string,
110
+ operation: AuditItemOperation,
111
+ ): AuditRecord {
112
+ return buildLocaleContentAuditRecord(
113
+ m,
114
+ "post.updated",
115
+ id,
116
+ locale,
117
+ operation,
118
+ );
119
+ }
120
+
121
+ export function buildPageLocaleContentAuditRecord(
122
+ m: AuditMetadata,
123
+ id: string,
124
+ locale: string,
125
+ operation: AuditItemOperation,
126
+ ): AuditRecord {
127
+ return buildLocaleContentAuditRecord(
128
+ m,
129
+ "page.updated",
130
+ id,
131
+ locale,
132
+ operation,
133
+ );
134
+ }
135
+
136
+ export function buildWorkLocaleContentAuditRecord(
137
+ m: AuditMetadata,
138
+ id: string,
139
+ locale: string,
140
+ operation: AuditItemOperation,
141
+ ): AuditRecord {
142
+ return buildLocaleContentAuditRecord(
143
+ m,
144
+ "work.updated",
145
+ id,
146
+ locale,
147
+ operation,
148
+ );
149
+ }
150
+
151
+ export function buildPostSeriesLocaleContentAuditRecord(
152
+ m: AuditMetadata,
153
+ id: string,
154
+ locale: string,
155
+ operation: AuditItemOperation,
156
+ ): AuditRecord {
157
+ return buildLocaleContentAuditRecord(
158
+ m,
159
+ "post_series.updated",
160
+ id,
161
+ locale,
162
+ operation,
163
+ );
164
+ }
165
+
166
+ export function buildProgramEventLocaleContentAuditRecord(
167
+ m: AuditMetadata,
168
+ id: string,
169
+ locale: string,
170
+ operation: AuditItemOperation,
171
+ ): AuditRecord {
172
+ return buildLocaleContentAuditRecord(
173
+ m,
174
+ "program_event.updated",
175
+ id,
176
+ locale,
177
+ operation,
178
+ );
179
+ }
180
+
181
+ export function buildReleaseLocaleContentAuditRecord(
182
+ m: AuditMetadata,
183
+ id: string,
184
+ locale: string,
185
+ operation: AuditItemOperation,
186
+ ): AuditRecord {
187
+ return buildLocaleContentAuditRecord(
188
+ m,
189
+ "release.updated",
190
+ id,
191
+ locale,
192
+ operation,
193
+ );
194
+ }
195
+
196
+ export function buildArtistLocaleContentAuditRecord(
197
+ m: AuditMetadata,
198
+ id: string,
199
+ locale: string,
200
+ operation: AuditItemOperation,
201
+ ): AuditRecord {
202
+ return buildLocaleContentAuditRecord(
203
+ m,
204
+ "artist.updated",
205
+ id,
206
+ locale,
207
+ operation,
208
+ );
209
+ }
210
+
211
+ export function buildLabelLocaleContentAuditRecord(
212
+ m: AuditMetadata,
213
+ id: string,
214
+ locale: string,
215
+ operation: AuditItemOperation,
216
+ ): AuditRecord {
217
+ return buildLocaleContentAuditRecord(
218
+ m,
219
+ "label.updated",
220
+ id,
221
+ locale,
222
+ operation,
223
+ );
224
+ }
225
+
226
+ export function buildMenuLocaleContentAuditRecord(
227
+ m: AuditMetadata,
228
+ id: string,
229
+ locale: string,
230
+ operation: AuditItemOperation,
231
+ ): AuditRecord {
232
+ return buildLocaleContentAuditRecord(
233
+ m,
234
+ "menu.updated",
235
+ id,
236
+ locale,
237
+ operation,
238
+ );
239
+ }
240
+
241
+ export function buildCampaignLocaleContentAuditRecord(
242
+ m: AuditMetadata,
243
+ id: string,
244
+ locale: string,
245
+ operation: AuditItemOperation,
246
+ ): AuditRecord {
247
+ return buildLocaleContentAuditRecord(
248
+ m,
249
+ "campaign.updated",
250
+ id,
251
+ locale,
252
+ operation,
253
+ );
254
+ }
255
+
256
+ export function buildFormLocaleContentAuditRecord(
257
+ m: AuditMetadata,
258
+ id: string,
259
+ locale: string,
260
+ operation: AuditItemOperation,
261
+ ): AuditRecord {
262
+ return buildLocaleContentAuditRecord(
263
+ m,
264
+ "form.updated",
265
+ id,
266
+ locale,
267
+ operation,
268
+ );
269
+ }
270
+
271
+ export function buildEmailTemplateLocaleContentAuditRecord(
272
+ m: AuditMetadata,
273
+ id: string,
274
+ locale: string,
275
+ operation: AuditItemOperation,
276
+ ): AuditRecord {
277
+ return buildLocaleContentAuditRecord(
278
+ m,
279
+ "email_template.updated",
280
+ id,
281
+ locale,
282
+ operation,
283
+ );
284
+ }
285
+
286
+ export function buildEmailLayoutLocaleContentAuditRecord(
287
+ m: AuditMetadata,
288
+ id: string,
289
+ locale: string,
290
+ operation: AuditItemOperation,
291
+ ): AuditRecord {
292
+ return buildLocaleContentAuditRecord(
293
+ m,
294
+ "email_layout.updated",
295
+ id,
296
+ locale,
297
+ operation,
298
+ );
299
+ }
300
+
301
+ function buildLegalPolicyLocaleContentAuditRecord(
302
+ metadata: AuditMetadata,
303
+ id: string,
304
+ policyType: "terms" | "privacy",
305
+ versionNumber: number,
306
+ locale: string,
307
+ operation: AuditItemOperation,
308
+ ): AuditRecord {
309
+ return buildAuditRecord(
310
+ metadata,
311
+ {
312
+ action: "legal_policy.updated",
313
+ target_type: AUDIT_CATALOG["legal_policy.updated"],
314
+ target_id: id,
315
+ },
316
+ {
317
+ policy_type: policyType,
318
+ version_number: versionNumber,
319
+ changed_fields: ["locale_content"],
320
+ locale,
321
+ item_operation: operation,
322
+ },
323
+ );
324
+ }
325
+
326
+ export function buildPrivacyLocaleContentAuditRecord(
327
+ m: AuditMetadata,
328
+ id: string,
329
+ versionNumber: number,
330
+ locale: string,
331
+ operation: AuditItemOperation,
332
+ ): AuditRecord {
333
+ return buildLegalPolicyLocaleContentAuditRecord(
334
+ m,
335
+ id,
336
+ "privacy",
337
+ versionNumber,
338
+ locale,
339
+ operation,
340
+ );
341
+ }
342
+
343
+ export function buildTermsLocaleContentAuditRecord(
344
+ m: AuditMetadata,
345
+ id: string,
346
+ versionNumber: number,
347
+ locale: string,
348
+ operation: AuditItemOperation,
349
+ ): AuditRecord {
350
+ return buildLegalPolicyLocaleContentAuditRecord(
351
+ m,
352
+ id,
353
+ "terms",
354
+ versionNumber,
355
+ locale,
356
+ operation,
357
+ );
358
+ }