@databricks/appkit 0.1.4 → 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.
Files changed (163) hide show
  1. package/AGENTS.md +89 -12
  2. package/CLAUDE.md +89 -12
  3. package/NOTICE.md +4 -0
  4. package/README.md +21 -15
  5. package/bin/appkit-lint.js +129 -0
  6. package/dist/analytics/analytics.d.ts +33 -8
  7. package/dist/analytics/analytics.d.ts.map +1 -1
  8. package/dist/analytics/analytics.js +67 -27
  9. package/dist/analytics/analytics.js.map +1 -1
  10. package/dist/analytics/defaults.js.map +1 -1
  11. package/dist/analytics/query.js +12 -6
  12. package/dist/analytics/query.js.map +1 -1
  13. package/dist/app/index.d.ts.map +1 -1
  14. package/dist/app/index.js +7 -5
  15. package/dist/app/index.js.map +1 -1
  16. package/dist/appkit/package.js +1 -1
  17. package/dist/cache/defaults.js.map +1 -1
  18. package/dist/cache/index.d.ts +1 -0
  19. package/dist/cache/index.d.ts.map +1 -1
  20. package/dist/cache/index.js +25 -5
  21. package/dist/cache/index.js.map +1 -1
  22. package/dist/cache/storage/memory.js.map +1 -1
  23. package/dist/cache/storage/persistent.js +12 -6
  24. package/dist/cache/storage/persistent.js.map +1 -1
  25. package/dist/connectors/lakebase/client.js +31 -21
  26. package/dist/connectors/lakebase/client.js.map +1 -1
  27. package/dist/connectors/lakebase/defaults.js.map +1 -1
  28. package/dist/connectors/sql-warehouse/client.js +68 -28
  29. package/dist/connectors/sql-warehouse/client.js.map +1 -1
  30. package/dist/connectors/sql-warehouse/defaults.js.map +1 -1
  31. package/dist/context/execution-context.js +75 -0
  32. package/dist/context/execution-context.js.map +1 -0
  33. package/dist/context/index.js +27 -0
  34. package/dist/context/index.js.map +1 -0
  35. package/dist/context/service-context.js +154 -0
  36. package/dist/context/service-context.js.map +1 -0
  37. package/dist/context/user-context.js +15 -0
  38. package/dist/context/user-context.js.map +1 -0
  39. package/dist/core/appkit.d.ts +3 -0
  40. package/dist/core/appkit.d.ts.map +1 -1
  41. package/dist/core/appkit.js +7 -0
  42. package/dist/core/appkit.js.map +1 -1
  43. package/dist/errors/authentication.d.ts +38 -0
  44. package/dist/errors/authentication.d.ts.map +1 -0
  45. package/dist/errors/authentication.js +48 -0
  46. package/dist/errors/authentication.js.map +1 -0
  47. package/dist/errors/base.d.ts +58 -0
  48. package/dist/errors/base.d.ts.map +1 -0
  49. package/dist/errors/base.js +70 -0
  50. package/dist/errors/base.js.map +1 -0
  51. package/dist/errors/configuration.d.ts +38 -0
  52. package/dist/errors/configuration.d.ts.map +1 -0
  53. package/dist/errors/configuration.js +45 -0
  54. package/dist/errors/configuration.js.map +1 -0
  55. package/dist/errors/connection.d.ts +42 -0
  56. package/dist/errors/connection.d.ts.map +1 -0
  57. package/dist/errors/connection.js +54 -0
  58. package/dist/errors/connection.js.map +1 -0
  59. package/dist/errors/execution.d.ts +42 -0
  60. package/dist/errors/execution.d.ts.map +1 -0
  61. package/dist/errors/execution.js +51 -0
  62. package/dist/errors/execution.js.map +1 -0
  63. package/dist/errors/index.js +28 -0
  64. package/dist/errors/index.js.map +1 -0
  65. package/dist/errors/initialization.d.ts +34 -0
  66. package/dist/errors/initialization.d.ts.map +1 -0
  67. package/dist/errors/initialization.js +42 -0
  68. package/dist/errors/initialization.js.map +1 -0
  69. package/dist/errors/server.d.ts +38 -0
  70. package/dist/errors/server.d.ts.map +1 -0
  71. package/dist/errors/server.js +45 -0
  72. package/dist/errors/server.js.map +1 -0
  73. package/dist/errors/tunnel.d.ts +38 -0
  74. package/dist/errors/tunnel.d.ts.map +1 -0
  75. package/dist/errors/tunnel.js +51 -0
  76. package/dist/errors/tunnel.js.map +1 -0
  77. package/dist/errors/validation.d.ts +36 -0
  78. package/dist/errors/validation.d.ts.map +1 -0
  79. package/dist/errors/validation.js +45 -0
  80. package/dist/errors/validation.js.map +1 -0
  81. package/dist/index.d.ts +12 -4
  82. package/dist/index.js +12 -4
  83. package/dist/index.js.map +1 -1
  84. package/dist/logging/logger.js +179 -0
  85. package/dist/logging/logger.js.map +1 -0
  86. package/dist/logging/sampling.js +56 -0
  87. package/dist/logging/sampling.js.map +1 -0
  88. package/dist/logging/wide-event-emitter.js +108 -0
  89. package/dist/logging/wide-event-emitter.js.map +1 -0
  90. package/dist/logging/wide-event.js +167 -0
  91. package/dist/logging/wide-event.js.map +1 -0
  92. package/dist/plugin/dev-reader.d.ts.map +1 -1
  93. package/dist/plugin/dev-reader.js +8 -3
  94. package/dist/plugin/dev-reader.js.map +1 -1
  95. package/dist/plugin/interceptors/cache.js.map +1 -1
  96. package/dist/plugin/interceptors/retry.js +10 -2
  97. package/dist/plugin/interceptors/retry.js.map +1 -1
  98. package/dist/plugin/interceptors/telemetry.js +24 -9
  99. package/dist/plugin/interceptors/telemetry.js.map +1 -1
  100. package/dist/plugin/interceptors/timeout.js +4 -0
  101. package/dist/plugin/interceptors/timeout.js.map +1 -1
  102. package/dist/plugin/plugin.d.ts +38 -4
  103. package/dist/plugin/plugin.d.ts.map +1 -1
  104. package/dist/plugin/plugin.js +86 -5
  105. package/dist/plugin/plugin.js.map +1 -1
  106. package/dist/plugin/to-plugin.d.ts +4 -0
  107. package/dist/plugin/to-plugin.d.ts.map +1 -1
  108. package/dist/plugin/to-plugin.js +3 -0
  109. package/dist/plugin/to-plugin.js.map +1 -1
  110. package/dist/server/index.d.ts +3 -0
  111. package/dist/server/index.d.ts.map +1 -1
  112. package/dist/server/index.js +25 -21
  113. package/dist/server/index.js.map +1 -1
  114. package/dist/server/remote-tunnel/remote-tunnel-controller.js +4 -2
  115. package/dist/server/remote-tunnel/remote-tunnel-controller.js.map +1 -1
  116. package/dist/server/remote-tunnel/remote-tunnel-manager.js +10 -8
  117. package/dist/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  118. package/dist/server/utils.js.map +1 -1
  119. package/dist/server/vite-dev-server.js +8 -5
  120. package/dist/server/vite-dev-server.js.map +1 -1
  121. package/dist/shared/src/sql/helpers.js.map +1 -1
  122. package/dist/stream/arrow-stream-processor.js +13 -6
  123. package/dist/stream/arrow-stream-processor.js.map +1 -1
  124. package/dist/stream/buffers.js +5 -1
  125. package/dist/stream/buffers.js.map +1 -1
  126. package/dist/stream/sse-writer.js.map +1 -1
  127. package/dist/stream/stream-manager.d.ts.map +1 -1
  128. package/dist/stream/stream-manager.js +47 -36
  129. package/dist/stream/stream-manager.js.map +1 -1
  130. package/dist/stream/stream-registry.js.map +1 -1
  131. package/dist/stream/types.js.map +1 -1
  132. package/dist/telemetry/index.d.ts +2 -2
  133. package/dist/telemetry/index.js +2 -2
  134. package/dist/telemetry/instrumentations.js +14 -10
  135. package/dist/telemetry/instrumentations.js.map +1 -1
  136. package/dist/telemetry/telemetry-manager.js +8 -6
  137. package/dist/telemetry/telemetry-manager.js.map +1 -1
  138. package/dist/telemetry/trace-sampler.js +33 -0
  139. package/dist/telemetry/trace-sampler.js.map +1 -0
  140. package/dist/type-generator/index.js +4 -2
  141. package/dist/type-generator/index.js.map +1 -1
  142. package/dist/type-generator/query-registry.js +4 -2
  143. package/dist/type-generator/query-registry.js.map +1 -1
  144. package/dist/type-generator/types.js.map +1 -1
  145. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  146. package/dist/type-generator/vite-plugin.js +5 -3
  147. package/dist/type-generator/vite-plugin.js.map +1 -1
  148. package/dist/utils/env-validator.js +5 -5
  149. package/dist/utils/env-validator.js.map +1 -1
  150. package/dist/utils/merge.js +1 -5
  151. package/dist/utils/merge.js.map +1 -1
  152. package/dist/utils/path-exclusions.js +66 -0
  153. package/dist/utils/path-exclusions.js.map +1 -0
  154. package/dist/utils/vite-config-merge.js +1 -5
  155. package/dist/utils/vite-config-merge.js.map +1 -1
  156. package/llms.txt +89 -12
  157. package/package.json +6 -1
  158. package/dist/utils/databricks-client-middleware.d.ts +0 -17
  159. package/dist/utils/databricks-client-middleware.d.ts.map +0 -1
  160. package/dist/utils/databricks-client-middleware.js +0 -117
  161. package/dist/utils/databricks-client-middleware.js.map +0 -1
  162. package/dist/utils/index.js +0 -26
  163. package/dist/utils/index.js.map +0 -1
@@ -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/server.ts
5
+ var ServerError;
6
+ var init_server = __esmMin((() => {
7
+ init_base();
8
+ ServerError = class ServerError extends AppKitError {
9
+ constructor(..._args) {
10
+ super(..._args);
11
+ this.code = "SERVER_ERROR";
12
+ this.statusCode = 500;
13
+ this.isRetryable = false;
14
+ }
15
+ /**
16
+ * Create a server error for autoStart conflict
17
+ */
18
+ static autoStartConflict(operation) {
19
+ return new ServerError(`Cannot ${operation} when autoStart is true`, { context: { operation } });
20
+ }
21
+ /**
22
+ * Create a server error for server not started
23
+ */
24
+ static notStarted() {
25
+ return new ServerError("Server not started. Please start the server first by calling the start() method");
26
+ }
27
+ /**
28
+ * Create a server error for Vite dev server not initialized
29
+ */
30
+ static viteNotInitialized() {
31
+ return new ServerError("Vite dev server not initialized");
32
+ }
33
+ /**
34
+ * Create a server error for missing client directory
35
+ */
36
+ static clientDirectoryNotFound(searchedPaths) {
37
+ return new ServerError(`Could not find client directory. Searched for vite.config.ts/js + index.html in: ${searchedPaths.join(", ")}`, { context: { searchedPaths } });
38
+ }
39
+ };
40
+ }));
41
+
42
+ //#endregion
43
+ init_server();
44
+ export { ServerError, init_server };
45
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../src/errors/server.ts"],"sourcesContent":["import { AppKitError } from \"./base\";\n\n/**\n * Error thrown when server lifecycle operations fail.\n * Use for server start/stop issues, configuration conflicts, etc.\n *\n * @example\n * ```typescript\n * throw new ServerError(\"Cannot get server when autoStart is true\");\n * throw new ServerError(\"Server not started\");\n * ```\n */\nexport class ServerError extends AppKitError {\n readonly code = \"SERVER_ERROR\";\n readonly statusCode = 500;\n readonly isRetryable = false;\n\n /**\n * Create a server error for autoStart conflict\n */\n static autoStartConflict(operation: string): ServerError {\n return new ServerError(`Cannot ${operation} when autoStart is true`, {\n context: { operation },\n });\n }\n\n /**\n * Create a server error for server not started\n */\n static notStarted(): ServerError {\n return new ServerError(\n \"Server not started. Please start the server first by calling the start() method\",\n );\n }\n\n /**\n * Create a server error for Vite dev server not initialized\n */\n static viteNotInitialized(): ServerError {\n return new ServerError(\"Vite dev server not initialized\");\n }\n\n /**\n * Create a server error for missing client directory\n */\n static clientDirectoryNotFound(searchedPaths: string[]): ServerError {\n return new ServerError(\n `Could not find client directory. Searched for vite.config.ts/js + index.html in: ${searchedPaths.join(\", \")}`,\n { context: { searchedPaths } },\n );\n }\n}\n"],"mappings":";;;;;;YAAqC;CAYxB,cAAb,MAAa,oBAAoB,YAAY;;;eAC3B;qBACM;sBACC;;;;;EAKvB,OAAO,kBAAkB,WAAgC;AACvD,UAAO,IAAI,YAAY,UAAU,UAAU,0BAA0B,EACnE,SAAS,EAAE,WAAW,EACvB,CAAC;;;;;EAMJ,OAAO,aAA0B;AAC/B,UAAO,IAAI,YACT,kFACD;;;;;EAMH,OAAO,qBAAkC;AACvC,UAAO,IAAI,YAAY,kCAAkC;;;;;EAM3D,OAAO,wBAAwB,eAAsC;AACnE,UAAO,IAAI,YACT,oFAAoF,cAAc,KAAK,KAAK,IAC5G,EAAE,SAAS,EAAE,eAAe,EAAE,CAC/B"}
@@ -0,0 +1,38 @@
1
+ import { AppKitError } from "./base.js";
2
+
3
+ //#region src/errors/tunnel.d.ts
4
+
5
+ /**
6
+ * Error thrown when remote tunnel operations fail.
7
+ * Use for tunnel connection issues, message parsing failures, etc.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * throw new TunnelError("No tunnel connection available");
12
+ * throw new TunnelError("Failed to parse WebSocket message", { cause: parseError });
13
+ * ```
14
+ */
15
+ declare class TunnelError extends AppKitError {
16
+ readonly code = "TUNNEL_ERROR";
17
+ readonly statusCode = 502;
18
+ readonly isRetryable = true;
19
+ /**
20
+ * Create a tunnel error for missing tunnel getter
21
+ */
22
+ static getterNotRegistered(): TunnelError;
23
+ /**
24
+ * Create a tunnel error for no available connection
25
+ */
26
+ static noConnection(): TunnelError;
27
+ /**
28
+ * Create a tunnel error for asset fetch failure
29
+ */
30
+ static fetchFailed(path: string, cause?: Error): TunnelError;
31
+ /**
32
+ * Create a tunnel error for message parsing failure
33
+ */
34
+ static parseError(messageType: string, cause?: Error): TunnelError;
35
+ }
36
+ //#endregion
37
+ export { TunnelError };
38
+ //# sourceMappingURL=tunnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.d.ts","names":[],"sources":["../../src/errors/tunnel.ts"],"sourcesContent":[],"mappings":";;;;;;AAYA;;;;;;;;AAAiC,cAApB,WAAA,SAAoB,WAAA,CAAA;EAAW,SAAA,IAAA,GAAA,cAAA;;;;;;gCAQZ;;;;yBASP;;;;2CAOkB,QAAQ;;;;iDAUF,QAAQ"}
@@ -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
@@ -1,15 +1,23 @@
1
1
  import { BasePluginConfig, IAppRouter } from "./shared/src/plugin.js";
2
+ import { CacheConfig } from "./shared/src/cache.js";
2
3
  import { StreamExecutionSettings } from "./shared/src/execute.js";
3
- import { SQLTypeMarker } from "./shared/src/sql/types.js";
4
4
  import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
5
5
  import { CacheManager } from "./cache/index.js";
6
- import { ITelemetry } from "./telemetry/types.js";
6
+ import { ITelemetry, TelemetryConfig } from "./telemetry/types.js";
7
7
  import { Counter, Histogram, SeverityNumber, Span, SpanStatusCode } from "./telemetry/index.js";
8
8
  import { Plugin } from "./plugin/plugin.js";
9
9
  import { toPlugin } from "./plugin/to-plugin.js";
10
10
  import { analytics } from "./analytics/analytics.js";
11
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
- import { getRequestContext } from "./utils/databricks-client-middleware.js";
15
- export { type BasePluginConfig, CacheManager, type Counter, type Histogram, type IAppRouter, type ITelemetry, Plugin, type SQLTypeMarker, SeverityNumber, type Span, SpanStatusCode, type StreamExecutionSettings, analytics, appKitTypesPlugin, createApp, getRequestContext, 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,7 +1,15 @@
1
1
  import { isSQLTypeMarker, sql } from "./shared/src/sql/helpers.js";
2
2
  import { SeverityNumber, SpanStatusCode } from "./telemetry/index.js";
3
- import { getRequestContext } from "./utils/databricks-client-middleware.js";
4
- import { init_utils } from "./utils/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";
5
13
  import { CacheManager } from "./cache/index.js";
6
14
  import { Plugin } from "./plugin/plugin.js";
7
15
  import { toPlugin } from "./plugin/to-plugin.js";
@@ -14,8 +22,8 @@ import { appKitTypesPlugin } from "./type-generator/vite-plugin.js";
14
22
  import { server } from "./server/index.js";
15
23
 
16
24
  //#region src/index.ts
17
- init_utils();
25
+ init_errors();
18
26
 
19
27
  //#endregion
20
- export { CacheManager, Plugin, SeverityNumber, SpanStatusCode, analytics, appKitTypesPlugin, createApp, getRequestContext, isSQLTypeMarker, server, sql, toPlugin };
28
+ export { AppKitError, AuthenticationError, CacheManager, ConfigurationError, ConnectionError, ExecutionError, InitializationError, Plugin, ServerError, SeverityNumber, SpanStatusCode, TunnelError, ValidationError, analytics, appKitTypesPlugin, createApp, isSQLTypeMarker, server, sql, toPlugin };
21
29
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type {\n BasePluginConfig,\n IAppRouter,\n SQLTypeMarker,\n StreamExecutionSettings,\n} from \"shared\";\nexport {\n isSQLTypeMarker,\n sql,\n} from \"shared\";\nexport { analytics } from \"./analytics\";\nexport { CacheManager } from \"./cache\";\nexport { createApp } from \"./core\";\nexport { Plugin, toPlugin } from \"./plugin\";\nexport { server } from \"./server\";\nexport type { ITelemetry } from \"./telemetry\";\nexport {\n type Counter,\n type Histogram,\n SeverityNumber,\n type Span,\n SpanStatusCode,\n} from \"./telemetry\";\nexport { appKitTypesPlugin } from \"./type-generator/vite-plugin\";\nexport { getRequestContext } from \"./utils\";\n"],"mappings":";;;;;;;;;;;;;;;;YAwB4C"}
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":"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"}
@@ -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"}