@magemetrics/core 0.1.3 → 0.1.5

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/dist/index.js CHANGED
@@ -305,10 +305,6 @@ var Edge = z.object({
305
305
  source: z.string(),
306
306
  target: z.string()
307
307
  });
308
- z.object({
309
- score: z.number(),
310
- reason: z.string()
311
- });
312
308
  var LLMColumnsLineageResponse = z.record(z.string(), z.object({
313
309
  nodes: z.array(Node3),
314
310
  edges: z.array(Edge)
@@ -995,7 +991,7 @@ var RetrieveRecentFlows = createRoute({
995
991
  }
996
992
  }
997
993
  });
998
- createRoute({
994
+ var GetReportColumns = createRoute({
999
995
  method: "get",
1000
996
  path: "/api/v1/data-reports/{report_id}/columns",
1001
997
  operationId: "getDataReportColumns",
@@ -1118,7 +1114,7 @@ createRoute({
1118
1114
  }
1119
1115
  }
1120
1116
  });
1121
- createRoute({
1117
+ var GetReportData = createRoute({
1122
1118
  method: "get",
1123
1119
  path: "/api/v1/data-reports/{report_id}/data",
1124
1120
  operationId: "getDataReportData",
@@ -1211,7 +1207,7 @@ createRoute({
1211
1207
  }
1212
1208
  }
1213
1209
  });
1214
- createRoute({
1210
+ var GetReportRowCount = createRoute({
1215
1211
  method: "get",
1216
1212
  path: "/api/v1/data-reports/{report_id}/count",
1217
1213
  operationId: "getDataReportRowCount",
@@ -2367,10 +2363,227 @@ var hashString = (value) => {
2367
2363
  const hash = sha256(data);
2368
2364
  return Array.from(hash).map((b) => b.toString(16).padStart(2, "0")).join("");
2369
2365
  };
2366
+ var QuickActionSchema = z.object({
2367
+ label: z.string(),
2368
+ explanation: z.string()
2369
+ });
2370
+ var QuickActionsSchema = QuickActionSchema.array();
2371
+ var FrontendTimelineItemSchema = z.discriminatedUnion("type", [
2372
+ z.object({
2373
+ type: z.literal("dataReport"),
2374
+ flowDataId: z.number(),
2375
+ flowId: z.string(),
2376
+ title: z.string(),
2377
+ bookmarked: z.boolean(),
2378
+ answer: z.string().nullable()
2379
+ }),
2380
+ z.object({
2381
+ type: z.literal("visualization"),
2382
+ flowId: z.string(),
2383
+ flowDataId: z.number(),
2384
+ flowDataVisualizationId: z.number(),
2385
+ title: z.string(),
2386
+ bookmarked: z.boolean()
2387
+ }),
2388
+ z.object({
2389
+ type: z.literal("message")
2390
+ })
2391
+ ]);
2392
+
2393
+ // ../shared/dist/src/endpoints/companion/chat.routes.js
2394
+ createRoute({
2395
+ hide: true,
2396
+ method: "get",
2397
+ path: "/api/v1/chat/{flowId}/timeline",
2398
+ operationId: "retrieveChatTimeline",
2399
+ request: {
2400
+ headers: SupabaseHeaderSchema,
2401
+ params: FlowIdParam
2402
+ },
2403
+ responses: {
2404
+ 200: {
2405
+ description: "The timeline of generated insights for the flow",
2406
+ content: {
2407
+ "application/json": {
2408
+ schema: FrontendTimelineItemSchema.array()
2409
+ }
2410
+ }
2411
+ },
2412
+ 404: {
2413
+ content: {
2414
+ "application/json": {
2415
+ schema: z.object({ error: z.string() })
2416
+ }
2417
+ },
2418
+ description: "Unable to retrieve flow with this id"
2419
+ },
2420
+ 500: {
2421
+ description: "Something wrong happened",
2422
+ content: {
2423
+ "application/json": {
2424
+ schema: z.object({
2425
+ error: z.string()
2426
+ })
2427
+ }
2428
+ }
2429
+ }
2430
+ }
2431
+ });
2432
+ var ChatMessages = createRoute({
2433
+ method: "get",
2434
+ path: "/api/v1/chat/{flowId}/messages",
2435
+ operationId: "retrieveChatMessages",
2436
+ request: {
2437
+ headers: SupabaseHeaderSchema,
2438
+ params: FlowIdParam
2439
+ },
2440
+ responses: {
2441
+ 200: {
2442
+ description: "The messages from the given flow",
2443
+ content: {
2444
+ "application/json": {
2445
+ // we don't want to have to write a parser for Vercel AI messages
2446
+ schema: z.object({ messages: z.object({}).passthrough().array() })
2447
+ }
2448
+ }
2449
+ },
2450
+ 404: {
2451
+ content: {
2452
+ "application/json": {
2453
+ schema: z.object({ error: z.string() })
2454
+ }
2455
+ },
2456
+ description: "Unable to retrieve flow with this id"
2457
+ },
2458
+ 500: {
2459
+ description: "Something wrong happened",
2460
+ content: {
2461
+ "application/json": {
2462
+ schema: z.object({
2463
+ error: z.string()
2464
+ })
2465
+ }
2466
+ }
2467
+ }
2468
+ }
2469
+ });
2470
+ createRoute({
2471
+ hide: true,
2472
+ method: "post",
2473
+ path: "/api/v1/chat/{flowId}/quick-actions",
2474
+ request: {
2475
+ headers: SupabaseHeaderSchema,
2476
+ params: FlowIdParam,
2477
+ body: {
2478
+ content: {
2479
+ "application/json": {
2480
+ schema: z.object({
2481
+ content: z.string()
2482
+ })
2483
+ }
2484
+ }
2485
+ }
2486
+ },
2487
+ responses: {
2488
+ 200: {
2489
+ description: "The quick actions for the given message",
2490
+ content: {
2491
+ "application/json": {
2492
+ schema: QuickActionsSchema
2493
+ }
2494
+ }
2495
+ },
2496
+ 400: {
2497
+ content: {
2498
+ "application/json": {
2499
+ schema: z.object({ error: z.string() })
2500
+ }
2501
+ },
2502
+ description: "The provided content is invalid"
2503
+ },
2504
+ 404: {
2505
+ content: {
2506
+ "application/json": {
2507
+ schema: z.object({ error: z.string() })
2508
+ }
2509
+ },
2510
+ description: "Unable to retrieve flow with this id"
2511
+ },
2512
+ 500: {
2513
+ description: "Something wrong happened",
2514
+ content: {
2515
+ "application/json": {
2516
+ schema: z.object({
2517
+ error: z.string()
2518
+ })
2519
+ }
2520
+ }
2521
+ }
2522
+ }
2523
+ });
2524
+ var DataTableSchema = z.object({
2525
+ id: z.number(),
2526
+ dataset: z.string(),
2527
+ table_name: z.string(),
2528
+ description: z.string().nullable(),
2529
+ primary_key_names: z.string().array(),
2530
+ row_count: z.number().nullable(),
2531
+ created_at: z.string(),
2532
+ updated_at: z.string(),
2533
+ source_id: z.number(),
2534
+ parent_table_id: z.number().nullable()
2535
+ });
2536
+ var DataTableColumnSchema = z.object({
2537
+ id: z.number(),
2538
+ name: z.string(),
2539
+ data_type: z.string().nullable(),
2540
+ description: z.string().nullable(),
2541
+ null_percentage: z.number().nullable(),
2542
+ unique_values: z.number().nullable(),
2543
+ sample_values: z.array(z.unknown()).nullable()
2544
+ });
2545
+ DataTableSchema.extend({
2546
+ columns: z.array(DataTableColumnSchema)
2547
+ });
2548
+ var toSearchParams = ({ cursor, filters, sorting, limit }) => {
2549
+ const params = {};
2550
+ if (limit !== void 0) {
2551
+ params.limit = limit;
2552
+ }
2553
+ if (cursor !== void 0) {
2554
+ params.cursor = cursor;
2555
+ }
2556
+ const sortingParams = sorting?.map((c) => `${c.id}:${c.desc ? "DESC" : "ASC"}`).join(",");
2557
+ if (sortingParams) {
2558
+ params.order = sortingParams;
2559
+ }
2560
+ if (filters) {
2561
+ const filterParams = Object.entries(filters).flatMap(([key, value]) => {
2562
+ if (!(typeof value === "string" || typeof value === "boolean" || typeof value === "number"))
2563
+ return [];
2564
+ return `${key}:${encodeURIComponent(value)}`;
2565
+ }).join(",");
2566
+ if (filterParams.length > 0) {
2567
+ params.filter = filterParams;
2568
+ }
2569
+ }
2570
+ return params;
2571
+ };
2370
2572
 
2371
2573
  // package.json
2372
2574
  var package_default = {
2373
- version: "0.1.3"};
2575
+ version: "0.1.5"};
2576
+ var MageMetricsChatTransport = class extends DefaultChatTransport {
2577
+ constructor(apiUrl, flowId, options) {
2578
+ super({
2579
+ ...options,
2580
+ api: `${apiUrl}/api/v1/chat/${flowId}`,
2581
+ prepareSendMessagesRequest({ messages, id: chatId }) {
2582
+ return { body: { message: messages.at(-1), id: chatId } };
2583
+ }
2584
+ });
2585
+ }
2586
+ };
2374
2587
 
2375
2588
  // src/core/MageMetricsEventEmitter.ts
2376
2589
  var MageMetricsEventEmitter = class {
@@ -2414,26 +2627,20 @@ var MageMetricsEventEmitter = class {
2414
2627
  }
2415
2628
  };
2416
2629
 
2630
+ // src/core/resolvable.ts
2631
+ var resolve = (value) => {
2632
+ if (typeof value === "function") {
2633
+ return Promise.resolve(value());
2634
+ }
2635
+ return Promise.resolve(value);
2636
+ };
2637
+
2417
2638
  // src/core/types.ts
2418
2639
  var TOKEN_STORAGE_KEY = "mm-ai-sp-token";
2419
2640
  var CHECK_KEY = "mm-ai-check-key";
2420
2641
 
2421
2642
  // src/core/MageMetricsClient.ts
2422
2643
  var MM_CLIENT_VERSION = package_default.version;
2423
- var MageMetricsChatTransport = class extends DefaultChatTransport {
2424
- constructor(apiUrl, flowId, options) {
2425
- super({
2426
- ...options,
2427
- headers: {
2428
- [HEADER_CLIENT_VERSION]: MM_CLIENT_VERSION
2429
- },
2430
- api: `${apiUrl}/api/v1/chat/${flowId}`,
2431
- prepareSendMessagesRequest({ messages, id: chatId }) {
2432
- return { body: { message: messages.at(-1), id: chatId } };
2433
- }
2434
- });
2435
- }
2436
- };
2437
2644
  var getPublicApiClient = (apiUrl, apiKey) => {
2438
2645
  const client = createApiClient({ baseUrl: apiUrl });
2439
2646
  client.use(throwOnError);
@@ -2445,17 +2652,29 @@ var MageMetricsClient = class {
2445
2652
  config;
2446
2653
  authState = "initializing";
2447
2654
  supabaseClient = null;
2448
- internalApiClient = null;
2655
+ internalApiClient;
2449
2656
  noAuthApiClient = null;
2450
2657
  authApiResponse = null;
2451
2658
  authPromise = null;
2452
2659
  processedJwt = Symbol("initial");
2660
+ userId = null;
2453
2661
  storageAdapter;
2454
2662
  events = new MageMetricsEventEmitter();
2455
- constructor(config) {
2663
+ authOptions;
2664
+ constructor(config, auth) {
2456
2665
  this.config = config;
2457
2666
  this.storageAdapter = config.storageAdapter || createStorageAdapter();
2458
- void this.initializeAuth();
2667
+ this.internalApiClient = createApiClient({
2668
+ baseUrl: this.config.apiUrl
2669
+ });
2670
+ this.internalApiClient.use(throwOnError);
2671
+ this.internalApiClient.use(this.createSupabaseHeaderMiddleware());
2672
+ this.authOptions = auth || {};
2673
+ }
2674
+ toJSON() {
2675
+ return {
2676
+ config: this.encodeCheckKey(this.config.apiKey, this.config.apiUrl)
2677
+ };
2459
2678
  }
2460
2679
  /**
2461
2680
  * Add an event listener for authentication state changes
@@ -2482,10 +2701,10 @@ var MageMetricsClient = class {
2482
2701
  if (this.authState === "error") {
2483
2702
  throw new Error("Authentication failed");
2484
2703
  }
2485
- if (this.authPromise) {
2486
- return this.authPromise;
2704
+ if (!this.authPromise) {
2705
+ this.authPromise = this.performAuthFlow();
2487
2706
  }
2488
- throw new Error("Authentication not initialized");
2707
+ return this.authPromise;
2489
2708
  }
2490
2709
  async updateExternalJwt(jwt) {
2491
2710
  if (this.config.externalJwt === jwt) return;
@@ -2496,7 +2715,7 @@ var MageMetricsClient = class {
2496
2715
  get state() {
2497
2716
  return this.authState;
2498
2717
  }
2499
- async getAuthHeaders() {
2718
+ async getHeaders() {
2500
2719
  await this.waitForAuth();
2501
2720
  if (!this.supabaseClient) {
2502
2721
  return {};
@@ -2506,11 +2725,10 @@ var MageMetricsClient = class {
2506
2725
  return {};
2507
2726
  }
2508
2727
  const { access_token } = session.data.session;
2509
- const headers = {};
2510
- if (access_token) {
2511
- headers["sp-access-token"] = access_token;
2512
- }
2513
- return headers;
2728
+ return {
2729
+ [HEADER_CLIENT_VERSION]: MM_CLIENT_VERSION,
2730
+ "sp-access-token": access_token
2731
+ };
2514
2732
  }
2515
2733
  async clearStorage() {
2516
2734
  try {
@@ -2525,13 +2743,16 @@ var MageMetricsClient = class {
2525
2743
  }
2526
2744
  await this.clearStorage();
2527
2745
  }
2746
+ auth = {
2747
+ startAutoRefresh: () => this.supabaseClient?.startAutoRefresh(),
2748
+ stopAuthRefresh: () => this.supabaseClient?.startAutoRefresh()
2749
+ };
2528
2750
  /**
2529
2751
  * Public API methods that automatically wait for authentication
2530
2752
  */
2531
2753
  api = {
2532
2754
  getRecentFlows: async (params = {}) => {
2533
2755
  await this.waitForAuth();
2534
- if (!this.internalApiClient) throw new Error("API client not ready");
2535
2756
  const { data, error, response } = await this.internalApiClient.GET(
2536
2757
  RetrieveRecentFlows.path,
2537
2758
  {
@@ -2554,7 +2775,6 @@ var MageMetricsClient = class {
2554
2775
  },
2555
2776
  createFlow: async (request) => {
2556
2777
  await this.waitForAuth();
2557
- if (!this.internalApiClient) throw new Error("API client not ready");
2558
2778
  if ("query" in request) {
2559
2779
  const { data, error, response } = await this.internalApiClient.POST(
2560
2780
  CreateFlow.path,
@@ -2601,7 +2821,6 @@ var MageMetricsClient = class {
2601
2821
  },
2602
2822
  getDashboard: async (dashboardId) => {
2603
2823
  await this.waitForAuth();
2604
- if (!this.internalApiClient) throw new Error("API client not ready");
2605
2824
  const { data, error, response } = await this.internalApiClient.GET(
2606
2825
  RetrieveDashboard.path,
2607
2826
  {
@@ -2624,7 +2843,6 @@ var MageMetricsClient = class {
2624
2843
  },
2625
2844
  exportReportData: async (reportId, format = "blob") => {
2626
2845
  await this.waitForAuth();
2627
- if (!this.internalApiClient) throw new Error("API client not ready");
2628
2846
  const { data, error, response } = await this.internalApiClient.GET(
2629
2847
  ExportReportData.path,
2630
2848
  {
@@ -2650,7 +2868,6 @@ var MageMetricsClient = class {
2650
2868
  },
2651
2869
  generateContextualInsight: async (payload) => {
2652
2870
  await this.waitForAuth();
2653
- if (!this.internalApiClient) throw new Error("API client not ready");
2654
2871
  const { data, error, response } = await this.internalApiClient.POST(
2655
2872
  GenerateInsight.path,
2656
2873
  {
@@ -2669,19 +2886,105 @@ var MageMetricsClient = class {
2669
2886
  });
2670
2887
  }
2671
2888
  return data;
2889
+ },
2890
+ getChatTransport: (flowId, options) => {
2891
+ return new MageMetricsChatTransport(this.config.apiUrl, flowId, {
2892
+ ...options,
2893
+ headers: async () => {
2894
+ const authHeaders = await this.getHeaders();
2895
+ const headers = await resolve(options?.headers);
2896
+ return {
2897
+ ...headers,
2898
+ ...authHeaders
2899
+ };
2900
+ }
2901
+ });
2902
+ },
2903
+ getChatMessages: async (flowId) => {
2904
+ const { data, error, response } = await this.internalApiClient.GET(
2905
+ ChatMessages.path,
2906
+ {
2907
+ params: {
2908
+ path: {
2909
+ flowId
2910
+ }
2911
+ }
2912
+ }
2913
+ );
2914
+ if (error) {
2915
+ throw new ApiError(error.error, response, {
2916
+ status: response.status,
2917
+ statusText: response.statusText,
2918
+ url: response.url,
2919
+ method: "GET"
2920
+ });
2921
+ }
2922
+ return data;
2923
+ },
2924
+ fetchReportData: async (reportId, { columns, limit, sorting, filters, cursor }) => {
2925
+ const params = toSearchParams({ limit, sorting, filters, cursor });
2926
+ const { data, error } = await this.internalApiClient.GET(
2927
+ GetReportData.path,
2928
+ {
2929
+ params: {
2930
+ path: {
2931
+ report_id: reportId.toString()
2932
+ },
2933
+ query: {
2934
+ columns: columns?.join(","),
2935
+ limit: params.limit,
2936
+ order: params.order,
2937
+ cursor: params.cursor,
2938
+ filter: params.filter
2939
+ }
2940
+ }
2941
+ }
2942
+ );
2943
+ if (error) {
2944
+ console.error(
2945
+ `Failed to fetch data for report ${reportId}: ${error.error}`
2946
+ );
2947
+ throw new Error(`Failed to fetch data for flow data ${reportId}`);
2948
+ }
2949
+ return data;
2950
+ },
2951
+ getReport: async (reportId) => {
2952
+ const params = { params: { path: { report_id: String(reportId) } } };
2953
+ const columnsPromise = this.internalApiClient.GET(
2954
+ GetReportColumns.path,
2955
+ params
2956
+ );
2957
+ const rowCountPromise = this.internalApiClient.GET(
2958
+ GetReportRowCount.path,
2959
+ params
2960
+ );
2961
+ const [
2962
+ { data: columns, error: columnsError, response: columnsResponse },
2963
+ { data: rowCount, error: rowCountError, response: rowCountResponse }
2964
+ ] = await Promise.all([columnsPromise, rowCountPromise]);
2965
+ if (columnsError) {
2966
+ throw new ApiError(columnsError.error, columnsResponse, {
2967
+ status: columnsResponse.status,
2968
+ statusText: columnsResponse.statusText,
2969
+ url: columnsResponse.url,
2970
+ method: "GET"
2971
+ });
2972
+ }
2973
+ if (rowCountError) {
2974
+ throw new ApiError(rowCountError.error, rowCountResponse, {
2975
+ status: rowCountResponse.status,
2976
+ statusText: rowCountResponse.statusText,
2977
+ url: rowCountResponse.url,
2978
+ method: "GET"
2979
+ });
2980
+ }
2981
+ return {
2982
+ columns,
2983
+ rowCount,
2984
+ data: this.api.fetchReportData
2985
+ };
2672
2986
  }
2673
2987
  };
2674
- /**
2675
- * Initialize authentication flow
2676
- */
2677
- async initializeAuth() {
2678
- this.authPromise = this.performAuthFlow();
2679
- try {
2680
- await this.authPromise;
2681
- } catch (error) {
2682
- console.error("Failed to initialize authentication:", error);
2683
- }
2684
- }
2685
2988
  /**
2686
2989
  * Perform the complete authentication flow
2687
2990
  */
@@ -2690,7 +2993,6 @@ var MageMetricsClient = class {
2690
2993
  this.setState("initializing");
2691
2994
  const apiInfo = await this.getApiInformation();
2692
2995
  this.initializeSupabaseClient(apiInfo.anonKey, apiInfo.apiUrl);
2693
- this.initializeApiClient();
2694
2996
  await this.handleAuthentication();
2695
2997
  if (this.config.externalJwt) {
2696
2998
  await this.saveCheckKey(this.config.externalJwt, this.config.apiKey);
@@ -2729,10 +3031,18 @@ var MageMetricsClient = class {
2729
3031
  storageKey: TOKEN_STORAGE_KEY,
2730
3032
  headers: {
2731
3033
  apiKey: anonKey
2732
- }
3034
+ },
3035
+ storage: this.storageAdapter,
3036
+ ...this.authOptions
2733
3037
  });
2734
3038
  this.supabaseClient.onAuthStateChange((event, session) => {
2735
3039
  console.debug("Supabase auth state change:", event, !!session);
3040
+ if (event === "SIGNED_IN" && session?.user) {
3041
+ if (this.userId !== session.user.id && this.userId !== null) {
3042
+ this.events.dispatch("userChange", { id: session.user.id });
3043
+ }
3044
+ this.userId = session.user.id;
3045
+ }
2736
3046
  if (event === "TOKEN_REFRESHED" && session) {
2737
3047
  console.debug("Token refreshed successfully");
2738
3048
  this.setState("ready");
@@ -2744,27 +3054,8 @@ var MageMetricsClient = class {
2744
3054
  });
2745
3055
  }
2746
3056
  }
2747
- /* This method always waits for authentication to complete before returning the client */
2748
- async client() {
2749
- await this.waitForAuth();
2750
- if (this.authState === "ready") {
2751
- if (!this.internalApiClient) {
2752
- throw new Error(
2753
- "Client is ready but API client not available. This should never happen"
2754
- );
2755
- }
2756
- return this.internalApiClient;
2757
- }
2758
- throw new Error(`Client is not available in auth state: ${this.authState}`);
2759
- }
2760
- /**
2761
- * Initialize the API client with Supabase headers
2762
- */
2763
- initializeApiClient() {
2764
- this.internalApiClient = createApiClient({ baseUrl: this.config.apiUrl });
2765
- this.internalApiClient.use(throwOnError);
2766
- this.internalApiClient.use(this.createSupabaseHeaderMiddleware());
2767
- this.internalApiClient.use(addVersionHeader(MM_CLIENT_VERSION));
3057
+ client() {
3058
+ return this.internalApiClient;
2768
3059
  }
2769
3060
  /**
2770
3061
  * Handle the authentication logic (check session or exchange token)
@@ -2834,19 +3125,18 @@ var MageMetricsClient = class {
2834
3125
  console.debug("Token exchange successful, session set");
2835
3126
  }
2836
3127
  createSupabaseHeaderMiddleware() {
2837
- const supabaseClient = this.supabaseClient;
2838
3128
  return {
2839
- async onRequest({ request }) {
2840
- if (!supabaseClient) {
3129
+ onRequest: async ({ request }) => {
3130
+ const headers = await this.getHeaders();
3131
+ if (!this.supabaseClient) {
2841
3132
  return request;
2842
3133
  }
2843
- const session = await supabaseClient.getSession();
3134
+ const session = await this.supabaseClient.getSession();
2844
3135
  if (!session.data.session) {
2845
3136
  return request;
2846
3137
  }
2847
- const { access_token } = session.data.session;
2848
- if (access_token) {
2849
- request.headers.set("sp-access-token", access_token);
3138
+ for (const [key, value] of Object.entries(headers)) {
3139
+ request.headers.set(key, value);
2850
3140
  }
2851
3141
  return request;
2852
3142
  }
@@ -2879,6 +3169,6 @@ var MageMetricsClient = class {
2879
3169
  }
2880
3170
  };
2881
3171
 
2882
- export { BrowserStorageAdapter, CHECK_KEY, MageMetricsChatTransport, MageMetricsClient, MageMetricsEventEmitter, MemoryStorageAdapter, TOKEN_STORAGE_KEY, getPublicApiClient };
3172
+ export { BrowserStorageAdapter, CHECK_KEY, MageMetricsClient, MageMetricsEventEmitter, MemoryStorageAdapter, TOKEN_STORAGE_KEY, getPublicApiClient };
2883
3173
  //# sourceMappingURL=index.js.map
2884
3174
  //# sourceMappingURL=index.js.map