Spaces:
Running
Running
File size: 5,196 Bytes
aa89cb5 0ccd578 aa89cb5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | export type RuntimeOperationFamily =
| 'query'
| 'mutation'
| 'relation_load'
| 'provider'
| 'cache'
| 'tfp'
| 'audit'
| 'object_storage';
export type RuntimeAttributeValue = string | number | boolean;
export type RuntimeErrorCategory =
| 'validation' | 'authorization' | 'conflict' | 'timeout'
| 'transport' | 'provider' | 'internal';
/** Stable classification derived from a native error type, never its message. */
export function runtimeErrorCategory(error: unknown): RuntimeErrorCategory {
const type = error instanceof Error && error.constructor?.name
? error.constructor.name.toLowerCase()
: typeof error;
if (/timeout|deadline/.test(type)) return 'timeout';
if (/authentication|authorization|unauthorized|forbidden|permission/.test(type)) return 'authorization';
if (/validation|invalidargument|valueerror|parse|format/.test(type)) return 'validation';
if (/conflict|optimistic|version|duplicate|alreadyexists/.test(type)) return 'conflict';
if (/transport|network|connection|socket|http|ioerror/.test(type)) return 'transport';
if (/provider|sql|database|jdbc/.test(type)) return 'provider';
return 'internal';
}
export interface RuntimeOperation {
readonly family: RuntimeOperationFamily;
readonly name: string;
readonly attributes?: Readonly<Record<string, RuntimeAttributeValue>>;
}
export interface RuntimeOperationCompletion {
readonly attributes?: Readonly<Record<string, RuntimeAttributeValue>>;
}
export interface RuntimeExecutionCorrelation {
readonly traceId: string;
readonly spanId: string;
}
/** Only runtime-projected, allow-listed diagnostics may enter this event. */
export interface RuntimeDiagnosticEvent {
readonly body: 'TeaQL SQL execution';
readonly correlation?: RuntimeExecutionCorrelation;
readonly attributes: Readonly<Record<string, RuntimeAttributeValue>>;
}
export interface RuntimeTelemetryScope {
success(completion?: RuntimeOperationCompletion): void;
failure(error: unknown): void;
run?<T>(work: () => Promise<T>): Promise<T>;
correlation?(): RuntimeExecutionCorrelation | undefined;
}
/** Provider-neutral TeaQL lifecycle contract. Implementations must be fail open. */
export interface RuntimeTelemetry {
recordsSQLDiagnostics?(): boolean;
start(operation: RuntimeOperation): RuntimeTelemetryScope;
inject?(carrier: Record<string, string>): void;
flush?(): void | Promise<void>;
shutdown?(): void | Promise<void>;
recordDiagnostic?(event: RuntimeDiagnosticEvent): void;
}
/** Inject the active runtime trace into transport metadata without affecting business work. */
export function injectRuntimeContext(
telemetry: RuntimeTelemetry | undefined,
carrier: Record<string, string>,
): Record<string, string> {
try { telemetry?.inject?.(carrier); } catch { /* telemetry is fail open */ }
return carrier;
}
const noopScope: RuntimeTelemetryScope = Object.freeze({
success: () => undefined,
failure: () => undefined,
});
export const NOOP_RUNTIME_TELEMETRY: RuntimeTelemetry = Object.freeze({
start: () => noopScope,
});
const forbiddenAttributes = new Set([
'teaql.entity.id',
'teaql.user.id',
'teaql.tenant.id',
'teaql.query.parameters',
'teaql.field.values',
'teaql.audit.reason',
'db.query.parameter_values',
'http.request.body',
'url.full',
]);
export function safeRuntimeOperation(operation: RuntimeOperation): RuntimeOperation {
const attributes: Record<string, RuntimeAttributeValue> = {
'teaql.operation.family': operation.family,
'teaql.operation.name': operation.name,
};
for (const [key, value] of Object.entries(operation.attributes || {})) {
if (!forbiddenAttributes.has(key)) attributes[key] = value;
}
return Object.freeze({ ...operation, attributes: Object.freeze(attributes) });
}
export function startRuntimeOperation(
telemetry: RuntimeTelemetry | undefined,
operation: RuntimeOperation,
): RuntimeTelemetryScope {
if (!telemetry) return noopScope;
try {
const delegate = telemetry.start(safeRuntimeOperation(operation));
let ended = false;
return {
correlation: () => {
try { return delegate.correlation?.(); } catch { return undefined; }
},
run: delegate.run
? <T>(work: () => Promise<T>) => delegate.run!(work)
: <T>(work: () => Promise<T>) => work(),
success(completion) {
if (ended) return;
ended = true;
try { delegate?.success(completion); } catch { /* telemetry is fail open */ }
},
failure(error) {
if (ended) return;
ended = true;
try { delegate?.failure(error); } catch { /* telemetry is fail open */ }
},
};
} catch {
return noopScope;
}
}
export async function observeRuntimeOperation<T>(
telemetry: RuntimeTelemetry | undefined,
operation: RuntimeOperation,
work: () => Promise<T>,
completion?: (result: T) => RuntimeOperationCompletion,
): Promise<T> {
const scope = startRuntimeOperation(telemetry, operation);
try {
const result = await (scope.run ? scope.run(work) : work());
scope.success(completion?.(result));
return result;
} catch (error) {
scope.failure(error);
throw error;
}
}
|