silgi 0.0.14 → 0.1.0-beta.10

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 (210) hide show
  1. package/README.md +102 -1
  2. package/dist/_virtual/_rolldown/runtime.mjs +5 -0
  3. package/dist/adapters/_fetch-adapter.d.mts +18 -0
  4. package/dist/adapters/_fetch-adapter.mjs +53 -0
  5. package/dist/adapters/astro.d.mts +15 -0
  6. package/dist/adapters/astro.mjs +31 -0
  7. package/dist/adapters/aws-lambda.d.mts +42 -0
  8. package/dist/adapters/aws-lambda.mjs +92 -0
  9. package/dist/adapters/express.d.mts +16 -0
  10. package/dist/adapters/express.mjs +120 -0
  11. package/dist/adapters/message-port.d.mts +42 -0
  12. package/dist/adapters/message-port.mjs +132 -0
  13. package/dist/adapters/nestjs.d.mts +25 -0
  14. package/dist/adapters/nestjs.mjs +75 -0
  15. package/dist/adapters/nextjs.d.mts +14 -0
  16. package/dist/adapters/nextjs.mjs +29 -0
  17. package/dist/adapters/peer.d.mts +27 -0
  18. package/dist/adapters/peer.mjs +36 -0
  19. package/dist/adapters/remix.d.mts +15 -0
  20. package/dist/adapters/remix.mjs +30 -0
  21. package/dist/adapters/solidstart.d.mts +12 -0
  22. package/dist/adapters/solidstart.mjs +29 -0
  23. package/dist/adapters/sveltekit.d.mts +14 -0
  24. package/dist/adapters/sveltekit.mjs +30 -0
  25. package/dist/broker/index.d.mts +62 -0
  26. package/dist/broker/index.mjs +153 -0
  27. package/dist/broker/nats.d.mts +33 -0
  28. package/dist/broker/nats.mjs +31 -0
  29. package/dist/broker/redis.d.mts +51 -0
  30. package/dist/broker/redis.mjs +92 -0
  31. package/dist/builder.d.mts +55 -0
  32. package/dist/builder.mjs +70 -0
  33. package/dist/callable.d.mts +19 -0
  34. package/dist/callable.mjs +42 -0
  35. package/dist/caller.mjs +90 -0
  36. package/dist/client/adapters/fetch/index.d.mts +19 -0
  37. package/dist/client/adapters/fetch/index.mjs +82 -0
  38. package/dist/client/adapters/ofetch/index.d.mts +57 -0
  39. package/dist/client/adapters/ofetch/index.mjs +100 -0
  40. package/dist/client/adapters/websocket/index.d.mts +20 -0
  41. package/dist/client/adapters/websocket/index.mjs +101 -0
  42. package/dist/client/client.d.mts +37 -0
  43. package/dist/client/client.mjs +80 -0
  44. package/dist/client/consume.d.mts +50 -0
  45. package/dist/client/consume.mjs +66 -0
  46. package/dist/client/dynamic-link.d.mts +16 -0
  47. package/dist/client/dynamic-link.mjs +19 -0
  48. package/dist/client/index.d.mts +6 -0
  49. package/dist/client/index.mjs +5 -0
  50. package/dist/client/interceptor.d.mts +31 -0
  51. package/dist/client/interceptor.mjs +34 -0
  52. package/dist/client/openapi.d.mts +29 -0
  53. package/dist/client/openapi.mjs +89 -0
  54. package/dist/client/plugins/batch.d.mts +26 -0
  55. package/dist/client/plugins/batch.mjs +64 -0
  56. package/dist/client/plugins/circuit-breaker.d.mts +24 -0
  57. package/dist/client/plugins/circuit-breaker.mjs +60 -0
  58. package/dist/client/plugins/csrf.d.mts +13 -0
  59. package/dist/client/plugins/csrf.mjs +20 -0
  60. package/dist/client/plugins/dedupe.d.mts +10 -0
  61. package/dist/client/plugins/dedupe.mjs +28 -0
  62. package/dist/client/plugins/index.d.mts +8 -0
  63. package/dist/client/plugins/index.mjs +8 -0
  64. package/dist/client/plugins/otel.d.mts +12 -0
  65. package/dist/client/plugins/otel.mjs +27 -0
  66. package/dist/client/plugins/retry.d.mts +34 -0
  67. package/dist/client/plugins/retry.mjs +79 -0
  68. package/dist/client/plugins/timeout.d.mts +10 -0
  69. package/dist/client/plugins/timeout.mjs +14 -0
  70. package/dist/client/server.d.mts +16 -0
  71. package/dist/client/server.mjs +62 -0
  72. package/dist/client/types.d.mts +29 -0
  73. package/dist/codec/devalue.d.mts +21 -0
  74. package/dist/codec/devalue.mjs +33 -0
  75. package/dist/codec/msgpack.d.mts +18 -0
  76. package/dist/codec/msgpack.mjs +45 -0
  77. package/dist/codec/sanitize.mjs +38 -0
  78. package/dist/compile.d.mts +46 -0
  79. package/dist/compile.mjs +332 -0
  80. package/dist/contract.d.mts +36 -0
  81. package/dist/contract.mjs +40 -0
  82. package/dist/core/codec.mjs +67 -0
  83. package/dist/core/dispatch.mjs +62 -0
  84. package/dist/core/error.d.mts +104 -0
  85. package/dist/core/error.mjs +139 -0
  86. package/dist/core/handler.d.mts +6 -0
  87. package/dist/core/handler.mjs +148 -0
  88. package/dist/core/input.mjs +49 -0
  89. package/dist/core/iterator.d.mts +17 -0
  90. package/dist/core/iterator.mjs +79 -0
  91. package/dist/core/router-utils.d.mts +25 -0
  92. package/dist/core/router-utils.mjs +98 -0
  93. package/dist/core/schema.d.mts +20 -0
  94. package/dist/core/schema.mjs +33 -0
  95. package/dist/core/serve.d.mts +51 -0
  96. package/dist/core/serve.mjs +76 -0
  97. package/dist/core/sse.d.mts +18 -0
  98. package/dist/core/sse.mjs +98 -0
  99. package/dist/core/storage.d.mts +20 -0
  100. package/dist/core/storage.mjs +61 -0
  101. package/dist/core/task.d.mts +62 -0
  102. package/dist/core/task.mjs +165 -0
  103. package/dist/core/utils.mjs +24 -0
  104. package/dist/index.d.mts +18 -37
  105. package/dist/index.mjs +15 -7
  106. package/dist/integrations/ai/index.d.mts +25 -0
  107. package/dist/integrations/ai/index.mjs +117 -0
  108. package/dist/integrations/better-auth/index.d.mts +61 -0
  109. package/dist/integrations/better-auth/index.mjs +332 -0
  110. package/dist/integrations/drizzle/index.d.mts +27 -0
  111. package/dist/integrations/drizzle/index.mjs +286 -0
  112. package/dist/integrations/hey-api/index.d.mts +2 -0
  113. package/dist/integrations/hey-api/index.mjs +2 -0
  114. package/dist/integrations/hey-api/to-client.d.mts +20 -0
  115. package/dist/integrations/hey-api/to-client.mjs +39 -0
  116. package/dist/integrations/pinia-colada/general-utils.d.mts +13 -0
  117. package/dist/integrations/pinia-colada/general-utils.mjs +9 -0
  118. package/dist/integrations/pinia-colada/index.d.mts +6 -0
  119. package/dist/integrations/pinia-colada/index.mjs +5 -0
  120. package/dist/integrations/pinia-colada/key.d.mts +11 -0
  121. package/dist/integrations/pinia-colada/key.mjs +11 -0
  122. package/dist/integrations/pinia-colada/procedure-utils.d.mts +25 -0
  123. package/dist/integrations/pinia-colada/procedure-utils.mjs +33 -0
  124. package/dist/integrations/pinia-colada/router-utils.d.mts +17 -0
  125. package/dist/integrations/pinia-colada/router-utils.mjs +30 -0
  126. package/dist/integrations/pinia-colada/types.d.mts +25 -0
  127. package/dist/integrations/react/index.d.mts +83 -0
  128. package/dist/integrations/react/index.mjs +196 -0
  129. package/dist/integrations/tanstack-query/index.d.mts +120 -0
  130. package/dist/integrations/tanstack-query/index.mjs +100 -0
  131. package/dist/integrations/tanstack-query/ssr.d.mts +60 -0
  132. package/dist/integrations/tanstack-query/ssr.mjs +102 -0
  133. package/dist/integrations/zod/converter.d.mts +75 -0
  134. package/dist/integrations/zod/converter.mjs +345 -0
  135. package/dist/integrations/zod/index.d.mts +2 -0
  136. package/dist/integrations/zod/index.mjs +2 -0
  137. package/dist/lazy.d.mts +22 -0
  138. package/dist/lazy.mjs +34 -0
  139. package/dist/lifecycle.d.mts +36 -0
  140. package/dist/lifecycle.mjs +46 -0
  141. package/dist/map-input.d.mts +17 -0
  142. package/dist/map-input.mjs +47 -0
  143. package/dist/plugins/analytics.d.mts +236 -0
  144. package/dist/plugins/analytics.mjs +816 -0
  145. package/dist/plugins/batch-server.d.mts +20 -0
  146. package/dist/plugins/batch-server.mjs +91 -0
  147. package/dist/plugins/body-limit.d.mts +19 -0
  148. package/dist/plugins/body-limit.mjs +49 -0
  149. package/dist/plugins/cache.d.mts +170 -0
  150. package/dist/plugins/cache.mjs +212 -0
  151. package/dist/plugins/coerce.d.mts +24 -0
  152. package/dist/plugins/coerce.mjs +70 -0
  153. package/dist/plugins/cookies.d.mts +14 -0
  154. package/dist/plugins/cookies.mjs +48 -0
  155. package/dist/plugins/cors.d.mts +43 -0
  156. package/dist/plugins/cors.mjs +62 -0
  157. package/dist/plugins/file-upload.d.mts +38 -0
  158. package/dist/plugins/file-upload.mjs +102 -0
  159. package/dist/plugins/index.d.mts +14 -0
  160. package/dist/plugins/index.mjs +14 -0
  161. package/dist/plugins/otel.d.mts +35 -0
  162. package/dist/plugins/otel.mjs +40 -0
  163. package/dist/plugins/pino.d.mts +60 -0
  164. package/dist/plugins/pino.mjs +42 -0
  165. package/dist/plugins/pubsub.d.mts +50 -0
  166. package/dist/plugins/pubsub.mjs +53 -0
  167. package/dist/plugins/ratelimit.d.mts +53 -0
  168. package/dist/plugins/ratelimit.mjs +92 -0
  169. package/dist/plugins/signing.d.mts +41 -0
  170. package/dist/plugins/signing.mjs +118 -0
  171. package/dist/plugins/strict-get.d.mts +10 -0
  172. package/dist/plugins/strict-get.mjs +33 -0
  173. package/dist/scalar.d.mts +49 -0
  174. package/dist/scalar.mjs +311 -0
  175. package/dist/silgi.d.mts +144 -0
  176. package/dist/silgi.mjs +156 -0
  177. package/dist/trpc-interop.d.mts +22 -0
  178. package/dist/trpc-interop.mjs +68 -0
  179. package/dist/types.d.mts +108 -0
  180. package/dist/ws.d.mts +88 -0
  181. package/dist/ws.mjs +205 -0
  182. package/lib/dashboard/index.html +123 -0
  183. package/lib/ocache.d.mts +1 -0
  184. package/lib/ocache.mjs +1 -0
  185. package/lib/ofetch.d.mts +1 -0
  186. package/lib/ofetch.mjs +1 -0
  187. package/lib/srvx.d.mts +1 -0
  188. package/lib/srvx.mjs +1 -0
  189. package/lib/unstorage.d.mts +1 -0
  190. package/lib/unstorage.mjs +1 -0
  191. package/package.json +324 -65
  192. package/bin/silgi.mjs +0 -3
  193. package/dist/chunks/generate.mjs +0 -933
  194. package/dist/chunks/init.mjs +0 -21
  195. package/dist/cli/config.d.mts +0 -19
  196. package/dist/cli/config.d.ts +0 -19
  197. package/dist/cli/config.mjs +0 -5
  198. package/dist/cli/index.d.mts +0 -2
  199. package/dist/cli/index.d.ts +0 -2
  200. package/dist/cli/index.mjs +0 -119
  201. package/dist/index.d.ts +0 -37
  202. package/dist/plugins/openapi.d.mts +0 -138
  203. package/dist/plugins/openapi.d.ts +0 -138
  204. package/dist/plugins/openapi.mjs +0 -204
  205. package/dist/plugins/scalar.d.mts +0 -14
  206. package/dist/plugins/scalar.d.ts +0 -14
  207. package/dist/plugins/scalar.mjs +0 -66
  208. package/dist/shared/silgi.BMCYk2cR.mjs +0 -841
  209. package/dist/shared/silgi.D5qK9QOm.d.mts +0 -301
  210. package/dist/shared/silgi.D5qK9QOm.d.ts +0 -301
@@ -0,0 +1,117 @@
1
+ import { compileProcedure } from "../../compile.mjs";
2
+ import { jsonSchema, tool } from "ai";
3
+ //#region src/integrations/ai/index.ts
4
+ /**
5
+ * Vercel AI SDK integration — expose silgi procedures as AI tools.
6
+ *
7
+ * Converts silgi router procedures into AI SDK tools that can be
8
+ * called by LLMs via function calling / tool use.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { generateText } from "ai"
13
+ * import { openai } from "@ai-sdk/openai"
14
+ * import { routerToTools } from "silgi/ai"
15
+ *
16
+ * const tools = routerToTools(appRouter)
17
+ *
18
+ * const { text } = await generateText({
19
+ * model: openai("gpt-4o"),
20
+ * tools,
21
+ * prompt: "List all users with limit 5",
22
+ * })
23
+ * ```
24
+ */
25
+ /**
26
+ * Convert a single silgi procedure into an AI SDK tool.
27
+ */
28
+ function procedureToTool(name, procedure, options) {
29
+ const handler = compileProcedure(procedure);
30
+ return tool({
31
+ description: options?.description ?? procedure.route?.description ?? procedure.route?.summary ?? `Call ${name}`,
32
+ parameters: jsonSchema(procedure.input ? zodToJsonSchemaSimple(procedure.input) : {
33
+ type: "object",
34
+ properties: {}
35
+ }),
36
+ execute: async (input, execOptions) => {
37
+ const ctx = Object.create(null);
38
+ const signal = execOptions?.abortSignal ?? new AbortController().signal;
39
+ const result = handler(ctx, input ?? {}, signal);
40
+ return result instanceof Promise ? await result : result;
41
+ }
42
+ });
43
+ }
44
+ /**
45
+ * Convert all procedures in a silgi router into AI SDK tools.
46
+ * Nested routers are flattened with underscore separators.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * // Router: { users: { list, create }, health }
51
+ * // Tools: { users_list, users_create, health }
52
+ * ```
53
+ */
54
+ function routerToTools(router, options) {
55
+ const tools = {};
56
+ collectProcedures(router, [], (path, proc) => {
57
+ const flatName = path.join("_");
58
+ if (options?.filter && !options.filter(flatName, proc)) return;
59
+ tools[flatName] = procedureToTool(flatName, proc, { description: options?.descriptions?.[flatName] });
60
+ });
61
+ return tools;
62
+ }
63
+ function isProcedureDef(v) {
64
+ return typeof v === "object" && v !== null && "type" in v && "resolve" in v;
65
+ }
66
+ function collectProcedures(node, path, cb) {
67
+ if (isProcedureDef(node)) {
68
+ cb(path, node);
69
+ return;
70
+ }
71
+ if (typeof node === "object" && node !== null) for (const [key, child] of Object.entries(node)) collectProcedures(child, [...path, key], cb);
72
+ }
73
+ /** Simple Zod → JSON Schema for AI tool parameters */
74
+ function zodToJsonSchemaSimple(schema) {
75
+ const zod = schema?._zod ?? schema?._def;
76
+ if (!zod) return {
77
+ type: "object",
78
+ properties: {}
79
+ };
80
+ return convertDef(zod.def ?? zod);
81
+ }
82
+ function convertDef(def) {
83
+ if (!def) return {};
84
+ switch (def.type ?? def.typeName) {
85
+ case "string": return { type: "string" };
86
+ case "number":
87
+ case "float": return { type: "number" };
88
+ case "int": return { type: "integer" };
89
+ case "boolean": return { type: "boolean" };
90
+ case "object": {
91
+ const props = {};
92
+ const required = [];
93
+ if (def.shape) for (const [k, v] of Object.entries(def.shape)) {
94
+ props[k] = zodToJsonSchemaSimple(v);
95
+ const fz = v?._zod?.def ?? v?._def;
96
+ if (fz?.type !== "optional" && fz?.typeName !== "ZodOptional") required.push(k);
97
+ }
98
+ return {
99
+ type: "object",
100
+ properties: props,
101
+ ...required.length ? { required } : {}
102
+ };
103
+ }
104
+ case "array": return {
105
+ type: "array",
106
+ ...def.element ? { items: zodToJsonSchemaSimple(def.element) } : {}
107
+ };
108
+ case "optional": return zodToJsonSchemaSimple(def.innerType ?? def.inner);
109
+ case "enum": return {
110
+ type: "string",
111
+ enum: def.values ?? def.entries
112
+ };
113
+ default: return {};
114
+ }
115
+ }
116
+ //#endregion
117
+ export { procedureToTool, routerToTools };
@@ -0,0 +1,61 @@
1
+ //#region src/integrations/better-auth/index.d.ts
2
+ /**
3
+ * Silgi + Better Auth tracing integration.
4
+ *
5
+ * Provides a Better Auth plugin factory that auto-traces all auth operations
6
+ * (sign-in, sign-up, OAuth, session management, etc.) into silgi analytics.
7
+ *
8
+ * The silgi request context is passed via `request.__silgiCtx`, set by
9
+ * the silgi auth handler before calling `auth.handler(request)`.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { tracing } from 'silgi/better-auth'
14
+ *
15
+ * const auth = betterAuth({
16
+ * plugins: [
17
+ * tracing(), // auto-traces all auth operations
18
+ * ],
19
+ * })
20
+ * ```
21
+ */
22
+ interface TracingConfig {
23
+ /** Capture request body as span input (default: true) */
24
+ captureInput?: boolean;
25
+ /** Capture response data as span output (default: true) */
26
+ captureOutput?: boolean;
27
+ /** Pass `createAuthMiddleware` from `better-auth/api` to wrap hooks handler */
28
+ createAuthMiddleware?: (handler: any) => any;
29
+ }
30
+ /**
31
+ * Creates a Better Auth plugin that auto-traces all auth operations
32
+ * into silgi analytics spans.
33
+ *
34
+ * @param config - Optional configuration
35
+ * @returns A Better Auth plugin (typed as `any` to avoid requiring better-auth types at build time)
36
+ */
37
+ declare function tracing(config?: TracingConfig): any;
38
+ /**
39
+ * Instrument a Better Auth instance to trace all `auth.api.*` method calls.
40
+ * Works with `withCtx` — programmatic calls from background jobs,
41
+ * server-side session fetches etc. are traced when context is available.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { instrumentBetterAuth, withCtx } from 'silgi/better-auth'
46
+ *
47
+ * const auth = instrumentBetterAuth(betterAuth({ ... }))
48
+ *
49
+ * // In a silgi procedure:
50
+ * const me = s.$resolve(async ({ ctx }) => {
51
+ * return withCtx(ctx, () => auth.api.getSession({ headers: ctx.headers }))
52
+ * })
53
+ * ```
54
+ */
55
+ declare function instrumentBetterAuth<T extends Record<string, any>>(auth: T): T;
56
+ /**
57
+ * Run a function with silgi context available to instrumented Better Auth API calls.
58
+ */
59
+ declare function withCtx<T>(ctx: Record<string, unknown>, fn: () => T): T;
60
+ //#endregion
61
+ export { TracingConfig, instrumentBetterAuth, tracing, withCtx };
@@ -0,0 +1,332 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ //#region src/integrations/better-auth/index.ts
3
+ /**
4
+ * Silgi + Better Auth tracing integration.
5
+ *
6
+ * Provides a Better Auth plugin factory that auto-traces all auth operations
7
+ * (sign-in, sign-up, OAuth, session management, etc.) into silgi analytics.
8
+ *
9
+ * The silgi request context is passed via `request.__silgiCtx`, set by
10
+ * the silgi auth handler before calling `auth.handler(request)`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { tracing } from 'silgi/better-auth'
15
+ *
16
+ * const auth = betterAuth({
17
+ * plugins: [
18
+ * tracing(), // auto-traces all auth operations
19
+ * ],
20
+ * })
21
+ * ```
22
+ */
23
+ const ctxStorage = new AsyncLocalStorage();
24
+ function matchOperation(path) {
25
+ const normalized = path.replace(/^\/+/, "");
26
+ if (normalized.endsWith("/sign-up/email") || normalized === "sign-up/email") return {
27
+ spanName: "auth.signup.email",
28
+ operation: "signup",
29
+ method: "email"
30
+ };
31
+ if (normalized.endsWith("/sign-in/email") || normalized === "sign-in/email") return {
32
+ spanName: "auth.signin.email",
33
+ operation: "signin",
34
+ method: "email"
35
+ };
36
+ if (normalized.endsWith("/sign-out") || normalized === "sign-out") return {
37
+ spanName: "auth.signout",
38
+ operation: "signout"
39
+ };
40
+ if (normalized.endsWith("/get-session") || normalized === "get-session") return {
41
+ spanName: "auth.get_session",
42
+ operation: "get_session"
43
+ };
44
+ if (normalized.endsWith("/update-user") || normalized === "update-user") return {
45
+ spanName: "auth.update_user",
46
+ operation: "update_user"
47
+ };
48
+ if (normalized.endsWith("/delete-user") || normalized === "delete-user") return {
49
+ spanName: "auth.delete_user",
50
+ operation: "delete_user"
51
+ };
52
+ if (normalized.endsWith("/change-password") || normalized === "change-password") return {
53
+ spanName: "auth.change_password",
54
+ operation: "change_password"
55
+ };
56
+ if (normalized.endsWith("/change-email") || normalized === "change-email") return {
57
+ spanName: "auth.change_email",
58
+ operation: "change_email"
59
+ };
60
+ if (normalized.endsWith("/verify-email") || normalized === "verify-email") return {
61
+ spanName: "auth.verify_email",
62
+ operation: "verify_email"
63
+ };
64
+ if (normalized.endsWith("/forget-password") || normalized === "forget-password") return {
65
+ spanName: "auth.forgot_password",
66
+ operation: "forgot_password"
67
+ };
68
+ if (normalized.endsWith("/reset-password") || normalized === "reset-password") return {
69
+ spanName: "auth.reset_password",
70
+ operation: "reset_password"
71
+ };
72
+ if (normalized.endsWith("/list-sessions") || normalized === "list-sessions") return {
73
+ spanName: "auth.list_sessions",
74
+ operation: "list_sessions"
75
+ };
76
+ if (normalized.endsWith("/revoke-session") || normalized === "revoke-session") return {
77
+ spanName: "auth.revoke_session",
78
+ operation: "revoke_session"
79
+ };
80
+ const callbackMatch = normalized.match(/\/callback\/([^/?]+)/);
81
+ if (callbackMatch) {
82
+ const provider = callbackMatch[1];
83
+ return {
84
+ spanName: `auth.oauth.callback.${provider}`,
85
+ operation: "oauth_callback",
86
+ method: "oauth",
87
+ provider
88
+ };
89
+ }
90
+ const signinMatch = normalized.match(/\/sign-in\/([^/?]+)$/);
91
+ if (signinMatch && signinMatch[1] !== "email") {
92
+ const provider = signinMatch[1];
93
+ return {
94
+ spanName: `auth.oauth.initiate.${provider}`,
95
+ operation: "oauth_initiate",
96
+ method: "oauth",
97
+ provider
98
+ };
99
+ }
100
+ const segments = normalized.split("/");
101
+ return {
102
+ spanName: `auth.${(segments[segments.length - 1] || "unknown").replace(/-/g, "_")}`,
103
+ operation: "unknown"
104
+ };
105
+ }
106
+ function round(n) {
107
+ return Math.round(n * 100) / 100;
108
+ }
109
+ function extractUserData(returned) {
110
+ const result = {};
111
+ if (!returned || typeof returned !== "object") return result;
112
+ const data = returned.data ?? returned;
113
+ if (data.user?.id) result.userId = String(data.user.id);
114
+ if (data.user?.email) result.userEmail = String(data.user.email);
115
+ if (data.session?.id) result.sessionId = String(data.session.id);
116
+ if (!result.userId && returned.id && returned.email) {
117
+ result.userId = String(returned.id);
118
+ result.userEmail = String(returned.email);
119
+ }
120
+ return result;
121
+ }
122
+ const requestMetas = /* @__PURE__ */ new WeakMap();
123
+ /**
124
+ * Creates a Better Auth plugin that auto-traces all auth operations
125
+ * into silgi analytics spans.
126
+ *
127
+ * @param config - Optional configuration
128
+ * @returns A Better Auth plugin (typed as `any` to avoid requiring better-auth types at build time)
129
+ */
130
+ function tracing(config) {
131
+ const captureInput = config?.captureInput ?? true;
132
+ const captureOutput = config?.captureOutput ?? true;
133
+ return {
134
+ id: "silgi-tracing",
135
+ onRequest: async (request, _ctx) => {
136
+ try {
137
+ const path = new URL(request.url).pathname;
138
+ const match = matchOperation(path);
139
+ requestMetas.set(request, {
140
+ startTime: performance.now(),
141
+ path,
142
+ operation: match.operation,
143
+ method: match.method,
144
+ provider: match.provider,
145
+ spanName: match.spanName
146
+ });
147
+ } catch {}
148
+ },
149
+ hooks: { after: [{
150
+ matcher: () => true,
151
+ handler: (config?.createAuthMiddleware ?? ((fn) => fn))(async (ctx) => {
152
+ try {
153
+ const request = ctx.request;
154
+ if (!request) return;
155
+ const silgiCtx = request.__silgiCtx;
156
+ if (!silgiCtx) return;
157
+ const reqTrace = silgiCtx.__analyticsTrace;
158
+ if (!reqTrace) return;
159
+ const meta = requestMetas.get(request);
160
+ requestMetas.delete(request);
161
+ const path = ctx.path || "";
162
+ const match = meta ? {
163
+ spanName: meta.spanName,
164
+ operation: meta.operation,
165
+ method: meta.method,
166
+ provider: meta.provider
167
+ } : matchOperation(path);
168
+ const startTime = meta?.startTime ?? performance.now();
169
+ const durationMs = round(performance.now() - startTime);
170
+ const returned = ctx.context?.returned;
171
+ const newSession = ctx.context?.newSession;
172
+ const userData = extractUserData(returned);
173
+ if (!userData.userId && newSession?.user?.id) userData.userId = String(newSession.user.id);
174
+ if (!userData.userEmail && newSession?.user?.email) userData.userEmail = String(newSession.user.email);
175
+ if (!userData.sessionId && newSession?.session?.id) userData.sessionId = String(newSession.session.id);
176
+ const success = !returned?.error && !ctx.context?.error;
177
+ const attributes = {
178
+ "auth.operation": match.operation,
179
+ "auth.success": success
180
+ };
181
+ if (match.method) attributes["auth.method"] = match.method;
182
+ if (match.provider) attributes["auth.provider"] = match.provider;
183
+ if (userData.userId) attributes["user.id"] = userData.userId;
184
+ if (userData.userEmail) attributes["user.email"] = userData.userEmail;
185
+ if (userData.sessionId) attributes["session.id"] = userData.sessionId;
186
+ const span = {
187
+ name: match.spanName,
188
+ kind: "http",
189
+ durationMs,
190
+ startOffsetMs: round(startTime - reqTrace.t0),
191
+ attributes
192
+ };
193
+ if (captureInput && ctx.body) span.input = ctx.body;
194
+ if (captureOutput && returned && typeof returned === "object") span.output = returned;
195
+ if (!success && returned?.error) span.error = typeof returned.error === "string" ? returned.error : returned.error?.message ?? "error";
196
+ reqTrace.spans.push(span);
197
+ if (captureInput && ctx.body) reqTrace.procedureInput = ctx.body;
198
+ if (captureOutput && returned && typeof returned === "object") reqTrace.procedureOutput = returned;
199
+ } catch {}
200
+ })
201
+ }] }
202
+ };
203
+ }
204
+ const API_METHOD_METADATA = {
205
+ getSession: { operation: "get_session" },
206
+ signOut: { operation: "signout" },
207
+ signInEmail: {
208
+ operation: "signin",
209
+ method: "email"
210
+ },
211
+ signUpEmail: {
212
+ operation: "signup",
213
+ method: "email"
214
+ },
215
+ signInSocial: {
216
+ operation: "signin",
217
+ method: "oauth"
218
+ },
219
+ callbackOAuth: {
220
+ operation: "oauth_callback",
221
+ method: "oauth"
222
+ },
223
+ linkSocialAccount: {
224
+ operation: "link_social_account",
225
+ method: "oauth"
226
+ },
227
+ unlinkAccount: { operation: "unlink_account" },
228
+ listUserAccounts: { operation: "list_user_accounts" },
229
+ updateUser: { operation: "update_user" },
230
+ deleteUser: { operation: "delete_user" },
231
+ changePassword: { operation: "change_password" },
232
+ setPassword: { operation: "set_password" },
233
+ changeEmail: { operation: "change_email" },
234
+ verifyEmail: { operation: "verify_email" },
235
+ sendVerificationEmail: { operation: "send_verification_email" },
236
+ forgetPassword: { operation: "forget_password" },
237
+ resetPassword: { operation: "reset_password" },
238
+ listSessions: { operation: "list_sessions" },
239
+ revokeSession: { operation: "revoke_session" },
240
+ revokeSessions: { operation: "revoke_sessions" },
241
+ revokeOtherSessions: { operation: "revoke_other_sessions" },
242
+ refreshToken: { operation: "refresh_token" },
243
+ getAccessToken: { operation: "get_access_token" }
244
+ };
245
+ const AUTH_INSTRUMENTED = "__silgiBetterAuthInstrumented";
246
+ /**
247
+ * Instrument a Better Auth instance to trace all `auth.api.*` method calls.
248
+ * Works with `withCtx` — programmatic calls from background jobs,
249
+ * server-side session fetches etc. are traced when context is available.
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * import { instrumentBetterAuth, withCtx } from 'silgi/better-auth'
254
+ *
255
+ * const auth = instrumentBetterAuth(betterAuth({ ... }))
256
+ *
257
+ * // In a silgi procedure:
258
+ * const me = s.$resolve(async ({ ctx }) => {
259
+ * return withCtx(ctx, () => auth.api.getSession({ headers: ctx.headers }))
260
+ * })
261
+ * ```
262
+ */
263
+ function instrumentBetterAuth(auth) {
264
+ if (!auth || auth[AUTH_INSTRUMENTED]) return auth;
265
+ const api = auth.api;
266
+ if (!api || typeof api !== "object") return auth;
267
+ const instrumented = /* @__PURE__ */ new Set();
268
+ for (const [methodName, metadata] of Object.entries(API_METHOD_METADATA)) if (typeof api[methodName] === "function") {
269
+ api[methodName] = wrapApiMethod(api[methodName], metadata.operation, metadata.method);
270
+ instrumented.add(methodName);
271
+ }
272
+ for (const key of Object.keys(api)) if (typeof api[key] === "function" && !instrumented.has(key) && !key.startsWith("$") && !key.startsWith("_")) {
273
+ const operation = key.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
274
+ api[key] = wrapApiMethod(api[key], operation);
275
+ instrumented.add(key);
276
+ }
277
+ auth[AUTH_INSTRUMENTED] = true;
278
+ return auth;
279
+ }
280
+ /**
281
+ * Run a function with silgi context available to instrumented Better Auth API calls.
282
+ */
283
+ function withCtx(ctx, fn) {
284
+ return ctxStorage.run(ctx, fn);
285
+ }
286
+ function wrapApiMethod(originalFn, operation, method) {
287
+ return async function instrumented(...args) {
288
+ const reqTrace = ctxStorage.getStore()?.__analyticsTrace;
289
+ if (!reqTrace) return originalFn.apply(this, args);
290
+ const spanName = `auth.api.${operation}`;
291
+ const start = performance.now();
292
+ const attributes = {
293
+ "auth.operation": operation,
294
+ "auth.success": true
295
+ };
296
+ if (method) attributes["auth.method"] = method;
297
+ try {
298
+ const result = await originalFn.apply(this, args);
299
+ const data = result?.data ?? result;
300
+ if (data?.user?.id) attributes["user.id"] = String(data.user.id);
301
+ if (data?.user?.email) attributes["user.email"] = String(data.user.email);
302
+ if (data?.session?.id) attributes["session.id"] = String(data.session.id);
303
+ if (result?.error) {
304
+ attributes["auth.success"] = false;
305
+ attributes["auth.error"] = typeof result.error === "string" ? result.error : result.error?.message ?? "unknown";
306
+ }
307
+ reqTrace.spans.push({
308
+ name: spanName,
309
+ kind: "http",
310
+ durationMs: round(performance.now() - start),
311
+ startOffsetMs: round(start - reqTrace.t0),
312
+ attributes,
313
+ output: result && typeof result === "object" ? result : void 0
314
+ });
315
+ return result;
316
+ } catch (error) {
317
+ attributes["auth.success"] = false;
318
+ attributes["auth.error"] = error instanceof Error ? error.message : String(error);
319
+ reqTrace.spans.push({
320
+ name: spanName,
321
+ kind: "http",
322
+ durationMs: round(performance.now() - start),
323
+ startOffsetMs: round(start - reqTrace.t0),
324
+ attributes,
325
+ error: error instanceof Error ? error.stack ?? error.message : String(error)
326
+ });
327
+ throw error;
328
+ }
329
+ };
330
+ }
331
+ //#endregion
332
+ export { instrumentBetterAuth, tracing, withCtx };
@@ -0,0 +1,27 @@
1
+ //#region src/integrations/drizzle/index.d.ts
2
+ interface InstrumentDrizzleConfig {
3
+ /** Logical database name (e.g. 'auth', 'ecommerce') */
4
+ dbName?: string;
5
+ /** Database system identifier. Default: 'postgresql' */
6
+ dbSystem?: string;
7
+ /** Capture SQL query text in spans. Default: true */
8
+ captureQueryText?: boolean;
9
+ /** Max query text length before truncation. Default: 1000 */
10
+ maxQueryTextLength?: number;
11
+ /** Database host */
12
+ peerName?: string;
13
+ /** Database port */
14
+ peerPort?: number;
15
+ }
16
+ /**
17
+ * Instrument a Drizzle db instance to record query spans in silgi analytics.
18
+ * Returns the same db instance (mutated). Safe to call multiple times.
19
+ */
20
+ declare function instrumentDrizzle<T extends Record<string, any>>(db: T, config?: InstrumentDrizzleConfig): T;
21
+ /**
22
+ * Run a function with silgi context available to instrumented Drizzle instances.
23
+ * All Drizzle queries inside `fn` will be recorded as trace spans.
24
+ */
25
+ declare function withCtx<T>(ctx: Record<string, unknown>, fn: () => T): T;
26
+ //#endregion
27
+ export { InstrumentDrizzleConfig, instrumentDrizzle, withCtx };