@databricks/appkit 0.1.5 → 0.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/AGENTS.md +52 -0
- package/CLAUDE.md +52 -0
- package/NOTICE.md +2 -0
- package/README.md +21 -15
- package/bin/appkit-lint.js +129 -0
- package/dist/analytics/analytics.d.ts.map +1 -1
- package/dist/analytics/analytics.js +16 -3
- package/dist/analytics/analytics.js.map +1 -1
- package/dist/analytics/query.js +8 -2
- package/dist/analytics/query.js.map +1 -1
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +7 -5
- package/dist/app/index.js.map +1 -1
- package/dist/appkit/package.js +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +24 -3
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/storage/persistent.js +12 -6
- package/dist/cache/storage/persistent.js.map +1 -1
- package/dist/connectors/lakebase/client.js +25 -14
- package/dist/connectors/lakebase/client.js.map +1 -1
- package/dist/connectors/sql-warehouse/client.js +68 -28
- package/dist/connectors/sql-warehouse/client.js.map +1 -1
- package/dist/context/service-context.js +13 -8
- package/dist/context/service-context.js.map +1 -1
- package/dist/errors/authentication.d.ts +38 -0
- package/dist/errors/authentication.d.ts.map +1 -0
- package/dist/errors/authentication.js +48 -0
- package/dist/errors/authentication.js.map +1 -0
- package/dist/errors/base.d.ts +58 -0
- package/dist/errors/base.d.ts.map +1 -0
- package/dist/errors/base.js +70 -0
- package/dist/errors/base.js.map +1 -0
- package/dist/errors/configuration.d.ts +38 -0
- package/dist/errors/configuration.d.ts.map +1 -0
- package/dist/errors/configuration.js +45 -0
- package/dist/errors/configuration.js.map +1 -0
- package/dist/errors/connection.d.ts +42 -0
- package/dist/errors/connection.d.ts.map +1 -0
- package/dist/errors/connection.js +54 -0
- package/dist/errors/connection.js.map +1 -0
- package/dist/errors/execution.d.ts +42 -0
- package/dist/errors/execution.d.ts.map +1 -0
- package/dist/errors/execution.js +51 -0
- package/dist/errors/execution.js.map +1 -0
- package/dist/errors/index.js +28 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/initialization.d.ts +34 -0
- package/dist/errors/initialization.d.ts.map +1 -0
- package/dist/errors/initialization.js +42 -0
- package/dist/errors/initialization.js.map +1 -0
- package/dist/errors/server.d.ts +38 -0
- package/dist/errors/server.d.ts.map +1 -0
- package/dist/errors/server.js +45 -0
- package/dist/errors/server.js.map +1 -0
- package/dist/errors/tunnel.d.ts +38 -0
- package/dist/errors/tunnel.d.ts.map +1 -0
- package/dist/errors/tunnel.js +51 -0
- package/dist/errors/tunnel.js.map +1 -0
- package/dist/errors/validation.d.ts +36 -0
- package/dist/errors/validation.d.ts.map +1 -0
- package/dist/errors/validation.js +45 -0
- package/dist/errors/validation.js.map +1 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +18 -3
- package/dist/index.js.map +1 -0
- package/dist/logging/logger.js +179 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/sampling.js +56 -0
- package/dist/logging/sampling.js.map +1 -0
- package/dist/logging/wide-event-emitter.js +108 -0
- package/dist/logging/wide-event-emitter.js.map +1 -0
- package/dist/logging/wide-event.js +167 -0
- package/dist/logging/wide-event.js.map +1 -0
- package/dist/plugin/dev-reader.d.ts.map +1 -1
- package/dist/plugin/dev-reader.js +8 -3
- package/dist/plugin/dev-reader.js.map +1 -1
- package/dist/plugin/interceptors/cache.js.map +1 -1
- package/dist/plugin/interceptors/retry.js +10 -2
- package/dist/plugin/interceptors/retry.js.map +1 -1
- package/dist/plugin/interceptors/telemetry.js +24 -9
- package/dist/plugin/interceptors/telemetry.js.map +1 -1
- package/dist/plugin/interceptors/timeout.js +4 -0
- package/dist/plugin/interceptors/timeout.js.map +1 -1
- package/dist/plugin/plugin.d.ts +1 -1
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +9 -4
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +22 -17
- package/dist/server/index.js.map +1 -1
- package/dist/server/remote-tunnel/remote-tunnel-controller.js +4 -2
- package/dist/server/remote-tunnel/remote-tunnel-controller.js.map +1 -1
- package/dist/server/remote-tunnel/remote-tunnel-manager.js +10 -8
- package/dist/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
- package/dist/server/vite-dev-server.js +8 -3
- package/dist/server/vite-dev-server.js.map +1 -1
- package/dist/stream/arrow-stream-processor.js +13 -6
- package/dist/stream/arrow-stream-processor.js.map +1 -1
- package/dist/stream/buffers.js +5 -1
- package/dist/stream/buffers.js.map +1 -1
- package/dist/stream/stream-manager.d.ts.map +1 -1
- package/dist/stream/stream-manager.js +47 -36
- package/dist/stream/stream-manager.js.map +1 -1
- package/dist/stream/types.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/instrumentations.js +14 -10
- package/dist/telemetry/instrumentations.js.map +1 -1
- package/dist/telemetry/telemetry-manager.js +8 -6
- package/dist/telemetry/telemetry-manager.js.map +1 -1
- package/dist/telemetry/trace-sampler.js +33 -0
- package/dist/telemetry/trace-sampler.js.map +1 -0
- package/dist/type-generator/index.js +4 -2
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/query-registry.js +4 -2
- package/dist/type-generator/query-registry.js.map +1 -1
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +5 -3
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/utils/env-validator.js +5 -1
- package/dist/utils/env-validator.js.map +1 -1
- package/dist/utils/path-exclusions.js +66 -0
- package/dist/utils/path-exclusions.js.map +1 -0
- package/llms.txt +52 -0
- package/package.json +4 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { __esmMin } from "../_virtual/rolldown_runtime.js";
|
|
2
|
+
import { AppKitError, init_base } from "./base.js";
|
|
3
|
+
|
|
4
|
+
//#region src/errors/tunnel.ts
|
|
5
|
+
var TunnelError;
|
|
6
|
+
var init_tunnel = __esmMin((() => {
|
|
7
|
+
init_base();
|
|
8
|
+
TunnelError = class TunnelError extends AppKitError {
|
|
9
|
+
constructor(..._args) {
|
|
10
|
+
super(..._args);
|
|
11
|
+
this.code = "TUNNEL_ERROR";
|
|
12
|
+
this.statusCode = 502;
|
|
13
|
+
this.isRetryable = true;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a tunnel error for missing tunnel getter
|
|
17
|
+
*/
|
|
18
|
+
static getterNotRegistered() {
|
|
19
|
+
return new TunnelError("Tunnel getter not registered for DevFileReader singleton");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a tunnel error for no available connection
|
|
23
|
+
*/
|
|
24
|
+
static noConnection() {
|
|
25
|
+
return new TunnelError("No tunnel connection available for file read");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a tunnel error for asset fetch failure
|
|
29
|
+
*/
|
|
30
|
+
static fetchFailed(path, cause) {
|
|
31
|
+
return new TunnelError("Failed to fetch asset", {
|
|
32
|
+
cause,
|
|
33
|
+
context: { path }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a tunnel error for message parsing failure
|
|
38
|
+
*/
|
|
39
|
+
static parseError(messageType, cause) {
|
|
40
|
+
return new TunnelError(`Failed to parse ${messageType} message`, {
|
|
41
|
+
cause,
|
|
42
|
+
context: { messageType }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
init_tunnel();
|
|
50
|
+
export { TunnelError, init_tunnel };
|
|
51
|
+
//# sourceMappingURL=tunnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel.js","names":[],"sources":["../../src/errors/tunnel.ts"],"sourcesContent":["import { AppKitError } from \"./base\";\n\n/**\n * Error thrown when remote tunnel operations fail.\n * Use for tunnel connection issues, message parsing failures, etc.\n *\n * @example\n * ```typescript\n * throw new TunnelError(\"No tunnel connection available\");\n * throw new TunnelError(\"Failed to parse WebSocket message\", { cause: parseError });\n * ```\n */\nexport class TunnelError extends AppKitError {\n readonly code = \"TUNNEL_ERROR\";\n readonly statusCode = 502;\n readonly isRetryable = true;\n\n /**\n * Create a tunnel error for missing tunnel getter\n */\n static getterNotRegistered(): TunnelError {\n return new TunnelError(\n \"Tunnel getter not registered for DevFileReader singleton\",\n );\n }\n\n /**\n * Create a tunnel error for no available connection\n */\n static noConnection(): TunnelError {\n return new TunnelError(\"No tunnel connection available for file read\");\n }\n\n /**\n * Create a tunnel error for asset fetch failure\n */\n static fetchFailed(path: string, cause?: Error): TunnelError {\n return new TunnelError(\"Failed to fetch asset\", {\n cause,\n context: { path },\n });\n }\n\n /**\n * Create a tunnel error for message parsing failure\n */\n static parseError(messageType: string, cause?: Error): TunnelError {\n return new TunnelError(`Failed to parse ${messageType} message`, {\n cause,\n context: { messageType },\n });\n }\n}\n"],"mappings":";;;;;;YAAqC;CAYxB,cAAb,MAAa,oBAAoB,YAAY;;;eAC3B;qBACM;sBACC;;;;;EAKvB,OAAO,sBAAmC;AACxC,UAAO,IAAI,YACT,2DACD;;;;;EAMH,OAAO,eAA4B;AACjC,UAAO,IAAI,YAAY,+CAA+C;;;;;EAMxE,OAAO,YAAY,MAAc,OAA4B;AAC3D,UAAO,IAAI,YAAY,yBAAyB;IAC9C;IACA,SAAS,EAAE,MAAM;IAClB,CAAC;;;;;EAMJ,OAAO,WAAW,aAAqB,OAA4B;AACjE,UAAO,IAAI,YAAY,mBAAmB,YAAY,WAAW;IAC/D;IACA,SAAS,EAAE,aAAa;IACzB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AppKitError } from "./base.js";
|
|
2
|
+
|
|
3
|
+
//#region src/errors/validation.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Error thrown when input validation fails.
|
|
7
|
+
* Use for invalid parameters, missing required fields, or type mismatches.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* throw new ValidationError("Statement is required", { context: { field: "statement" } });
|
|
12
|
+
* throw new ValidationError("maxPoolSize must be at least 1", { context: { value: config.maxPoolSize } });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare class ValidationError extends AppKitError {
|
|
16
|
+
readonly code = "VALIDATION_ERROR";
|
|
17
|
+
readonly statusCode = 400;
|
|
18
|
+
readonly isRetryable = false;
|
|
19
|
+
/**
|
|
20
|
+
* Create a validation error for a missing required field
|
|
21
|
+
*/
|
|
22
|
+
static missingField(fieldName: string): ValidationError;
|
|
23
|
+
/**
|
|
24
|
+
* Create a validation error for an invalid field value.
|
|
25
|
+
* Note: The actual value is not stored in context for security reasons.
|
|
26
|
+
* Only the value's type is recorded.
|
|
27
|
+
*/
|
|
28
|
+
static invalidValue(fieldName: string, value: unknown, expected?: string): ValidationError;
|
|
29
|
+
/**
|
|
30
|
+
* Create a validation error for missing environment variables
|
|
31
|
+
*/
|
|
32
|
+
static missingEnvVars(vars: string[]): ValidationError;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { ValidationError };
|
|
36
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","names":[],"sources":["../../src/errors/validation.ts"],"sourcesContent":[],"mappings":";;;;;;AAYA;;;;;;;;cAAa,eAAA,SAAwB,WAAA;;;;;;;0CAQK;;;;;;6EAerC;;;;yCAgBoC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { __esmMin } from "../_virtual/rolldown_runtime.js";
|
|
2
|
+
import { AppKitError, init_base } from "./base.js";
|
|
3
|
+
|
|
4
|
+
//#region src/errors/validation.ts
|
|
5
|
+
var ValidationError;
|
|
6
|
+
var init_validation = __esmMin((() => {
|
|
7
|
+
init_base();
|
|
8
|
+
ValidationError = class ValidationError extends AppKitError {
|
|
9
|
+
constructor(..._args) {
|
|
10
|
+
super(..._args);
|
|
11
|
+
this.code = "VALIDATION_ERROR";
|
|
12
|
+
this.statusCode = 400;
|
|
13
|
+
this.isRetryable = false;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a validation error for a missing required field
|
|
17
|
+
*/
|
|
18
|
+
static missingField(fieldName) {
|
|
19
|
+
return new ValidationError(`Missing required field: ${fieldName}`, { context: { field: fieldName } });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a validation error for an invalid field value.
|
|
23
|
+
* Note: The actual value is not stored in context for security reasons.
|
|
24
|
+
* Only the value's type is recorded.
|
|
25
|
+
*/
|
|
26
|
+
static invalidValue(fieldName, value, expected) {
|
|
27
|
+
return new ValidationError(expected ? `Invalid value for ${fieldName}: expected ${expected}` : `Invalid value for ${fieldName}`, { context: {
|
|
28
|
+
field: fieldName,
|
|
29
|
+
valueType: value === null ? "null" : typeof value,
|
|
30
|
+
expected
|
|
31
|
+
} });
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a validation error for missing environment variables
|
|
35
|
+
*/
|
|
36
|
+
static missingEnvVars(vars) {
|
|
37
|
+
return new ValidationError(`Missing required environment variables: ${vars.join(", ")}`, { context: { missingVars: vars } });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
init_validation();
|
|
44
|
+
export { ValidationError, init_validation };
|
|
45
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","names":[],"sources":["../../src/errors/validation.ts"],"sourcesContent":["import { AppKitError } from \"./base\";\n\n/**\n * Error thrown when input validation fails.\n * Use for invalid parameters, missing required fields, or type mismatches.\n *\n * @example\n * ```typescript\n * throw new ValidationError(\"Statement is required\", { context: { field: \"statement\" } });\n * throw new ValidationError(\"maxPoolSize must be at least 1\", { context: { value: config.maxPoolSize } });\n * ```\n */\nexport class ValidationError extends AppKitError {\n readonly code = \"VALIDATION_ERROR\";\n readonly statusCode = 400;\n readonly isRetryable = false;\n\n /**\n * Create a validation error for a missing required field\n */\n static missingField(fieldName: string): ValidationError {\n return new ValidationError(`Missing required field: ${fieldName}`, {\n context: { field: fieldName },\n });\n }\n\n /**\n * Create a validation error for an invalid field value.\n * Note: The actual value is not stored in context for security reasons.\n * Only the value's type is recorded.\n */\n static invalidValue(\n fieldName: string,\n value: unknown,\n expected?: string,\n ): ValidationError {\n const msg = expected\n ? `Invalid value for ${fieldName}: expected ${expected}`\n : `Invalid value for ${fieldName}`;\n return new ValidationError(msg, {\n context: {\n field: fieldName,\n valueType: value === null ? \"null\" : typeof value,\n expected,\n },\n });\n }\n\n /**\n * Create a validation error for missing environment variables\n */\n static missingEnvVars(vars: string[]): ValidationError {\n return new ValidationError(\n `Missing required environment variables: ${vars.join(\", \")}`,\n { context: { missingVars: vars } },\n );\n }\n}\n"],"mappings":";;;;;;YAAqC;CAYxB,kBAAb,MAAa,wBAAwB,YAAY;;;eAC/B;qBACM;sBACC;;;;;EAKvB,OAAO,aAAa,WAAoC;AACtD,UAAO,IAAI,gBAAgB,2BAA2B,aAAa,EACjE,SAAS,EAAE,OAAO,WAAW,EAC9B,CAAC;;;;;;;EAQJ,OAAO,aACL,WACA,OACA,UACiB;AAIjB,UAAO,IAAI,gBAHC,WACR,qBAAqB,UAAU,aAAa,aAC5C,qBAAqB,aACO,EAC9B,SAAS;IACP,OAAO;IACP,WAAW,UAAU,OAAO,SAAS,OAAO;IAC5C;IACD,EACF,CAAC;;;;;EAMJ,OAAO,eAAe,MAAiC;AACrD,UAAO,IAAI,gBACT,2CAA2C,KAAK,KAAK,KAAK,IAC1D,EAAE,SAAS,EAAE,aAAa,MAAM,EAAE,CACnC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,22 @@ import { BasePluginConfig, IAppRouter } from "./shared/src/plugin.js";
|
|
|
2
2
|
import { CacheConfig } from "./shared/src/cache.js";
|
|
3
3
|
import { StreamExecutionSettings } from "./shared/src/execute.js";
|
|
4
4
|
import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
|
|
5
|
+
import { CacheManager } from "./cache/index.js";
|
|
5
6
|
import { ITelemetry, TelemetryConfig } from "./telemetry/types.js";
|
|
6
7
|
import { Counter, Histogram, SeverityNumber, Span, SpanStatusCode } from "./telemetry/index.js";
|
|
7
|
-
import { createApp } from "./core/appkit.js";
|
|
8
|
-
import { CacheManager } from "./cache/index.js";
|
|
9
8
|
import { Plugin } from "./plugin/plugin.js";
|
|
10
9
|
import { toPlugin } from "./plugin/to-plugin.js";
|
|
11
10
|
import { analytics } from "./analytics/analytics.js";
|
|
11
|
+
import { createApp } from "./core/appkit.js";
|
|
12
|
+
import { AppKitError } from "./errors/base.js";
|
|
13
|
+
import { AuthenticationError } from "./errors/authentication.js";
|
|
14
|
+
import { ConfigurationError } from "./errors/configuration.js";
|
|
15
|
+
import { ConnectionError } from "./errors/connection.js";
|
|
16
|
+
import { ExecutionError } from "./errors/execution.js";
|
|
17
|
+
import { InitializationError } from "./errors/initialization.js";
|
|
18
|
+
import { ServerError } from "./errors/server.js";
|
|
19
|
+
import { TunnelError } from "./errors/tunnel.js";
|
|
20
|
+
import { ValidationError } from "./errors/validation.js";
|
|
12
21
|
import { server } from "./server/index.js";
|
|
13
22
|
import { appKitTypesPlugin } from "./type-generator/vite-plugin.js";
|
|
14
|
-
export { type BasePluginConfig, type CacheConfig, CacheManager, type Counter, type Histogram, type IAppRouter, type ITelemetry, Plugin, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, type TelemetryConfig, analytics, appKitTypesPlugin, createApp, isSQLTypeMarker, server, sql, toPlugin };
|
|
23
|
+
export { AppKitError, AuthenticationError, type BasePluginConfig, type CacheConfig, CacheManager, ConfigurationError, ConnectionError, type Counter, ExecutionError, type Histogram, type IAppRouter, type ITelemetry, InitializationError, Plugin, ServerError, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, type TelemetryConfig, TunnelError, ValidationError, analytics, appKitTypesPlugin, createApp, isSQLTypeMarker, server, sql, toPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
|
|
2
2
|
import { SeverityNumber, SpanStatusCode } from "./telemetry/index.js";
|
|
3
|
+
import { AppKitError } from "./errors/base.js";
|
|
4
|
+
import { AuthenticationError } from "./errors/authentication.js";
|
|
5
|
+
import { ConfigurationError } from "./errors/configuration.js";
|
|
6
|
+
import { ConnectionError } from "./errors/connection.js";
|
|
7
|
+
import { ExecutionError } from "./errors/execution.js";
|
|
8
|
+
import { InitializationError } from "./errors/initialization.js";
|
|
9
|
+
import { ServerError } from "./errors/server.js";
|
|
10
|
+
import { TunnelError } from "./errors/tunnel.js";
|
|
11
|
+
import { ValidationError } from "./errors/validation.js";
|
|
12
|
+
import { init_errors } from "./errors/index.js";
|
|
3
13
|
import { CacheManager } from "./cache/index.js";
|
|
4
|
-
import { createApp } from "./core/appkit.js";
|
|
5
|
-
import "./core/index.js";
|
|
6
14
|
import { Plugin } from "./plugin/plugin.js";
|
|
7
15
|
import { toPlugin } from "./plugin/to-plugin.js";
|
|
8
16
|
import "./plugin/index.js";
|
|
9
17
|
import { analytics } from "./analytics/analytics.js";
|
|
10
18
|
import "./analytics/index.js";
|
|
19
|
+
import { createApp } from "./core/appkit.js";
|
|
20
|
+
import "./core/index.js";
|
|
11
21
|
import { appKitTypesPlugin } from "./type-generator/vite-plugin.js";
|
|
12
22
|
import { server } from "./server/index.js";
|
|
13
23
|
|
|
14
|
-
|
|
24
|
+
//#region src/index.ts
|
|
25
|
+
init_errors();
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { AppKitError, AuthenticationError, CacheManager, ConfigurationError, ConnectionError, ExecutionError, InitializationError, Plugin, ServerError, SeverityNumber, SpanStatusCode, TunnelError, ValidationError, analytics, appKitTypesPlugin, createApp, isSQLTypeMarker, server, sql, toPlugin };
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// Types from shared\nexport type {\n BasePluginConfig,\n CacheConfig,\n IAppRouter,\n StreamExecutionSettings,\n} from \"shared\";\nexport { isSQLTypeMarker, sql } from \"shared\";\nexport { analytics } from \"./analytics\";\nexport { CacheManager } from \"./cache\";\nexport { createApp } from \"./core\";\n// Errors\nexport {\n AppKitError,\n AuthenticationError,\n ConfigurationError,\n ConnectionError,\n ExecutionError,\n InitializationError,\n ServerError,\n TunnelError,\n ValidationError,\n} from \"./errors\";\n// Plugin authoring\nexport { Plugin, toPlugin } from \"./plugin\";\nexport { server } from \"./server\";\n// Telemetry (for advanced custom telemetry)\nexport {\n type Counter,\n type Histogram,\n type ITelemetry,\n SeverityNumber,\n type Span,\n SpanStatusCode,\n type TelemetryConfig,\n} from \"./telemetry\";\n\n// Vite plugin\nexport { appKitTypesPlugin } from \"./type-generator/vite-plugin\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;aAsBkB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { DEFAULT_SAMPLING_CONFIG, shouldSample } from "./sampling.js";
|
|
2
|
+
import { WideEvent } from "./wide-event.js";
|
|
3
|
+
import { WideEventEmitter } from "./wide-event-emitter.js";
|
|
4
|
+
import { trace } from "@opentelemetry/api";
|
|
5
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
+
import { format } from "node:util";
|
|
7
|
+
import { createDebug } from "obug";
|
|
8
|
+
|
|
9
|
+
//#region src/logging/logger.ts
|
|
10
|
+
const eventStorage = new AsyncLocalStorage();
|
|
11
|
+
const eventsByRequest = /* @__PURE__ */ new WeakMap();
|
|
12
|
+
const emitter = new WideEventEmitter();
|
|
13
|
+
const MAX_REQUEST_ID_LENGTH = 128;
|
|
14
|
+
/**
|
|
15
|
+
* Sanitize a request ID from user headers
|
|
16
|
+
*/
|
|
17
|
+
function sanitizeRequestId(id) {
|
|
18
|
+
return id.replace(/[^a-zA-Z0-9_.-]/g, "").slice(0, MAX_REQUEST_ID_LENGTH);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate a request ID from the request
|
|
22
|
+
*/
|
|
23
|
+
function generateRequestId(req) {
|
|
24
|
+
const existingId = req.headers["x-request-id"] || req.headers["x-correlation-id"] || req.headers["x-amzn-trace-id"];
|
|
25
|
+
if (existingId && typeof existingId === "string" && existingId.length > 0) {
|
|
26
|
+
const sanitized = sanitizeRequestId(existingId);
|
|
27
|
+
if (sanitized.length > 0) return sanitized;
|
|
28
|
+
}
|
|
29
|
+
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a WideEvent for a request
|
|
33
|
+
*/
|
|
34
|
+
function createEventForRequest(req) {
|
|
35
|
+
const requestId = generateRequestId(req);
|
|
36
|
+
const wideEvent = new WideEvent(requestId);
|
|
37
|
+
const path = (req.path || req.url || req.originalUrl)?.split("?")[0];
|
|
38
|
+
wideEvent.set("method", req.method).set("path", path);
|
|
39
|
+
const rawUserId = req.headers["x-forwarded-user"];
|
|
40
|
+
if (rawUserId && typeof rawUserId === "string" && rawUserId.length > 0) {
|
|
41
|
+
const userId = rawUserId.replace(/[^a-zA-Z0-9_@.-]/g, "").slice(0, 128);
|
|
42
|
+
if (userId.length > 0) wideEvent.setUser({ id: userId });
|
|
43
|
+
}
|
|
44
|
+
const spanContext = trace.getActiveSpan()?.spanContext();
|
|
45
|
+
if (spanContext?.traceId) {
|
|
46
|
+
wideEvent.set("trace_id", spanContext.traceId);
|
|
47
|
+
createDebug("appkit:logger:event", { useColors: true })("WideEvent created: %s %s (reqId: %s, traceId: %s)", req.method, path, requestId.substring(0, 8), spanContext.traceId.substring(0, 8));
|
|
48
|
+
}
|
|
49
|
+
if (wideEvent.data.service) wideEvent.data.service = {
|
|
50
|
+
...wideEvent.data.service,
|
|
51
|
+
name: "appkit"
|
|
52
|
+
};
|
|
53
|
+
return wideEvent;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Setup response lifecycle handlers for WideEvent finalization
|
|
57
|
+
*/
|
|
58
|
+
function setupResponseHandlers(req, wideEvent) {
|
|
59
|
+
const res = req.res;
|
|
60
|
+
if (!res) return;
|
|
61
|
+
res.once("finish", () => {
|
|
62
|
+
const finalizedData = wideEvent.finalize(res.statusCode || 200);
|
|
63
|
+
if (shouldSample(finalizedData, DEFAULT_SAMPLING_CONFIG)) emitter.emit(finalizedData);
|
|
64
|
+
eventsByRequest.delete(req);
|
|
65
|
+
});
|
|
66
|
+
res.once("close", () => {
|
|
67
|
+
if (!res.writableFinished) eventsByRequest.delete(req);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get or create a WideEvent for the given request.
|
|
72
|
+
* If called within wideEventMiddleware context, returns the event from AsyncLocalStorage.
|
|
73
|
+
* Otherwise creates a new event for the request.
|
|
74
|
+
*/
|
|
75
|
+
function getOrCreateEvent(req) {
|
|
76
|
+
let wideEvent = eventsByRequest.get(req);
|
|
77
|
+
if (!wideEvent) {
|
|
78
|
+
const alsEvent = eventStorage.getStore();
|
|
79
|
+
if (alsEvent) {
|
|
80
|
+
eventsByRequest.set(req, alsEvent);
|
|
81
|
+
return alsEvent;
|
|
82
|
+
}
|
|
83
|
+
wideEvent = createEventForRequest(req);
|
|
84
|
+
eventsByRequest.set(req, wideEvent);
|
|
85
|
+
setupResponseHandlers(req, wideEvent);
|
|
86
|
+
}
|
|
87
|
+
return wideEvent;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get current WideEvent from AsyncLocalStorage or request
|
|
91
|
+
*/
|
|
92
|
+
function getCurrentEvent(req) {
|
|
93
|
+
if (req) return getOrCreateEvent(req);
|
|
94
|
+
return eventStorage.getStore();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if the first argument is an Express Request
|
|
98
|
+
*/
|
|
99
|
+
function isRequest(arg) {
|
|
100
|
+
return typeof arg === "object" && arg !== null && "method" in arg && "path" in arg && typeof arg.method === "string";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a logger instance for a specific scope
|
|
104
|
+
* @param scope - The scope identifier (e.g., "connectors:lakebase")
|
|
105
|
+
* @returns Logger instance with debug, info, warn, and error methods
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const logger = createLogger("connectors:lakebase");
|
|
110
|
+
*
|
|
111
|
+
* // Regular logging (no request tracking)
|
|
112
|
+
* logger.debug("Connection established with pool size: %d", poolSize);
|
|
113
|
+
* logger.info("Server started on port %d", port);
|
|
114
|
+
*
|
|
115
|
+
* // Request-scoped logging (tracks in WideEvent)
|
|
116
|
+
* logger.debug(req, "Processing query: %s", queryId);
|
|
117
|
+
* logger.error(req, "Query failed: %O", error);
|
|
118
|
+
*
|
|
119
|
+
* // Get WideEvent - works in route handlers (with req) or interceptors (from context)
|
|
120
|
+
* const event = logger.event(req); // In route handler
|
|
121
|
+
* const event = logger.event(); // In interceptor (gets from AsyncLocalStorage)
|
|
122
|
+
* event?.setComponent("analytics", "executeQuery");
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
function createLogger(scope) {
|
|
126
|
+
const debug = createDebug(`appkit:${scope}`, { useColors: true });
|
|
127
|
+
const prefix = `[appkit:${scope}]`;
|
|
128
|
+
function debugLog(reqOrMessage, ...args) {
|
|
129
|
+
if (isRequest(reqOrMessage)) {
|
|
130
|
+
const req = reqOrMessage;
|
|
131
|
+
const message = args[0];
|
|
132
|
+
const logArgs = args.slice(1);
|
|
133
|
+
const formatted = format(message, ...logArgs);
|
|
134
|
+
debug(message, ...logArgs);
|
|
135
|
+
getOrCreateEvent(req).addLog("debug", formatted);
|
|
136
|
+
} else debug(reqOrMessage, ...args);
|
|
137
|
+
}
|
|
138
|
+
function infoLog(reqOrMessage, ...args) {
|
|
139
|
+
if (isRequest(reqOrMessage)) {
|
|
140
|
+
const req = reqOrMessage;
|
|
141
|
+
const message = args[0];
|
|
142
|
+
const formatted = format(message, ...args.slice(1));
|
|
143
|
+
console.log(prefix, formatted);
|
|
144
|
+
getOrCreateEvent(req).addLog("info", formatted);
|
|
145
|
+
} else console.log(prefix, format(reqOrMessage, ...args));
|
|
146
|
+
}
|
|
147
|
+
function warnLog(reqOrMessage, ...args) {
|
|
148
|
+
if (isRequest(reqOrMessage)) {
|
|
149
|
+
const req = reqOrMessage;
|
|
150
|
+
const message = args[0];
|
|
151
|
+
const formatted = format(message, ...args.slice(1));
|
|
152
|
+
console.warn(prefix, formatted);
|
|
153
|
+
getOrCreateEvent(req).addLog("warn", formatted);
|
|
154
|
+
} else console.warn(prefix, format(reqOrMessage, ...args));
|
|
155
|
+
}
|
|
156
|
+
function errorLog(reqOrMessage, ...args) {
|
|
157
|
+
if (isRequest(reqOrMessage)) {
|
|
158
|
+
const req = reqOrMessage;
|
|
159
|
+
const message = args[0];
|
|
160
|
+
const formatted = format(message, ...args.slice(1));
|
|
161
|
+
console.error(prefix, formatted);
|
|
162
|
+
getOrCreateEvent(req).addLog("error", formatted);
|
|
163
|
+
} else console.error(prefix, format(reqOrMessage, ...args));
|
|
164
|
+
}
|
|
165
|
+
function event(req) {
|
|
166
|
+
return getCurrentEvent(req);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
debug: debugLog,
|
|
170
|
+
info: infoLog,
|
|
171
|
+
warn: warnLog,
|
|
172
|
+
error: errorLog,
|
|
173
|
+
event
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
178
|
+
export { createLogger };
|
|
179
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","names":["createObug"],"sources":["../../src/logging/logger.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { format } from \"node:util\";\nimport { trace } from \"@opentelemetry/api\";\nimport type { NextFunction, Request, Response } from \"express\";\nimport { createDebug as createObug } from \"obug\";\nimport { DEFAULT_SAMPLING_CONFIG, shouldSample } from \"./sampling\";\nimport { WideEvent } from \"./wide-event\";\nimport { WideEventEmitter } from \"./wide-event-emitter\";\n\n/**\n * Logger interface for AppKit components\n */\nexport interface Logger {\n /** Debug output (disabled by default, enable via DEBUG env var) */\n debug(message: string, ...args: unknown[]): void;\n debug(req: Request, message: string, ...args: unknown[]): void;\n\n /** Info output (always visible, for operational messages) */\n info(message: string, ...args: unknown[]): void;\n info(req: Request, message: string, ...args: unknown[]): void;\n\n /** Warning output (always visible, for degraded states) */\n warn(message: string, ...args: unknown[]): void;\n warn(req: Request, message: string, ...args: unknown[]): void;\n\n /** Error output (always visible, for failures) */\n error(message: string, ...args: unknown[]): void;\n error(req: Request, message: string, ...args: unknown[]): void;\n\n /** Get request-scoped WideEvent (from AsyncLocalStorage or explicit req) */\n event(req?: Request): WideEvent | undefined;\n}\n\n// AsyncLocalStorage for WideEvent context propagation\nconst eventStorage = new AsyncLocalStorage<WideEvent>();\n\n// WeakMap to store WideEvent per request (for explicit req usage)\nconst eventsByRequest = new WeakMap<Request, WideEvent>();\n\n// Global emitter instance\nconst emitter = new WideEventEmitter();\n\nconst MAX_REQUEST_ID_LENGTH = 128;\n\n/**\n * Sanitize a request ID from user headers\n */\nfunction sanitizeRequestId(id: string): string {\n const sanitized = id.replace(/[^a-zA-Z0-9_.-]/g, \"\");\n return sanitized.slice(0, MAX_REQUEST_ID_LENGTH);\n}\n\n/**\n * Generate a request ID from the request\n */\nfunction generateRequestId(req: Request): string {\n const existingId =\n req.headers[\"x-request-id\"] ||\n req.headers[\"x-correlation-id\"] ||\n req.headers[\"x-amzn-trace-id\"];\n\n if (existingId && typeof existingId === \"string\" && existingId.length > 0) {\n const sanitized = sanitizeRequestId(existingId);\n if (sanitized.length > 0) {\n return sanitized;\n }\n }\n\n return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Create a WideEvent for a request\n */\nfunction createEventForRequest(req: Request): WideEvent {\n const requestId = generateRequestId(req);\n const wideEvent = new WideEvent(requestId);\n\n // extract path from request (strip query string)\n const rawPath = req.path || req.url || req.originalUrl;\n const path = rawPath?.split(\"?\")[0];\n wideEvent.set(\"method\", req.method).set(\"path\", path);\n\n // extract user id from request headers (sanitized)\n const rawUserId = req.headers[\"x-forwarded-user\"];\n if (rawUserId && typeof rawUserId === \"string\" && rawUserId.length > 0) {\n const userId = rawUserId.replace(/[^a-zA-Z0-9_@.-]/g, \"\").slice(0, 128);\n if (userId.length > 0) {\n wideEvent.setUser({ id: userId });\n }\n }\n\n // extract trace id from active span for distributed tracing\n const currentSpan = trace.getActiveSpan();\n const spanContext = currentSpan?.spanContext();\n if (spanContext?.traceId) {\n wideEvent.set(\"trace_id\", spanContext.traceId);\n\n const debugLogger = createObug(\"appkit:logger:event\", { useColors: true });\n debugLogger(\n \"WideEvent created: %s %s (reqId: %s, traceId: %s)\",\n req.method,\n path,\n requestId.substring(0, 8),\n spanContext.traceId.substring(0, 8),\n );\n }\n\n // Update service scope\n if (wideEvent.data.service) {\n wideEvent.data.service = {\n ...wideEvent.data.service,\n name: \"appkit\",\n };\n }\n\n return wideEvent;\n}\n\n/**\n * Setup response lifecycle handlers for WideEvent finalization\n */\nfunction setupResponseHandlers(req: Request, wideEvent: WideEvent): void {\n const res = req.res as Response | undefined;\n if (!res) return;\n\n res.once(\"finish\", () => {\n // finalize the event with status code\n const finalizedData = wideEvent.finalize(res.statusCode || 200);\n\n // emit to OpenTelemetry if sampled\n if (shouldSample(finalizedData, DEFAULT_SAMPLING_CONFIG)) {\n emitter.emit(finalizedData);\n }\n\n // clean up the WeakMap\n eventsByRequest.delete(req);\n });\n\n res.once(\"close\", () => {\n if (!res.writableFinished) {\n // request was aborted - just cleanup\n eventsByRequest.delete(req);\n }\n });\n}\n\n/**\n * Express middleware that establishes AsyncLocalStorage context for WideEvent.\n * This properly scopes the context to the entire request lifecycle using run().\n *\n * @example\n * ```typescript\n * import { wideEventMiddleware } from \"@databricks/appkit\";\n *\n * app.use(wideEventMiddleware);\n * ```\n */\nexport function wideEventMiddleware(\n req: Request,\n _res: Response,\n next: NextFunction,\n): void {\n const wideEvent = createEventForRequest(req);\n eventsByRequest.set(req, wideEvent);\n setupResponseHandlers(req, wideEvent);\n\n // run() scopes the context to this request's entire async chain\n eventStorage.run(wideEvent, next);\n}\n\n/**\n * Get or create a WideEvent for the given request.\n * If called within wideEventMiddleware context, returns the event from AsyncLocalStorage.\n * Otherwise creates a new event for the request.\n */\nfunction getOrCreateEvent(req: Request): WideEvent {\n // first check if we already have an event\n let wideEvent = eventsByRequest.get(req);\n\n if (!wideEvent) {\n // check if we are in a middleware context\n const alsEvent = eventStorage.getStore();\n if (alsEvent) {\n // store the event in the WeakMap\n eventsByRequest.set(req, alsEvent);\n return alsEvent;\n }\n\n // no middleware context - create event directly\n wideEvent = createEventForRequest(req);\n eventsByRequest.set(req, wideEvent);\n setupResponseHandlers(req, wideEvent);\n }\n\n return wideEvent;\n}\n\n/**\n * Get current WideEvent from AsyncLocalStorage or request\n */\nfunction getCurrentEvent(req?: Request): WideEvent | undefined {\n // if req provided, use it\n if (req) {\n return getOrCreateEvent(req);\n }\n\n // otherwise, get from AsyncLocalStorage\n return eventStorage.getStore();\n}\n\n/**\n * Check if the first argument is an Express Request\n */\nfunction isRequest(arg: unknown): arg is Request {\n return (\n typeof arg === \"object\" &&\n arg !== null &&\n \"method\" in arg &&\n \"path\" in arg &&\n typeof (arg as Request).method === \"string\"\n );\n}\n\n/**\n * Create a logger instance for a specific scope\n * @param scope - The scope identifier (e.g., \"connectors:lakebase\")\n * @returns Logger instance with debug, info, warn, and error methods\n *\n * @example\n * ```typescript\n * const logger = createLogger(\"connectors:lakebase\");\n *\n * // Regular logging (no request tracking)\n * logger.debug(\"Connection established with pool size: %d\", poolSize);\n * logger.info(\"Server started on port %d\", port);\n *\n * // Request-scoped logging (tracks in WideEvent)\n * logger.debug(req, \"Processing query: %s\", queryId);\n * logger.error(req, \"Query failed: %O\", error);\n *\n * // Get WideEvent - works in route handlers (with req) or interceptors (from context)\n * const event = logger.event(req); // In route handler\n * const event = logger.event(); // In interceptor (gets from AsyncLocalStorage)\n * event?.setComponent(\"analytics\", \"executeQuery\");\n * ```\n */\nexport function createLogger(scope: string): Logger {\n const debug = createObug(`appkit:${scope}`, { useColors: true });\n const prefix = `[appkit:${scope}]`;\n\n function debugLog(reqOrMessage: Request | string, ...args: unknown[]): void {\n if (isRequest(reqOrMessage)) {\n const req = reqOrMessage;\n const message = args[0] as string;\n const logArgs = args.slice(1);\n const formatted = format(message, ...logArgs);\n\n debug(message, ...logArgs);\n getOrCreateEvent(req).addLog(\"debug\", formatted);\n } else {\n debug(reqOrMessage, ...args);\n }\n }\n\n function infoLog(reqOrMessage: Request | string, ...args: unknown[]): void {\n if (isRequest(reqOrMessage)) {\n const req = reqOrMessage;\n const message = args[0] as string;\n const logArgs = args.slice(1);\n const formatted = format(message, ...logArgs);\n\n console.log(prefix, formatted);\n getOrCreateEvent(req).addLog(\"info\", formatted);\n } else {\n console.log(prefix, format(reqOrMessage, ...args));\n }\n }\n\n function warnLog(reqOrMessage: Request | string, ...args: unknown[]): void {\n if (isRequest(reqOrMessage)) {\n const req = reqOrMessage;\n const message = args[0] as string;\n const logArgs = args.slice(1);\n const formatted = format(message, ...logArgs);\n\n console.warn(prefix, formatted);\n getOrCreateEvent(req).addLog(\"warn\", formatted);\n } else {\n console.warn(prefix, format(reqOrMessage, ...args));\n }\n }\n\n function errorLog(reqOrMessage: Request | string, ...args: unknown[]): void {\n if (isRequest(reqOrMessage)) {\n const req = reqOrMessage;\n const message = args[0] as string;\n const logArgs = args.slice(1);\n const formatted = format(message, ...logArgs);\n\n console.error(prefix, formatted);\n getOrCreateEvent(req).addLog(\"error\", formatted);\n } else {\n console.error(prefix, format(reqOrMessage, ...args));\n }\n }\n\n function event(req?: Request): WideEvent | undefined {\n return getCurrentEvent(req);\n }\n\n return {\n debug: debugLog as Logger[\"debug\"],\n info: infoLog as Logger[\"info\"],\n warn: warnLog as Logger[\"warn\"],\n error: errorLog as Logger[\"error\"],\n event,\n };\n}\n"],"mappings":";;;;;;;;;AAkCA,MAAM,eAAe,IAAI,mBAA8B;AAGvD,MAAM,kCAAkB,IAAI,SAA6B;AAGzD,MAAM,UAAU,IAAI,kBAAkB;AAEtC,MAAM,wBAAwB;;;;AAK9B,SAAS,kBAAkB,IAAoB;AAE7C,QADkB,GAAG,QAAQ,oBAAoB,GAAG,CACnC,MAAM,GAAG,sBAAsB;;;;;AAMlD,SAAS,kBAAkB,KAAsB;CAC/C,MAAM,aACJ,IAAI,QAAQ,mBACZ,IAAI,QAAQ,uBACZ,IAAI,QAAQ;AAEd,KAAI,cAAc,OAAO,eAAe,YAAY,WAAW,SAAS,GAAG;EACzE,MAAM,YAAY,kBAAkB,WAAW;AAC/C,MAAI,UAAU,SAAS,EACrB,QAAO;;AAIX,QAAO,OAAO,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,GAAG,EAAE;;;;;AAMxE,SAAS,sBAAsB,KAAyB;CACtD,MAAM,YAAY,kBAAkB,IAAI;CACxC,MAAM,YAAY,IAAI,UAAU,UAAU;CAI1C,MAAM,QADU,IAAI,QAAQ,IAAI,OAAO,IAAI,cACrB,MAAM,IAAI,CAAC;AACjC,WAAU,IAAI,UAAU,IAAI,OAAO,CAAC,IAAI,QAAQ,KAAK;CAGrD,MAAM,YAAY,IAAI,QAAQ;AAC9B,KAAI,aAAa,OAAO,cAAc,YAAY,UAAU,SAAS,GAAG;EACtE,MAAM,SAAS,UAAU,QAAQ,qBAAqB,GAAG,CAAC,MAAM,GAAG,IAAI;AACvE,MAAI,OAAO,SAAS,EAClB,WAAU,QAAQ,EAAE,IAAI,QAAQ,CAAC;;CAMrC,MAAM,cADc,MAAM,eAAe,EACR,aAAa;AAC9C,KAAI,aAAa,SAAS;AACxB,YAAU,IAAI,YAAY,YAAY,QAAQ;AAG9C,EADoBA,YAAW,uBAAuB,EAAE,WAAW,MAAM,CAAC,CAExE,qDACA,IAAI,QACJ,MACA,UAAU,UAAU,GAAG,EAAE,EACzB,YAAY,QAAQ,UAAU,GAAG,EAAE,CACpC;;AAIH,KAAI,UAAU,KAAK,QACjB,WAAU,KAAK,UAAU;EACvB,GAAG,UAAU,KAAK;EAClB,MAAM;EACP;AAGH,QAAO;;;;;AAMT,SAAS,sBAAsB,KAAc,WAA4B;CACvE,MAAM,MAAM,IAAI;AAChB,KAAI,CAAC,IAAK;AAEV,KAAI,KAAK,gBAAgB;EAEvB,MAAM,gBAAgB,UAAU,SAAS,IAAI,cAAc,IAAI;AAG/D,MAAI,aAAa,eAAe,wBAAwB,CACtD,SAAQ,KAAK,cAAc;AAI7B,kBAAgB,OAAO,IAAI;GAC3B;AAEF,KAAI,KAAK,eAAe;AACtB,MAAI,CAAC,IAAI,iBAEP,iBAAgB,OAAO,IAAI;GAE7B;;;;;;;AAgCJ,SAAS,iBAAiB,KAAyB;CAEjD,IAAI,YAAY,gBAAgB,IAAI,IAAI;AAExC,KAAI,CAAC,WAAW;EAEd,MAAM,WAAW,aAAa,UAAU;AACxC,MAAI,UAAU;AAEZ,mBAAgB,IAAI,KAAK,SAAS;AAClC,UAAO;;AAIT,cAAY,sBAAsB,IAAI;AACtC,kBAAgB,IAAI,KAAK,UAAU;AACnC,wBAAsB,KAAK,UAAU;;AAGvC,QAAO;;;;;AAMT,SAAS,gBAAgB,KAAsC;AAE7D,KAAI,IACF,QAAO,iBAAiB,IAAI;AAI9B,QAAO,aAAa,UAAU;;;;;AAMhC,SAAS,UAAU,KAA8B;AAC/C,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,YAAY,OACZ,UAAU,OACV,OAAQ,IAAgB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;AA2BvC,SAAgB,aAAa,OAAuB;CAClD,MAAM,QAAQA,YAAW,UAAU,SAAS,EAAE,WAAW,MAAM,CAAC;CAChE,MAAM,SAAS,WAAW,MAAM;CAEhC,SAAS,SAAS,cAAgC,GAAG,MAAuB;AAC1E,MAAI,UAAU,aAAa,EAAE;GAC3B,MAAM,MAAM;GACZ,MAAM,UAAU,KAAK;GACrB,MAAM,UAAU,KAAK,MAAM,EAAE;GAC7B,MAAM,YAAY,OAAO,SAAS,GAAG,QAAQ;AAE7C,SAAM,SAAS,GAAG,QAAQ;AAC1B,oBAAiB,IAAI,CAAC,OAAO,SAAS,UAAU;QAEhD,OAAM,cAAc,GAAG,KAAK;;CAIhC,SAAS,QAAQ,cAAgC,GAAG,MAAuB;AACzE,MAAI,UAAU,aAAa,EAAE;GAC3B,MAAM,MAAM;GACZ,MAAM,UAAU,KAAK;GAErB,MAAM,YAAY,OAAO,SAAS,GADlB,KAAK,MAAM,EAAE,CACgB;AAE7C,WAAQ,IAAI,QAAQ,UAAU;AAC9B,oBAAiB,IAAI,CAAC,OAAO,QAAQ,UAAU;QAE/C,SAAQ,IAAI,QAAQ,OAAO,cAAc,GAAG,KAAK,CAAC;;CAItD,SAAS,QAAQ,cAAgC,GAAG,MAAuB;AACzE,MAAI,UAAU,aAAa,EAAE;GAC3B,MAAM,MAAM;GACZ,MAAM,UAAU,KAAK;GAErB,MAAM,YAAY,OAAO,SAAS,GADlB,KAAK,MAAM,EAAE,CACgB;AAE7C,WAAQ,KAAK,QAAQ,UAAU;AAC/B,oBAAiB,IAAI,CAAC,OAAO,QAAQ,UAAU;QAE/C,SAAQ,KAAK,QAAQ,OAAO,cAAc,GAAG,KAAK,CAAC;;CAIvD,SAAS,SAAS,cAAgC,GAAG,MAAuB;AAC1E,MAAI,UAAU,aAAa,EAAE;GAC3B,MAAM,MAAM;GACZ,MAAM,UAAU,KAAK;GAErB,MAAM,YAAY,OAAO,SAAS,GADlB,KAAK,MAAM,EAAE,CACgB;AAE7C,WAAQ,MAAM,QAAQ,UAAU;AAChC,oBAAiB,IAAI,CAAC,OAAO,SAAS,UAAU;QAEhD,SAAQ,MAAM,QAAQ,OAAO,cAAc,GAAG,KAAK,CAAC;;CAIxD,SAAS,MAAM,KAAsC;AACnD,SAAO,gBAAgB,IAAI;;AAG7B,QAAO;EACL,OAAO;EACP,MAAM;EACN,MAAM;EACN,OAAO;EACP;EACD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { shouldExcludePath } from "../utils/path-exclusions.js";
|
|
2
|
+
|
|
3
|
+
//#region src/logging/sampling.ts
|
|
4
|
+
/**
|
|
5
|
+
* Get sample rate from environment variable or default to 1.0 (100%)
|
|
6
|
+
*/
|
|
7
|
+
function getSampleRate() {
|
|
8
|
+
const envRate = process.env.APPKIT_SAMPLE_RATE;
|
|
9
|
+
if (envRate) {
|
|
10
|
+
const parsed = parseFloat(envRate);
|
|
11
|
+
if (!Number.isNaN(parsed) && parsed >= 0 && parsed <= 1) return parsed;
|
|
12
|
+
}
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default sampling configuration
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_SAMPLING_CONFIG = {
|
|
19
|
+
alwaysSampleIf: {
|
|
20
|
+
hasErrors: true,
|
|
21
|
+
statusCodeGte: 400,
|
|
22
|
+
durationGte: 5e3,
|
|
23
|
+
hasCacheInfo: true
|
|
24
|
+
},
|
|
25
|
+
sampleRate: getSampleRate()
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Simple hash function for deterministic sampling
|
|
29
|
+
*/
|
|
30
|
+
function hashString(str) {
|
|
31
|
+
let hash = 0;
|
|
32
|
+
for (let i = 0; i < str.length; i++) {
|
|
33
|
+
const char = str.charCodeAt(i);
|
|
34
|
+
hash = (hash << 5) - hash + char;
|
|
35
|
+
hash = hash & hash;
|
|
36
|
+
}
|
|
37
|
+
return Math.abs(hash);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Determine if a WideEvent should be sampled based on configuration.
|
|
41
|
+
* Uses shared path exclusions from utils/path-exclusions.ts.
|
|
42
|
+
*/
|
|
43
|
+
function shouldSample(event, config = DEFAULT_SAMPLING_CONFIG) {
|
|
44
|
+
if (shouldExcludePath(event.path)) return false;
|
|
45
|
+
if (config.alwaysSampleIf.hasErrors && event.error) return true;
|
|
46
|
+
if (config.alwaysSampleIf.statusCodeGte && event.status_code && event.status_code >= config.alwaysSampleIf.statusCodeGte) return true;
|
|
47
|
+
if (config.alwaysSampleIf.durationGte && event.duration_ms && event.duration_ms >= config.alwaysSampleIf.durationGte) return true;
|
|
48
|
+
if (config.alwaysSampleIf.hasCacheInfo && event.execution?.cache_hit !== void 0) return true;
|
|
49
|
+
if (config.sampleRate >= 1) return true;
|
|
50
|
+
if (config.sampleRate <= 0) return false;
|
|
51
|
+
return hashString(event.request_id) % 100 < config.sampleRate * 100;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { DEFAULT_SAMPLING_CONFIG, shouldSample };
|
|
56
|
+
//# sourceMappingURL=sampling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling.js","names":[],"sources":["../../src/logging/sampling.ts"],"sourcesContent":["import { shouldExcludePath } from \"../utils/path-exclusions\";\nimport type { WideEventData } from \"./wide-event\";\n\n/**\n * Sampling configuration for WideEvents\n */\nexport interface SamplingConfig {\n /** Always sample if any of these conditions are true */\n alwaysSampleIf: {\n /** Sample if event has errors */\n hasErrors: boolean;\n /** Sample if status code >= this value (e.g., 400) */\n statusCodeGte: number;\n /** Sample if duration >= this value in ms (e.g., 5000) */\n durationGte: number;\n /** Sample if cache was used (hit or miss tracked) */\n hasCacheInfo: boolean;\n };\n\n /** Sample rate for normal requests (0-1, e.g., 0.1 = 10%) */\n sampleRate: number;\n}\n\n/**\n * Get sample rate from environment variable or default to 1.0 (100%)\n */\nfunction getSampleRate(): number {\n const envRate = process.env.APPKIT_SAMPLE_RATE;\n if (envRate) {\n const parsed = parseFloat(envRate);\n if (!Number.isNaN(parsed) && parsed >= 0 && parsed <= 1) {\n return parsed;\n }\n }\n return 1;\n}\n\n/**\n * Default sampling configuration\n */\nexport const DEFAULT_SAMPLING_CONFIG: SamplingConfig = {\n alwaysSampleIf: {\n hasErrors: true,\n statusCodeGte: 400,\n durationGte: 5000, // 5 seconds\n hasCacheInfo: true, // Always sample requests with cache info (hit or miss)\n },\n sampleRate: getSampleRate(),\n};\n\n/**\n * Simple hash function for deterministic sampling\n */\nfunction hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash);\n}\n\n/**\n * Determine if a WideEvent should be sampled based on configuration.\n * Uses shared path exclusions from utils/path-exclusions.ts.\n */\nexport function shouldSample(\n event: WideEventData,\n config: SamplingConfig = DEFAULT_SAMPLING_CONFIG,\n): boolean {\n // Check exclusions first using shared path exclusions\n if (shouldExcludePath(event.path)) {\n return false;\n }\n\n // Always sample if has errors\n if (config.alwaysSampleIf.hasErrors && event.error) {\n return true;\n }\n\n // Always sample if status code >= threshold\n if (\n config.alwaysSampleIf.statusCodeGte &&\n event.status_code &&\n event.status_code >= config.alwaysSampleIf.statusCodeGte\n ) {\n return true;\n }\n\n // Always sample if duration >= threshold\n if (\n config.alwaysSampleIf.durationGte &&\n event.duration_ms &&\n event.duration_ms >= config.alwaysSampleIf.durationGte\n ) {\n return true;\n }\n\n // Always sample if cache info is present (cache hit or miss)\n if (\n config.alwaysSampleIf.hasCacheInfo &&\n event.execution?.cache_hit !== undefined\n ) {\n return true;\n }\n\n // Sample based on sample rate\n if (config.sampleRate >= 1) {\n return true;\n }\n\n if (config.sampleRate <= 0) {\n return false;\n }\n\n // Deterministic sampling based on request ID\n const hash = hashString(event.request_id);\n return hash % 100 < config.sampleRate * 100;\n}\n"],"mappings":";;;;;;AA0BA,SAAS,gBAAwB;CAC/B,MAAM,UAAU,QAAQ,IAAI;AAC5B,KAAI,SAAS;EACX,MAAM,SAAS,WAAW,QAAQ;AAClC,MAAI,CAAC,OAAO,MAAM,OAAO,IAAI,UAAU,KAAK,UAAU,EACpD,QAAO;;AAGX,QAAO;;;;;AAMT,MAAa,0BAA0C;CACrD,gBAAgB;EACd,WAAW;EACX,eAAe;EACf,aAAa;EACb,cAAc;EACf;CACD,YAAY,eAAe;CAC5B;;;;AAKD,SAAS,WAAW,KAAqB;CACvC,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;EACnC,MAAM,OAAO,IAAI,WAAW,EAAE;AAC9B,UAAQ,QAAQ,KAAK,OAAO;AAC5B,SAAO,OAAO;;AAEhB,QAAO,KAAK,IAAI,KAAK;;;;;;AAOvB,SAAgB,aACd,OACA,SAAyB,yBAChB;AAET,KAAI,kBAAkB,MAAM,KAAK,CAC/B,QAAO;AAIT,KAAI,OAAO,eAAe,aAAa,MAAM,MAC3C,QAAO;AAIT,KACE,OAAO,eAAe,iBACtB,MAAM,eACN,MAAM,eAAe,OAAO,eAAe,cAE3C,QAAO;AAIT,KACE,OAAO,eAAe,eACtB,MAAM,eACN,MAAM,eAAe,OAAO,eAAe,YAE3C,QAAO;AAIT,KACE,OAAO,eAAe,gBACtB,MAAM,WAAW,cAAc,OAE/B,QAAO;AAIT,KAAI,OAAO,cAAc,EACvB,QAAO;AAGT,KAAI,OAAO,cAAc,EACvB,QAAO;AAKT,QADa,WAAW,MAAM,WAAW,GAC3B,MAAM,OAAO,aAAa"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { SeverityNumber, logs } from "@opentelemetry/api-logs";
|
|
2
|
+
|
|
3
|
+
//#region src/logging/wide-event-emitter.ts
|
|
4
|
+
/**
|
|
5
|
+
* Emits WideEvents to OpenTelemetry as structured logs
|
|
6
|
+
*/
|
|
7
|
+
var WideEventEmitter = class {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.logger = logs.getLogger("appkit", "1.0.0");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Emit a WideEvent to OpenTelemetry.
|
|
13
|
+
* Fails silently to avoid crashing the application due to observability issues.
|
|
14
|
+
*/
|
|
15
|
+
emit(event) {
|
|
16
|
+
try {
|
|
17
|
+
const logRecord = {
|
|
18
|
+
timestamp: Date.parse(event.timestamp),
|
|
19
|
+
severityNumber: this.getSeverityNumber(event),
|
|
20
|
+
severityText: this.getSeverityText(event),
|
|
21
|
+
body: this.createLogBody(event),
|
|
22
|
+
attributes: this.createAttributes(event)
|
|
23
|
+
};
|
|
24
|
+
this.logger.emit(logRecord);
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get OpenTelemetry severity number based on event data
|
|
29
|
+
*/
|
|
30
|
+
getSeverityNumber(event) {
|
|
31
|
+
if (event.error) return SeverityNumber.ERROR;
|
|
32
|
+
if (event.status_code) {
|
|
33
|
+
if (event.status_code >= 500) return SeverityNumber.ERROR;
|
|
34
|
+
if (event.status_code >= 400) return SeverityNumber.WARN;
|
|
35
|
+
}
|
|
36
|
+
if (event.logs) {
|
|
37
|
+
if (event.logs.some((log) => log.level === "error")) return SeverityNumber.ERROR;
|
|
38
|
+
if (event.logs.some((log) => log.level === "warn")) return SeverityNumber.WARN;
|
|
39
|
+
}
|
|
40
|
+
return SeverityNumber.INFO;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get severity text based on severity number
|
|
44
|
+
*/
|
|
45
|
+
getSeverityText(event) {
|
|
46
|
+
const severityNumber = this.getSeverityNumber(event);
|
|
47
|
+
if (severityNumber >= SeverityNumber.ERROR) return "ERROR";
|
|
48
|
+
if (severityNumber >= SeverityNumber.WARN) return "WARN";
|
|
49
|
+
if (severityNumber >= SeverityNumber.INFO) return "INFO";
|
|
50
|
+
return "DEBUG";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create log body from event data
|
|
54
|
+
*/
|
|
55
|
+
createLogBody(event) {
|
|
56
|
+
const parts = [];
|
|
57
|
+
if (event.method && event.path) parts.push(`${event.method} ${event.path}`);
|
|
58
|
+
if (event.status_code) parts.push(`→ ${event.status_code}`);
|
|
59
|
+
if (event.duration_ms) parts.push(`(${event.duration_ms}ms)`);
|
|
60
|
+
if (event.component) {
|
|
61
|
+
const componentStr = event.component.operation ? `${event.component.name}.${event.component.operation}` : event.component.name;
|
|
62
|
+
parts.push(`[${componentStr}]`);
|
|
63
|
+
}
|
|
64
|
+
if (event.error) parts.push(`ERROR: ${event.error.message}`);
|
|
65
|
+
return parts.join(" ");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create OpenTelemetry attributes from event data
|
|
69
|
+
*/
|
|
70
|
+
createAttributes(event) {
|
|
71
|
+
const attributes = {
|
|
72
|
+
request_id: event.request_id,
|
|
73
|
+
trace_id: event.trace_id,
|
|
74
|
+
"http.method": event.method,
|
|
75
|
+
"http.route": event.path,
|
|
76
|
+
"http.status_code": event.status_code,
|
|
77
|
+
"http.request.duration_ms": event.duration_ms,
|
|
78
|
+
"service.name": event.service?.name,
|
|
79
|
+
"service.version": event.service?.version,
|
|
80
|
+
"service.region": event.service?.region,
|
|
81
|
+
"service.deployment_id": event.service?.deployment_id,
|
|
82
|
+
"service.node_env": event.service?.node_env,
|
|
83
|
+
"component.name": event.component?.name,
|
|
84
|
+
"component.operation": event.component?.operation,
|
|
85
|
+
"user.id": event.user?.id,
|
|
86
|
+
"error.type": event.error?.type,
|
|
87
|
+
"error.code": event.error?.code,
|
|
88
|
+
"error.message": event.error?.message,
|
|
89
|
+
"error.retriable": event.error?.retriable,
|
|
90
|
+
"execution.timeout_ms": event.execution?.timeout_ms,
|
|
91
|
+
"execution.retry_attempts": event.execution?.retry_attempts,
|
|
92
|
+
"execution.cache_hit": event.execution?.cache_hit,
|
|
93
|
+
"execution.cache_key": event.execution?.cache_key,
|
|
94
|
+
"execution.cache_deduplication": event.execution?.cache_deduplication,
|
|
95
|
+
"stream.id": event.stream?.stream_id,
|
|
96
|
+
"stream.events_sent": event.stream?.events_sent,
|
|
97
|
+
log_count: event.logs?.length
|
|
98
|
+
};
|
|
99
|
+
if (event.context) {
|
|
100
|
+
for (const [scope, scopeData] of Object.entries(event.context)) for (const [key, value] of Object.entries(scopeData)) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") attributes[`${scope}.${key}`] = value;
|
|
101
|
+
}
|
|
102
|
+
return Object.fromEntries(Object.entries(attributes).filter(([_, value]) => value !== void 0));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
//#endregion
|
|
107
|
+
export { WideEventEmitter };
|
|
108
|
+
//# sourceMappingURL=wide-event-emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wide-event-emitter.js","names":[],"sources":["../../src/logging/wide-event-emitter.ts"],"sourcesContent":["import { logs, SeverityNumber } from \"@opentelemetry/api-logs\";\nimport type { WideEventData } from \"./wide-event\";\n\n/**\n * Emits WideEvents to OpenTelemetry as structured logs\n */\nexport class WideEventEmitter {\n private logger = logs.getLogger(\"appkit\", \"1.0.0\");\n\n /**\n * Emit a WideEvent to OpenTelemetry.\n * Fails silently to avoid crashing the application due to observability issues.\n */\n emit(event: WideEventData): void {\n try {\n const logRecord = {\n timestamp: Date.parse(event.timestamp),\n severityNumber: this.getSeverityNumber(event),\n severityText: this.getSeverityText(event),\n body: this.createLogBody(event),\n attributes: this.createAttributes(event),\n };\n\n this.logger.emit(logRecord);\n } catch {\n // Silent fail - observability should never crash the application\n }\n }\n\n /**\n * Get OpenTelemetry severity number based on event data\n */\n private getSeverityNumber(event: WideEventData): SeverityNumber {\n // Error level\n if (event.error) {\n return SeverityNumber.ERROR;\n }\n\n // Status code based\n if (event.status_code) {\n if (event.status_code >= 500) {\n return SeverityNumber.ERROR;\n }\n if (event.status_code >= 400) {\n return SeverityNumber.WARN;\n }\n }\n\n // Check logs for errors/warnings\n if (event.logs) {\n const hasError = event.logs.some((log) => log.level === \"error\");\n if (hasError) {\n return SeverityNumber.ERROR;\n }\n\n const hasWarn = event.logs.some((log) => log.level === \"warn\");\n if (hasWarn) {\n return SeverityNumber.WARN;\n }\n }\n\n return SeverityNumber.INFO;\n }\n\n /**\n * Get severity text based on severity number\n */\n private getSeverityText(event: WideEventData): string {\n const severityNumber = this.getSeverityNumber(event);\n\n if (severityNumber >= SeverityNumber.ERROR) {\n return \"ERROR\";\n }\n if (severityNumber >= SeverityNumber.WARN) {\n return \"WARN\";\n }\n if (severityNumber >= SeverityNumber.INFO) {\n return \"INFO\";\n }\n return \"DEBUG\";\n }\n\n /**\n * Create log body from event data\n */\n private createLogBody(event: WideEventData): string {\n const parts: string[] = [];\n\n // HTTP request info\n if (event.method && event.path) {\n parts.push(`${event.method} ${event.path}`);\n }\n\n // Status code\n if (event.status_code) {\n parts.push(`→ ${event.status_code}`);\n }\n\n // Duration\n if (event.duration_ms) {\n parts.push(`(${event.duration_ms}ms)`);\n }\n\n // Component info\n if (event.component) {\n const componentStr = event.component.operation\n ? `${event.component.name}.${event.component.operation}`\n : event.component.name;\n parts.push(`[${componentStr}]`);\n }\n\n // Error message\n if (event.error) {\n parts.push(`ERROR: ${event.error.message}`);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Create OpenTelemetry attributes from event data\n */\n private createAttributes(\n event: WideEventData,\n ): Record<string, string | number | boolean | undefined> {\n const attributes: Record<string, string | number | boolean | undefined> = {\n // Request metadata\n request_id: event.request_id,\n trace_id: event.trace_id,\n\n // HTTP attributes (OpenTelemetry semantic conventions)\n \"http.method\": event.method,\n \"http.route\": event.path,\n \"http.status_code\": event.status_code,\n \"http.request.duration_ms\": event.duration_ms,\n\n // Service attributes\n \"service.name\": event.service?.name,\n \"service.version\": event.service?.version,\n \"service.region\": event.service?.region,\n \"service.deployment_id\": event.service?.deployment_id,\n \"service.node_env\": event.service?.node_env,\n\n // Component attributes\n \"component.name\": event.component?.name,\n \"component.operation\": event.component?.operation,\n\n // User attributes\n \"user.id\": event.user?.id,\n\n // Error attributes\n \"error.type\": event.error?.type,\n \"error.code\": event.error?.code,\n \"error.message\": event.error?.message,\n \"error.retriable\": event.error?.retriable,\n\n // Execution metadata\n \"execution.timeout_ms\": event.execution?.timeout_ms,\n \"execution.retry_attempts\": event.execution?.retry_attempts,\n \"execution.cache_hit\": event.execution?.cache_hit,\n \"execution.cache_key\": event.execution?.cache_key,\n \"execution.cache_deduplication\": event.execution?.cache_deduplication,\n\n // Stream metadata\n \"stream.id\": event.stream?.stream_id,\n \"stream.events_sent\": event.stream?.events_sent,\n\n // Log count\n log_count: event.logs?.length,\n };\n\n // Add custom context as attributes with scope prefix (no \"appkit\" prefix)\n if (event.context) {\n for (const [scope, scopeData] of Object.entries(event.context)) {\n for (const [key, value] of Object.entries(scopeData)) {\n // Only add primitive values\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n attributes[`${scope}.${key}`] = value;\n }\n }\n }\n }\n\n // Remove undefined values\n return Object.fromEntries(\n Object.entries(attributes).filter(([_, value]) => value !== undefined),\n );\n }\n}\n"],"mappings":"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"}
|