autotel 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -2
- package/src/attribute-redacting-processor.test.ts +0 -763
- package/src/attribute-redacting-processor.ts +0 -621
- package/src/attributes/attachers.ts +0 -161
- package/src/attributes/builders.ts +0 -529
- package/src/attributes/domains.ts +0 -42
- package/src/attributes/index.ts +0 -81
- package/src/attributes/registry.ts +0 -323
- package/src/attributes/types.ts +0 -211
- package/src/attributes/utils.ts +0 -64
- package/src/attributes/validators.ts +0 -266
- package/src/attributes.test.ts +0 -292
- package/src/auto.ts +0 -67
- package/src/autotel-logger.test.ts +0 -548
- package/src/autotel-logger.ts +0 -364
- package/src/baggage-span-processor.test.ts +0 -202
- package/src/baggage-span-processor.ts +0 -100
- package/src/business-baggage.test.ts +0 -500
- package/src/business-baggage.ts +0 -669
- package/src/circuit-breaker.test.ts +0 -341
- package/src/circuit-breaker.ts +0 -184
- package/src/config.test.ts +0 -94
- package/src/config.ts +0 -172
- package/src/correlated-events.test.ts +0 -151
- package/src/correlated-events.ts +0 -47
- package/src/correlation-id.test.ts +0 -163
- package/src/correlation-id.ts +0 -206
- package/src/db.test.ts +0 -252
- package/src/db.ts +0 -447
- package/src/decorators.test.ts +0 -153
- package/src/decorators.ts +0 -188
- package/src/define-event.test.ts +0 -41
- package/src/define-event.ts +0 -58
- package/src/devtools.ts +0 -60
- package/src/drain-pipeline.test.ts +0 -68
- package/src/drain-pipeline.ts +0 -199
- package/src/drain-toolkit.test.ts +0 -113
- package/src/drain-toolkit.ts +0 -129
- package/src/enricher-toolkit.test.ts +0 -67
- package/src/enricher-toolkit.ts +0 -79
- package/src/enrichers.test.ts +0 -150
- package/src/enrichers.ts +0 -145
- package/src/env-config.test.ts +0 -323
- package/src/env-config.ts +0 -309
- package/src/error-catalog.test.ts +0 -133
- package/src/error-catalog.ts +0 -262
- package/src/event-queue.test.ts +0 -864
- package/src/event-queue.ts +0 -699
- package/src/event-subscriber.ts +0 -262
- package/src/event-testing.ts +0 -197
- package/src/event.test.ts +0 -1104
- package/src/event.ts +0 -988
- package/src/events-config.ts +0 -235
- package/src/exporters.ts +0 -165
- package/src/filtering-span-processor.test.ts +0 -281
- package/src/filtering-span-processor.ts +0 -111
- package/src/flatten-attributes.test.ts +0 -76
- package/src/flatten-attributes.ts +0 -80
- package/src/functional.strict-types.typecheck.ts +0 -53
- package/src/functional.test.ts +0 -1464
- package/src/functional.ts +0 -2539
- package/src/functional.types.test.ts +0 -135
- package/src/hook.mjs +0 -15
- package/src/http.test.ts +0 -485
- package/src/http.ts +0 -424
- package/src/index.ts +0 -433
- package/src/init-auto-redactor.test.ts +0 -53
- package/src/init-redactor.test.ts +0 -8
- package/src/init.customization.test.ts +0 -665
- package/src/init.integrations.test.ts +0 -399
- package/src/init.openllmetry.test.ts +0 -194
- package/src/init.protocol.test.ts +0 -215
- package/src/init.ts +0 -2439
- package/src/instrumentation.test.ts +0 -108
- package/src/instrumentation.ts +0 -319
- package/src/logger.test.ts +0 -125
- package/src/logger.ts +0 -341
- package/src/messaging-adapters.test.ts +0 -595
- package/src/messaging-adapters.ts +0 -583
- package/src/messaging-testing.test.ts +0 -573
- package/src/messaging-testing.ts +0 -935
- package/src/messaging.test.ts +0 -1646
- package/src/messaging.ts +0 -2245
- package/src/metric-helpers.ts +0 -47
- package/src/metric-testing.ts +0 -197
- package/src/metric.ts +0 -446
- package/src/metrics.test.ts +0 -241
- package/src/node-require.ts +0 -123
- package/src/operation-context.ts +0 -93
- package/src/parse-error.test.ts +0 -73
- package/src/parse-error.ts +0 -112
- package/src/posthog-logs.test.ts +0 -115
- package/src/posthog-logs.ts +0 -77
- package/src/pretty-console-exporter.test.ts +0 -545
- package/src/pretty-console-exporter.ts +0 -413
- package/src/pretty-log-formatter.test.ts +0 -123
- package/src/pretty-log-formatter.ts +0 -210
- package/src/processors/canonical-log-line-processor.test.ts +0 -523
- package/src/processors/canonical-log-line-processor.ts +0 -396
- package/src/processors.ts +0 -152
- package/src/rate-limiter.test.ts +0 -199
- package/src/rate-limiter.ts +0 -98
- package/src/redact-values.test.ts +0 -90
- package/src/redact-values.ts +0 -34
- package/src/register.ts +0 -37
- package/src/request-logger.test.ts +0 -545
- package/src/request-logger.ts +0 -342
- package/src/sampling.test.ts +0 -1060
- package/src/sampling.ts +0 -737
- package/src/security-schema.test.ts +0 -45
- package/src/security-schema.ts +0 -107
- package/src/semantic-conventions.ts +0 -15
- package/src/semantic-helpers.test.ts +0 -226
- package/src/semantic-helpers.ts +0 -438
- package/src/shutdown.test.ts +0 -364
- package/src/shutdown.ts +0 -246
- package/src/span-name-normalizer.test.ts +0 -377
- package/src/span-name-normalizer.ts +0 -213
- package/src/stable-hash.ts +0 -27
- package/src/structured-error.test.ts +0 -191
- package/src/structured-error.ts +0 -157
- package/src/stub.integration.test.ts +0 -361
- package/src/tail-sampling-processor.test.ts +0 -230
- package/src/tail-sampling-processor.ts +0 -55
- package/src/test-span-collector.test.ts +0 -234
- package/src/test-span-collector.ts +0 -150
- package/src/testing.ts +0 -705
- package/src/trace-context.test.ts +0 -73
- package/src/trace-context.ts +0 -567
- package/src/trace-helpers.new.test.ts +0 -278
- package/src/trace-helpers.test.ts +0 -290
- package/src/trace-helpers.ts +0 -710
- package/src/trace-hybrid.test.ts +0 -42
- package/src/trace-hybrid.ts +0 -37
- package/src/tracer-provider.test.ts +0 -183
- package/src/tracer-provider.ts +0 -266
- package/src/track.test.ts +0 -154
- package/src/track.ts +0 -216
- package/src/validate.test.ts +0 -287
- package/src/validate.ts +0 -307
- package/src/validation-attributes.ts +0 -43
- package/src/validation.test.ts +0 -330
- package/src/validation.ts +0 -246
- package/src/variable-name-inference.test.ts +0 -178
- package/src/variable-name-inference.ts +0 -242
- package/src/webhook.test.ts +0 -649
- package/src/webhook.ts +0 -637
- package/src/workflow-distributed.test.ts +0 -786
- package/src/workflow-distributed.ts +0 -916
- package/src/workflow.async-safety.integration.test.ts +0 -345
- package/src/workflow.test.ts +0 -647
- package/src/workflow.ts +0 -810
- package/src/yaml-config.test.ts +0 -373
- package/src/yaml-config.ts +0 -351
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
defineErrorCatalog,
|
|
4
|
-
defineAuditCatalog,
|
|
5
|
-
isCatalogError,
|
|
6
|
-
getCatalogCode,
|
|
7
|
-
} from './error-catalog';
|
|
8
|
-
|
|
9
|
-
describe('defineErrorCatalog', () => {
|
|
10
|
-
const billing = defineErrorCatalog('billing', {
|
|
11
|
-
PAYMENT_DECLINED: {
|
|
12
|
-
status: 402,
|
|
13
|
-
message: 'Card declined',
|
|
14
|
-
why: 'The issuer rejected the charge',
|
|
15
|
-
fix: 'Try a different payment method',
|
|
16
|
-
link: 'https://docs.example.com/billing',
|
|
17
|
-
},
|
|
18
|
-
INSUFFICIENT_FUNDS: {
|
|
19
|
-
status: 402,
|
|
20
|
-
message: ({
|
|
21
|
-
available,
|
|
22
|
-
required,
|
|
23
|
-
}: {
|
|
24
|
-
available: number;
|
|
25
|
-
required: number;
|
|
26
|
-
}) => `Insufficient funds: $${available} of $${required}`,
|
|
27
|
-
why: ({ required }: { available: number; required: number }) =>
|
|
28
|
-
`Needs $${required}`,
|
|
29
|
-
},
|
|
30
|
-
LEGACY: {
|
|
31
|
-
code: 'BILLING_LEGACY_42',
|
|
32
|
-
message: 'Legacy failure',
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('builds a structured error from a static entry', () => {
|
|
37
|
-
const err = billing.PAYMENT_DECLINED();
|
|
38
|
-
expect(err).toBeInstanceOf(Error);
|
|
39
|
-
expect(err.message).toBe('Card declined');
|
|
40
|
-
expect(err.status).toBe(402);
|
|
41
|
-
expect(err.why).toBe('The issuer rejected the charge');
|
|
42
|
-
expect(err.fix).toBe('Try a different payment method');
|
|
43
|
-
expect(err.link).toBe('https://docs.example.com/billing');
|
|
44
|
-
expect(err.code).toBe('billing.PAYMENT_DECLINED');
|
|
45
|
-
expect(err.name).toBe('PAYMENT_DECLINED');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('interpolates typed params in message and why', () => {
|
|
49
|
-
const err = billing.INSUFFICIENT_FUNDS({ available: 5, required: 100 });
|
|
50
|
-
expect(err.message).toBe('Insufficient funds: $5 of $100');
|
|
51
|
-
expect(err.why).toBe('Needs $100');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('honors a custom code', () => {
|
|
55
|
-
const err = billing.LEGACY();
|
|
56
|
-
expect(err.code).toBe('BILLING_LEGACY_42');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('exposes the code on the builder', () => {
|
|
60
|
-
expect(billing.PAYMENT_DECLINED.code).toBe('billing.PAYMENT_DECLINED');
|
|
61
|
-
expect(billing.LEGACY.code).toBe('BILLING_LEGACY_42');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('matches its own errors and rejects others', () => {
|
|
65
|
-
const declined = billing.PAYMENT_DECLINED();
|
|
66
|
-
const funds = billing.INSUFFICIENT_FUNDS({ available: 1, required: 2 });
|
|
67
|
-
expect(billing.PAYMENT_DECLINED.match(declined)).toBe(true);
|
|
68
|
-
expect(billing.PAYMENT_DECLINED.match(funds)).toBe(false);
|
|
69
|
-
expect(billing.PAYMENT_DECLINED.match(new Error('nope'))).toBe(false);
|
|
70
|
-
expect(billing.PAYMENT_DECLINED.match(null)).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('tags errors so isCatalogError / getCatalogCode work', () => {
|
|
74
|
-
const err = billing.PAYMENT_DECLINED();
|
|
75
|
-
expect(isCatalogError(err)).toBe(true);
|
|
76
|
-
expect(isCatalogError(new Error('plain'))).toBe(false);
|
|
77
|
-
expect(getCatalogCode(err)).toBe('billing.PAYMENT_DECLINED');
|
|
78
|
-
expect(getCatalogCode(new Error('plain'))).toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('passes cause, details, and internal through build options', () => {
|
|
82
|
-
const cause = new Error('stripe boom');
|
|
83
|
-
const err = billing.PAYMENT_DECLINED({
|
|
84
|
-
cause,
|
|
85
|
-
details: { attempt: 2 },
|
|
86
|
-
internal: { stripeId: 'ch_1' },
|
|
87
|
-
});
|
|
88
|
-
expect(err.cause).toBe(cause);
|
|
89
|
-
expect(err.details).toEqual({ attempt: 2 });
|
|
90
|
-
expect(err.internal).toEqual({ stripeId: 'ch_1' });
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('accepts options as the second arg for param entries', () => {
|
|
94
|
-
const cause = new Error('root');
|
|
95
|
-
const err = billing.INSUFFICIENT_FUNDS(
|
|
96
|
-
{ available: 5, required: 100 },
|
|
97
|
-
{ cause },
|
|
98
|
-
);
|
|
99
|
-
expect(err.message).toBe('Insufficient funds: $5 of $100');
|
|
100
|
-
expect(err.cause).toBe(cause);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe('defineAuditCatalog', () => {
|
|
105
|
-
const audit = defineAuditCatalog('user', {
|
|
106
|
-
LOGIN: { message: 'User logged in' },
|
|
107
|
-
ROLE_CHANGED: {
|
|
108
|
-
severity: 'critical',
|
|
109
|
-
message: ({ role }: { role: string }) => `Role set to ${role}`,
|
|
110
|
-
},
|
|
111
|
-
DELETED: { action: 'user.account.deleted', severity: 'warn' },
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('produces typed action descriptors with defaults', () => {
|
|
115
|
-
const action = audit.LOGIN();
|
|
116
|
-
expect(action.action).toBe('user.LOGIN');
|
|
117
|
-
expect(action.severity).toBe('info');
|
|
118
|
-
expect(action.message).toBe('User logged in');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('interpolates params and respects severity', () => {
|
|
122
|
-
const action = audit.ROLE_CHANGED({ role: 'admin' });
|
|
123
|
-
expect(action.action).toBe('user.ROLE_CHANGED');
|
|
124
|
-
expect(action.severity).toBe('critical');
|
|
125
|
-
expect(action.message).toBe('Role set to admin');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('honors a custom action name', () => {
|
|
129
|
-
expect(audit.DELETED.action).toBe('user.account.deleted');
|
|
130
|
-
expect(audit.DELETED.severity).toBe('warn');
|
|
131
|
-
expect(audit.DELETED().message).toBeUndefined();
|
|
132
|
-
});
|
|
133
|
-
});
|
package/src/error-catalog.ts
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typed error and audit catalogs.
|
|
3
|
-
*
|
|
4
|
-
* Group related errors into one catalog and get a refactor-safe builder per
|
|
5
|
-
* code, with autocomplete at every call site and typed message parameters.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* import { defineErrorCatalog } from 'autotel';
|
|
10
|
-
*
|
|
11
|
-
* export const billing = defineErrorCatalog('billing', {
|
|
12
|
-
* PAYMENT_DECLINED: {
|
|
13
|
-
* status: 402,
|
|
14
|
-
* message: 'Card declined',
|
|
15
|
-
* why: 'The issuer rejected the charge',
|
|
16
|
-
* fix: 'Try a different payment method',
|
|
17
|
-
* },
|
|
18
|
-
* INSUFFICIENT_FUNDS: {
|
|
19
|
-
* status: 402,
|
|
20
|
-
* message: ({ available, required }: { available: number; required: number }) =>
|
|
21
|
-
* `Insufficient funds: $${available} of $${required}`,
|
|
22
|
-
* },
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* throw billing.PAYMENT_DECLINED({ cause: stripeError });
|
|
26
|
-
* throw billing.INSUFFICIENT_FUNDS({ available: 5, required: 100 });
|
|
27
|
-
*
|
|
28
|
-
* // In a catch block — refactor-safe, no magic strings:
|
|
29
|
-
* if (billing.PAYMENT_DECLINED.match(err)) { ... }
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import {
|
|
34
|
-
createStructuredError,
|
|
35
|
-
type StructuredError,
|
|
36
|
-
} from './structured-error';
|
|
37
|
-
|
|
38
|
-
const catalogCodeKey = Symbol.for('autotel.catalog.code');
|
|
39
|
-
|
|
40
|
-
/** Definition of a single error in a catalog. */
|
|
41
|
-
export interface ErrorCatalogEntry {
|
|
42
|
-
/**
|
|
43
|
-
* Human-readable message. Use a function to interpolate typed parameters;
|
|
44
|
-
* the parameter type flows through to the call site.
|
|
45
|
-
*/
|
|
46
|
-
message: string | ((params: never) => string);
|
|
47
|
-
/** HTTP status to surface to clients. */
|
|
48
|
-
status?: number;
|
|
49
|
-
/** Stable error code. Defaults to `${namespace}.${KEY}`. */
|
|
50
|
-
code?: string | number;
|
|
51
|
-
/** Why it happened. A function receives the same params as `message`. */
|
|
52
|
-
why?: string | ((params: never) => string);
|
|
53
|
-
/** What the caller should do next. */
|
|
54
|
-
fix?: string;
|
|
55
|
-
/** Docs or runbook link. */
|
|
56
|
-
link?: string;
|
|
57
|
-
/** Error name. Defaults to the catalog key. */
|
|
58
|
-
name?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Per-call options passed alongside (or instead of) typed params. */
|
|
62
|
-
export interface ErrorBuildOptions {
|
|
63
|
-
cause?: unknown;
|
|
64
|
-
details?: Record<string, unknown>;
|
|
65
|
-
/** Backend-only context. Never serialized to clients. */
|
|
66
|
-
internal?: Record<string, unknown>;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type ParamsOf<E> = E extends { message: (params: infer P) => string }
|
|
70
|
-
? P
|
|
71
|
-
: E extends { why: (params: infer P) => string }
|
|
72
|
-
? P
|
|
73
|
-
: void;
|
|
74
|
-
|
|
75
|
-
type BuilderArgs<E extends ErrorCatalogEntry> =
|
|
76
|
-
ParamsOf<E> extends void
|
|
77
|
-
? [options?: ErrorBuildOptions]
|
|
78
|
-
: [params: ParamsOf<E>, options?: ErrorBuildOptions];
|
|
79
|
-
|
|
80
|
-
/** A callable error factory produced by {@link defineErrorCatalog}. */
|
|
81
|
-
export interface ErrorBuilder<E extends ErrorCatalogEntry> {
|
|
82
|
-
(...args: BuilderArgs<E>): StructuredError;
|
|
83
|
-
/** Stable code assigned to every error from this entry. */
|
|
84
|
-
readonly code: string | number;
|
|
85
|
-
/** True when `error` was produced by this catalog entry. */
|
|
86
|
-
match(error: unknown): boolean;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export type ErrorCatalog<T extends Record<string, ErrorCatalogEntry>> = {
|
|
90
|
-
readonly [K in keyof T]: ErrorBuilder<T[K]>;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
function readCatalogCode(error: unknown): string | number | undefined {
|
|
94
|
-
if (error === null || typeof error !== 'object') return undefined;
|
|
95
|
-
return (error as Record<symbol, unknown>)[catalogCodeKey] as
|
|
96
|
-
| string
|
|
97
|
-
| number
|
|
98
|
-
| undefined;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** True when `error` was produced by any autotel error catalog. */
|
|
102
|
-
export function isCatalogError(error: unknown): error is StructuredError {
|
|
103
|
-
return readCatalogCode(error) !== undefined;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Returns the catalog code of `error`, or `undefined` if it has none. */
|
|
107
|
-
export function getCatalogCode(error: unknown): string | number | undefined {
|
|
108
|
-
return readCatalogCode(error);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Define a typed error catalog. Returns an object whose keys are error
|
|
113
|
-
* builders. Each builder produces a {@link StructuredError} carrying the
|
|
114
|
-
* entry's message, status, code, why, fix, and link.
|
|
115
|
-
*/
|
|
116
|
-
export function defineErrorCatalog<
|
|
117
|
-
const T extends Record<string, ErrorCatalogEntry>,
|
|
118
|
-
>(namespace: string, entries: T): ErrorCatalog<T> {
|
|
119
|
-
const catalog: Record<string, ErrorBuilder<ErrorCatalogEntry>> = {};
|
|
120
|
-
|
|
121
|
-
for (const [key, entry] of Object.entries(entries) as [
|
|
122
|
-
string,
|
|
123
|
-
ErrorCatalogEntry,
|
|
124
|
-
][]) {
|
|
125
|
-
const code = entry.code ?? `${namespace}.${key}`;
|
|
126
|
-
const usesParams =
|
|
127
|
-
typeof entry.message === 'function' || typeof entry.why === 'function';
|
|
128
|
-
|
|
129
|
-
const builder = ((
|
|
130
|
-
paramsOrOptions?: unknown,
|
|
131
|
-
maybeOptions?: ErrorBuildOptions,
|
|
132
|
-
): StructuredError => {
|
|
133
|
-
const params = usesParams ? paramsOrOptions : undefined;
|
|
134
|
-
const options = (usesParams ? maybeOptions : paramsOrOptions) as
|
|
135
|
-
| ErrorBuildOptions
|
|
136
|
-
| undefined;
|
|
137
|
-
|
|
138
|
-
const message =
|
|
139
|
-
typeof entry.message === 'function'
|
|
140
|
-
? (entry.message as (p: unknown) => string)(params)
|
|
141
|
-
: entry.message;
|
|
142
|
-
const why =
|
|
143
|
-
typeof entry.why === 'function'
|
|
144
|
-
? (entry.why as (p: unknown) => string)(params)
|
|
145
|
-
: entry.why;
|
|
146
|
-
|
|
147
|
-
const error = createStructuredError({
|
|
148
|
-
message,
|
|
149
|
-
name: entry.name ?? key,
|
|
150
|
-
code,
|
|
151
|
-
...(entry.status === undefined ? {} : { status: entry.status }),
|
|
152
|
-
...(why === undefined ? {} : { why }),
|
|
153
|
-
...(entry.fix === undefined ? {} : { fix: entry.fix }),
|
|
154
|
-
...(entry.link === undefined ? {} : { link: entry.link }),
|
|
155
|
-
...(options?.cause === undefined ? {} : { cause: options.cause }),
|
|
156
|
-
...(options?.details === undefined ? {} : { details: options.details }),
|
|
157
|
-
...(options?.internal === undefined
|
|
158
|
-
? {}
|
|
159
|
-
: { internal: options.internal }),
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
Object.defineProperty(error, catalogCodeKey, {
|
|
163
|
-
value: code,
|
|
164
|
-
enumerable: false,
|
|
165
|
-
writable: false,
|
|
166
|
-
configurable: true,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
return error;
|
|
170
|
-
}) as ErrorBuilder<ErrorCatalogEntry>;
|
|
171
|
-
|
|
172
|
-
Object.defineProperty(builder, 'code', {
|
|
173
|
-
value: code,
|
|
174
|
-
enumerable: true,
|
|
175
|
-
});
|
|
176
|
-
Object.defineProperty(builder, 'match', {
|
|
177
|
-
value: (error: unknown): boolean => readCatalogCode(error) === code,
|
|
178
|
-
enumerable: false,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
catalog[key] = builder;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return Object.freeze(catalog) as ErrorCatalog<T>;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/** Severity of an audit action. */
|
|
188
|
-
export type AuditSeverity = 'info' | 'warn' | 'critical';
|
|
189
|
-
|
|
190
|
-
/** Definition of a single action in an audit catalog. */
|
|
191
|
-
export interface AuditCatalogEntry {
|
|
192
|
-
/** Human-readable description. Use a function for typed params. */
|
|
193
|
-
message?: string | ((params: never) => string);
|
|
194
|
-
/** Stable action name. Defaults to `${namespace}.${KEY}`. */
|
|
195
|
-
action?: string;
|
|
196
|
-
/** Severity of the action. Defaults to `'info'`. */
|
|
197
|
-
severity?: AuditSeverity;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/** A resolved audit action descriptor produced by an audit catalog. */
|
|
201
|
-
export interface AuditAction {
|
|
202
|
-
readonly action: string;
|
|
203
|
-
readonly severity: AuditSeverity;
|
|
204
|
-
readonly message?: string;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
type AuditDescriptorArgs<E extends AuditCatalogEntry> =
|
|
208
|
-
ParamsOf<E> extends void ? [] : [params: ParamsOf<E>];
|
|
209
|
-
|
|
210
|
-
/** A callable audit-action descriptor produced by {@link defineAuditCatalog}. */
|
|
211
|
-
export interface AuditDescriptor<E extends AuditCatalogEntry> {
|
|
212
|
-
(...args: AuditDescriptorArgs<E>): AuditAction;
|
|
213
|
-
readonly action: string;
|
|
214
|
-
readonly severity: AuditSeverity;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export type AuditCatalog<T extends Record<string, AuditCatalogEntry>> = {
|
|
218
|
-
readonly [K in keyof T]: AuditDescriptor<T[K]>;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Define a typed audit catalog. Returns typed action descriptors you can pass
|
|
223
|
-
* to `track()` or audit helpers without scattering magic strings.
|
|
224
|
-
*/
|
|
225
|
-
export function defineAuditCatalog<
|
|
226
|
-
const T extends Record<string, AuditCatalogEntry>,
|
|
227
|
-
>(namespace: string, entries: T): AuditCatalog<T> {
|
|
228
|
-
const catalog: Record<string, AuditDescriptor<AuditCatalogEntry>> = {};
|
|
229
|
-
|
|
230
|
-
for (const [key, entry] of Object.entries(entries) as [
|
|
231
|
-
string,
|
|
232
|
-
AuditCatalogEntry,
|
|
233
|
-
][]) {
|
|
234
|
-
const action = entry.action ?? `${namespace}.${key}`;
|
|
235
|
-
const severity: AuditSeverity = entry.severity ?? 'info';
|
|
236
|
-
|
|
237
|
-
const descriptor = ((params?: unknown): AuditAction => {
|
|
238
|
-
const message =
|
|
239
|
-
typeof entry.message === 'function'
|
|
240
|
-
? (entry.message as (p: unknown) => string)(params)
|
|
241
|
-
: entry.message;
|
|
242
|
-
return Object.freeze({
|
|
243
|
-
action,
|
|
244
|
-
severity,
|
|
245
|
-
...(message === undefined ? {} : { message }),
|
|
246
|
-
});
|
|
247
|
-
}) as AuditDescriptor<AuditCatalogEntry>;
|
|
248
|
-
|
|
249
|
-
Object.defineProperty(descriptor, 'action', {
|
|
250
|
-
value: action,
|
|
251
|
-
enumerable: true,
|
|
252
|
-
});
|
|
253
|
-
Object.defineProperty(descriptor, 'severity', {
|
|
254
|
-
value: severity,
|
|
255
|
-
enumerable: true,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
catalog[key] = descriptor;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return Object.freeze(catalog) as AuditCatalog<T>;
|
|
262
|
-
}
|