@teleporthq/teleport-plugin-next-data-source 0.42.35 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/ecommerce-product-out-of-stock.test.ts +112 -0
- package/__tests__/fetchers.test.ts +0 -42
- package/__tests__/filter-utils.test.ts +149 -0
- package/__tests__/mocks.ts +0 -12
- package/__tests__/utils.test.ts +0 -2
- package/dist/cjs/array-mapper-registry.d.ts +2 -0
- package/dist/cjs/array-mapper-registry.d.ts.map +1 -1
- package/dist/cjs/array-mapper-registry.js +9 -1
- package/dist/cjs/array-mapper-registry.js.map +1 -1
- package/dist/cjs/count-fetchers.d.ts +2 -2
- package/dist/cjs/count-fetchers.d.ts.map +1 -1
- package/dist/cjs/count-fetchers.js +5 -5
- package/dist/cjs/count-fetchers.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts +2 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +11 -9
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
- package/dist/cjs/fetchers/airtable.js +1 -1
- package/dist/cjs/fetchers/airtable.js.map +1 -1
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/cjs/fetchers/clickhouse.js +1 -1
- package/dist/cjs/fetchers/clickhouse.js.map +1 -1
- package/dist/cjs/fetchers/csv-file.js +1 -1
- package/dist/cjs/fetchers/csv-file.js.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/google-sheets.js +1 -1
- package/dist/cjs/fetchers/google-sheets.js.map +1 -1
- package/dist/cjs/fetchers/index.d.ts +2 -1
- package/dist/cjs/fetchers/index.d.ts.map +1 -1
- package/dist/cjs/fetchers/index.js +8 -5
- package/dist/cjs/fetchers/index.js.map +1 -1
- package/dist/cjs/fetchers/javascript.js +1 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +3 -3
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +1 -1
- package/dist/cjs/fetchers/mongodb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +2 -2
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +2 -2
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +18 -0
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -0
- package/dist/cjs/fetchers/raw-query.js +70 -0
- package/dist/cjs/fetchers/raw-query.js.map +1 -0
- package/dist/cjs/fetchers/redis.js +1 -1
- package/dist/cjs/fetchers/redis.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +2 -2
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +1 -1
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
- package/dist/cjs/fetchers/supabase.js +62 -2
- package/dist/cjs/fetchers/supabase.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts +7 -0
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -0
- package/dist/cjs/fetchers/teleport.js +63 -0
- package/dist/cjs/fetchers/teleport.js.map +1 -0
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +1 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/filter-utils.d.ts +13 -0
- package/dist/cjs/filter-utils.d.ts.map +1 -0
- package/dist/cjs/filter-utils.js +95 -0
- package/dist/cjs/filter-utils.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +112 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +389 -128
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/sort-utils.d.ts +10 -0
- package/dist/cjs/sort-utils.d.ts.map +1 -0
- package/dist/cjs/sort-utils.js +141 -0
- package/dist/cjs/sort-utils.js.map +1 -0
- package/dist/cjs/transformations/blog-post.d.ts +7 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-post.js +13 -0
- package/dist/cjs/transformations/blog-post.js.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts +7 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.js +13 -0
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +26 -0
- package/dist/cjs/transformations/index.d.ts.map +1 -0
- package/dist/cjs/transformations/index.js +81 -0
- package/dist/cjs/transformations/index.js.map +1 -0
- package/dist/cjs/transformations/shared-utils.d.ts +7 -0
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -0
- package/dist/cjs/transformations/shared-utils.js +13 -0
- package/dist/cjs/transformations/shared-utils.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +30 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +173 -10
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-mapper-registry.d.ts +2 -0
- package/dist/esm/array-mapper-registry.d.ts.map +1 -1
- package/dist/esm/array-mapper-registry.js +9 -1
- package/dist/esm/array-mapper-registry.js.map +1 -1
- package/dist/esm/count-fetchers.d.ts +2 -2
- package/dist/esm/count-fetchers.d.ts.map +1 -1
- package/dist/esm/count-fetchers.js +4 -4
- package/dist/esm/count-fetchers.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts +2 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +10 -9
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/airtable.d.ts.map +1 -1
- package/dist/esm/fetchers/airtable.js +1 -1
- package/dist/esm/fetchers/airtable.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +1 -1
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/csv-file.js +1 -1
- package/dist/esm/fetchers/csv-file.js.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/google-sheets.js +1 -1
- package/dist/esm/fetchers/google-sheets.js.map +1 -1
- package/dist/esm/fetchers/index.d.ts +2 -1
- package/dist/esm/fetchers/index.d.ts.map +1 -1
- package/dist/esm/fetchers/index.js +2 -1
- package/dist/esm/fetchers/index.js.map +1 -1
- package/dist/esm/fetchers/javascript.js +1 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +4 -4
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.js +1 -1
- package/dist/esm/fetchers/mongodb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +3 -3
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +3 -3
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +18 -0
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -0
- package/dist/esm/fetchers/raw-query.js +65 -0
- package/dist/esm/fetchers/raw-query.js.map +1 -0
- package/dist/esm/fetchers/redis.js +1 -1
- package/dist/esm/fetchers/redis.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +3 -3
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.js +1 -1
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/supabase.d.ts.map +1 -1
- package/dist/esm/fetchers/supabase.js +63 -3
- package/dist/esm/fetchers/supabase.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts +7 -0
- package/dist/esm/fetchers/teleport.d.ts.map +1 -0
- package/dist/esm/fetchers/teleport.js +57 -0
- package/dist/esm/fetchers/teleport.js.map +1 -0
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +2 -2
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/filter-utils.d.ts +13 -0
- package/dist/esm/filter-utils.d.ts.map +1 -0
- package/dist/esm/filter-utils.js +66 -0
- package/dist/esm/filter-utils.js.map +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +113 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +389 -128
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/sort-utils.d.ts +10 -0
- package/dist/esm/sort-utils.d.ts.map +1 -0
- package/dist/esm/sort-utils.js +113 -0
- package/dist/esm/sort-utils.js.map +1 -0
- package/dist/esm/transformations/blog-post.d.ts +7 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -0
- package/dist/esm/transformations/blog-post.js +9 -0
- package/dist/esm/transformations/blog-post.js.map +1 -0
- package/dist/esm/transformations/ecommerce-product.d.ts +7 -0
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -0
- package/dist/esm/transformations/ecommerce-product.js +9 -0
- package/dist/esm/transformations/ecommerce-product.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +26 -0
- package/dist/esm/transformations/index.d.ts.map +1 -0
- package/dist/esm/transformations/index.js +74 -0
- package/dist/esm/transformations/index.js.map +1 -0
- package/dist/esm/transformations/shared-utils.d.ts +7 -0
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -0
- package/dist/esm/transformations/shared-utils.js +9 -0
- package/dist/esm/transformations/shared-utils.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +30 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +170 -9
- package/dist/esm/utils.js.map +1 -1
- package/package.json +6 -5
- package/src/array-mapper-registry.ts +13 -0
- package/src/count-fetchers.ts +5 -5
- package/src/data-source-fetchers.ts +15 -11
- package/src/fetchers/airtable.ts +54 -8
- package/src/fetchers/clickhouse.ts +25 -19
- package/src/fetchers/csv-file.ts +2 -2
- package/src/fetchers/firestore.ts +2 -2
- package/src/fetchers/google-sheets.ts +2 -2
- package/src/fetchers/index.ts +6 -5
- package/src/fetchers/javascript.ts +2 -2
- package/src/fetchers/mariadb.ts +27 -12
- package/src/fetchers/mongodb.ts +2 -2
- package/src/fetchers/mysql.ts +27 -12
- package/src/fetchers/postgresql.ts +31 -18
- package/src/fetchers/raw-query.ts +178 -0
- package/src/fetchers/redis.ts +2 -2
- package/src/fetchers/redshift.ts +14 -10
- package/src/fetchers/rest-api.ts +2 -2
- package/src/fetchers/supabase.ts +97 -14
- package/src/fetchers/teleport.ts +485 -0
- package/src/fetchers/turso.ts +15 -7
- package/src/filter-utils.ts +111 -0
- package/src/index.ts +146 -6
- package/src/pagination-plugin.ts +547 -308
- package/src/sort-utils.ts +150 -0
- package/src/transformations/blog-post.ts +128 -0
- package/src/transformations/ecommerce-product.ts +173 -0
- package/src/transformations/index.ts +97 -0
- package/src/transformations/shared-utils.ts +271 -0
- package/src/utils.ts +227 -11
- package/dist/cjs/fetchers/static-collection.d.ts +0 -7
- package/dist/cjs/fetchers/static-collection.d.ts.map +0 -1
- package/dist/cjs/fetchers/static-collection.js +0 -25
- package/dist/cjs/fetchers/static-collection.js.map +0 -1
- package/dist/esm/fetchers/static-collection.d.ts +0 -7
- package/dist/esm/fetchers/static-collection.d.ts.map +0 -1
- package/dist/esm/fetchers/static-collection.js +0 -19
- package/dist/esm/fetchers/static-collection.js.map +0 -1
- package/src/fetchers/static-collection.ts +0 -231
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { replaceSecretReference, generateDateFormatterCode, generateSafeJSONParseCode, } from '../utils';
|
|
1
|
+
import { replaceSecretReference, generateDateFormatterCode, generateSafeJSONParseCode, generateSearchEscapeHelpersCode, } from '../utils';
|
|
2
2
|
export var validateSupabaseConfig = function (config) {
|
|
3
3
|
if (!config || typeof config !== 'object') {
|
|
4
4
|
return { isValid: false, error: 'Config must be a valid object' };
|
|
@@ -27,10 +27,70 @@ export var generateSupabaseFetcher = function (config, tableName) {
|
|
|
27
27
|
var supabaseConfig = config;
|
|
28
28
|
var supabaseUrl = supabaseConfig.supabaseUrl;
|
|
29
29
|
var apiKey = supabaseConfig.serviceRoleKey || supabaseConfig.publicApiKey;
|
|
30
|
-
|
|
30
|
+
/*
|
|
31
|
+
DO $$
|
|
32
|
+
DECLARE
|
|
33
|
+
first_account_id UUID;
|
|
34
|
+
first_team_id UUID;
|
|
35
|
+
BEGIN
|
|
36
|
+
-- Step 1: Select the first account's id
|
|
37
|
+
SELECT id INTO first_account_id
|
|
38
|
+
FROM accounts
|
|
39
|
+
ORDER BY id
|
|
40
|
+
LIMIT 1;
|
|
41
|
+
|
|
42
|
+
-- Step 2: Insert into subscriptions
|
|
43
|
+
INSERT INTO subscriptions (
|
|
44
|
+
"stripeSubscriptionId",
|
|
45
|
+
"accountId",
|
|
46
|
+
"stripeCurrentPeriodStart",
|
|
47
|
+
"stripeCurrentPeriodEnd",
|
|
48
|
+
"createdAt",
|
|
49
|
+
"updatedAt",
|
|
50
|
+
"percentOff"
|
|
51
|
+
) VALUES (
|
|
52
|
+
'abc',
|
|
53
|
+
first_account_id,
|
|
54
|
+
'2025-08-13 00:00:00+00'::timestamptz,
|
|
55
|
+
'2035-09-13 00:00:00+00'::timestamptz,
|
|
56
|
+
'2025-08-13 00:00:00+00'::timestamptz,
|
|
57
|
+
'2025-08-13 00:00:00+00'::timestamptz,
|
|
58
|
+
100
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
-- Step 3: Insert into subscription-items
|
|
62
|
+
INSERT INTO "subscription-items" (
|
|
63
|
+
"stripeSubscriptionItemId",
|
|
64
|
+
"stripePriceId",
|
|
65
|
+
"stripeSubscriptionId",
|
|
66
|
+
"createdAt",
|
|
67
|
+
"updatedAt"
|
|
68
|
+
) VALUES (
|
|
69
|
+
'abc',
|
|
70
|
+
'price_1KO3BuGd5V11xo4EFF8fKiVR',
|
|
71
|
+
'abc',
|
|
72
|
+
'2025-08-13 00:00:00+00'::timestamptz,
|
|
73
|
+
'2025-08-13 00:00:00+00'::timestamptz
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
-- Step 4: Get first team id and update its subscriptionItemId
|
|
77
|
+
SELECT id INTO first_team_id
|
|
78
|
+
FROM teams
|
|
79
|
+
ORDER BY id
|
|
80
|
+
LIMIT 1;
|
|
81
|
+
|
|
82
|
+
UPDATE teams
|
|
83
|
+
SET "subscriptionItemId" = 'abc'
|
|
84
|
+
WHERE id = first_team_id;
|
|
85
|
+
|
|
86
|
+
RAISE NOTICE 'Script completed successfully. Account ID: %, Team ID: %', first_account_id, first_team_id;
|
|
87
|
+
END $$;
|
|
88
|
+
|
|
89
|
+
*/
|
|
90
|
+
return "import { createClient } from '@supabase/supabase-js'\n\nlet client = null\n\nconst getClient = () => {\n if (client) return client\n \n client = createClient(\n ".concat(JSON.stringify(supabaseUrl), ",\n ").concat(replaceSecretReference(apiKey), "\n )\n \n return client\n}\n\n").concat(generateSafeJSONParseCode(), "\n\n").concat(generateSearchEscapeHelpersCode(), "\n\n// Helper function to process filter values\nconst processFilterValue = (value) => {\n if (typeof value === 'string' && !isNaN(Number(value))) {\n return Number(value)\n }\n return value\n}\n\n// Helper function to apply filters to a query\nconst applyFilters = (queryRef, filters) => {\n if (!filters) return queryRef\n \n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n parsedFilters.forEach((filter) => {\n if (!filter.source || filter.destination === undefined) return\n \n const field = filter.source\n const value = filter.destination\n const operand = filter.operand || '='\n \n if (Array.isArray(value)) {\n const processedValues = value.map(processFilterValue)\n if (operand === '!=') {\n queryRef = queryRef.not(field, 'in', processedValues)\n } else {\n queryRef = queryRef.in(field, processedValues)\n }\n } else {\n const processedValue = processFilterValue(value)\n \n // Handle null values\n if (processedValue === null) {\n if (operand === '=') {\n queryRef = queryRef.is(field, null)\n } else if (operand === '!=') {\n queryRef = queryRef.not(field, 'is', null)\n }\n } else {\n // Map operand to Supabase methods\n switch (operand) {\n case '=':\n queryRef = queryRef.eq(field, processedValue)\n break\n case '!=':\n queryRef = queryRef.neq(field, processedValue)\n break\n case '>':\n queryRef = queryRef.gt(field, processedValue)\n break\n case '>=':\n queryRef = queryRef.gte(field, processedValue)\n break\n case '<':\n queryRef = queryRef.lt(field, processedValue)\n break\n case '<=':\n queryRef = queryRef.lte(field, processedValue)\n break\n default:\n queryRef = queryRef.eq(field, processedValue)\n }\n }\n }\n })\n } else {\n // Old format: object with key-value pairs (backward compatibility)\n Object.entries(parsedFilters).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n const processedValues = value.map(processFilterValue)\n queryRef = queryRef.in(key, processedValues)\n } else if (typeof value === 'object' && value !== null) {\n const operator = Object.keys(value)[0]\n let operatorValue = value[operator]\n if (typeof operatorValue === 'string' && !isNaN(Number(operatorValue))) {\n operatorValue = Number(operatorValue)\n }\n switch (operator) {\n case 'eq': queryRef = queryRef.eq(key, operatorValue); break\n case 'neq': queryRef = queryRef.neq(key, operatorValue); break\n case 'gt': queryRef = queryRef.gt(key, operatorValue); break\n case 'gte': queryRef = queryRef.gte(key, operatorValue); break\n case 'lt': queryRef = queryRef.lt(key, operatorValue); break\n case 'lte': queryRef = queryRef.lte(key, operatorValue); break\n case 'like': queryRef = queryRef.like(key, operatorValue); break\n case 'ilike': queryRef = queryRef.ilike(key, operatorValue); break\n case 'in': queryRef = queryRef.in(key, operatorValue); break\n default: queryRef = queryRef.eq(key, operatorValue)\n }\n } else {\n let processedValue = value\n if (typeof value === 'string' && !isNaN(Number(value))) {\n processedValue = Number(value)\n }\n queryRef = queryRef.eq(key, processedValue)\n }\n })\n }\n \n return queryRef\n}\n\n").concat(generateDateFormatterCode(), "\n\nexport default async function handler(req, res) {\n try {\n const client = getClient()\n const { query, queryColumns, select, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n let queryRef = client.from('").concat(tableName, "').select(select || '*')\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await client.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n // PostgREST .or() DSL separates conditions with comma and uses\n // dot to split field / operator / value, so we wrap the pattern\n // in double quotes and backslash-escape any embedded quotes or\n // backslashes in the user's search term. Columns go through the\n // shared identifier sanitizer to reject injection. Note:\n // PostgREST .ilike. does not expose a LIKE ESCAPE clause, so\n // raw % / _ in the user input still act as wildcards in this\n // backend; the canvas preview path escapes them explicitly.\n const rawPattern = \"%\" + String(query) + \"%\"\n const escapedForPostgrest = rawPattern\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n const searchPattern = '\"' + escapedForPostgrest + '\"'\n const orConditions = columns\n .map((col) => sanitizeSearchIdentifier(col) + \".ilike.\" + searchPattern)\n .join(\",\")\n queryRef = queryRef.or(orConditions)\n }\n }\n\n // Apply filters using helper function\n queryRef = applyFilters(queryRef, filters)\n \n // Handle sorts - new array format\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts)) {\n parsedSorts.forEach((sort) => {\n if (sort.field) {\n queryRef = queryRef.order(sort.field, {\n ascending: !(sort.order || '').toLowerCase().startsWith('desc')\n })\n }\n })\n }\n } else if (sortBy) {\n queryRef = queryRef.order(sortBy, { ascending: !(sortOrder || '').toLowerCase().startsWith('desc') })\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (offsetValue !== undefined && limitValue) {\n queryRef = queryRef.range(offsetValue, offsetValue + parseInt(limitValue) - 1)\n } else if (limitValue) {\n queryRef = queryRef.limit(parseInt(limitValue))\n }\n \n const { data, error } = await queryRef\n \n if (error) {\n return res.status(500).json({\n success: false,\n error: error.message,\n timestamp: Date.now()\n })\n }\n \n const safeData = JSON.parse(JSON.stringify(data, dateReplacer))\n \n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Supabase fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n }\n}\n");
|
|
31
91
|
};
|
|
32
92
|
// tslint:disable-next-line:variable-name
|
|
33
93
|
export var generateSupabaseCountFetcher = function (_config, tableName) {
|
|
34
|
-
return "\nasync function getCount(req, res) {\n const supabase = getClient()\n\n try {\n const { query, queryColumns, filters } = req.query\n let countQuery = supabase.from('".concat(tableName, "').select('*', { count: 'exact', head: true })\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await supabase.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n
|
|
94
|
+
return "\nasync function getCount(req, res) {\n const supabase = getClient()\n\n try {\n const { query, queryColumns, filters } = req.query\n let countQuery = supabase.from('".concat(tableName, "').select('*', { count: 'exact', head: true })\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await supabase.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n // Mirror the fetch handler: sanitize identifiers, wrap the\n // pattern in double quotes for PostgREST .or() DSL, and escape\n // backslashes / quotes in the user's search term.\n const rawPattern = \"%\" + String(query) + \"%\"\n const escapedForPostgrest = rawPattern\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n const searchPattern = '\"' + escapedForPostgrest + '\"'\n const orConditions = columns\n .map((col) => sanitizeSearchIdentifier(col) + \".ilike.\" + searchPattern)\n .join(\",\")\n countQuery = countQuery.or(orConditions)\n }\n }\n\n // Apply filters using helper function\n countQuery = applyFilters(countQuery, filters)\n\n const { count, error } = await countQuery\n \n if (error) throw error\n\n return res.status(200).json({\n success: true,\n count: count || 0,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to get count',\n timestamp: Date.now()\n })\n }\n}\n");
|
|
35
95
|
};
|
|
36
96
|
//# sourceMappingURL=supabase.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../../src/fetchers/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../../src/fetchers/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,UAAU,CAAA;AAEjB,MAAM,CAAC,IAAM,sBAAsB,GAAG,UACpC,MAA+B;IAE/B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QACzC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAA;KAClE;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;QACjE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAA;KAC7D;IAED,IAAI;QACF,IAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QACvC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE;YACpF,OAAO,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAA;SACnF;KACF;IAAC,WAAM;QACN,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAA;KAChE;IAED,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE;QAClD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,+DAA+D;SACvE,CAAA;KACF;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA;AAUD,MAAM,CAAC,IAAM,uBAAuB,GAAG,UACrC,MAA+B,EAC/B,SAAiB;IAEjB,IAAM,cAAc,GAAG,MAAwB,CAAA;IAC/C,IAAM,WAAW,GAAG,cAAc,CAAC,WAAW,CAAA;IAC9C,IAAM,MAAM,GAAG,cAAc,CAAC,cAAc,IAAI,cAAc,CAAC,YAAY,CAAA;IAC3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2DA;IACA,OAAO,iLAQH,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,oBAC3B,sBAAsB,CAAC,MAAM,CAAC,8CAMlC,yBAAyB,EAAE,iBAE3B,+BAA+B,EAAE,uuHAyGjC,yBAAyB,EAAE,2QAOK,SAAS,qjBAaqC,SAAS,2+HA0GxF,CAAA;AACD,CAAC,CAAA;AAED,yCAAyC;AACzC,MAAM,CAAC,IAAM,4BAA4B,GAAG,UAAC,OAAY,EAAE,SAAiB;IAC1E,OAAO,wLAM6B,SAAS,kdAYmC,SAAS,o+EAoE1F,CAAA;AACD,CAAC,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const validateTeleportConfig: (config: Record<string, unknown>) => {
|
|
2
|
+
isValid: boolean;
|
|
3
|
+
error?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const generateTeleportFetcher: (config: Record<string, unknown>, tableName: string) => string;
|
|
6
|
+
export declare const generateTeleportCountFetcher: (config: Record<string, unknown>, tableName: string) => string;
|
|
7
|
+
//# sourceMappingURL=teleport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,sBAAsB,WACzB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAMpC,CAAA;AAED,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MAgVF,CAAA;AAED,eAAO,MAAM,4BAA4B,WAC/B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA0FF,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { replaceSecretReference, generateDateFormatterCode, generateSafeJSONParseCode, generateSearchEscapeHelpersCode, } from '../utils';
|
|
2
|
+
import { getTransformationCode, getTransformExpression, getTransformWrapperCode, } from '../transformations';
|
|
3
|
+
var DEFAULT_ENV_KEYS = {
|
|
4
|
+
host: 'TELEPORT_DB_HOST',
|
|
5
|
+
port: 'TELEPORT_DB_PORT',
|
|
6
|
+
user: 'TELEPORT_DB_USER',
|
|
7
|
+
password: 'TELEPORT_DB_PASSWORD',
|
|
8
|
+
database: 'TELEPORT_DB_NAME',
|
|
9
|
+
ssl: 'TELEPORT_DB_SSL',
|
|
10
|
+
};
|
|
11
|
+
var resolveEnvReference = function (value, defaultEnvKey) {
|
|
12
|
+
if (typeof value === 'string' && value.startsWith('teleporthq.secrets.')) {
|
|
13
|
+
return replaceSecretReference(value);
|
|
14
|
+
}
|
|
15
|
+
return "process.env.".concat(defaultEnvKey);
|
|
16
|
+
};
|
|
17
|
+
export var validateTeleportConfig = function (config) {
|
|
18
|
+
if (!config || typeof config !== 'object') {
|
|
19
|
+
return { isValid: false, error: 'Config must be a valid object' };
|
|
20
|
+
}
|
|
21
|
+
return { isValid: true };
|
|
22
|
+
};
|
|
23
|
+
export var generateTeleportFetcher = function (config, tableName) {
|
|
24
|
+
var _a;
|
|
25
|
+
var dbConfig = config;
|
|
26
|
+
var schema = (_a = dbConfig.options) === null || _a === void 0 ? void 0 : _a.schema;
|
|
27
|
+
var hostRef = resolveEnvReference(dbConfig.host, DEFAULT_ENV_KEYS.host);
|
|
28
|
+
var portRef = resolveEnvReference(dbConfig.port, DEFAULT_ENV_KEYS.port);
|
|
29
|
+
var userRef = resolveEnvReference(dbConfig.user || dbConfig.username, DEFAULT_ENV_KEYS.user);
|
|
30
|
+
var passwordRef = resolveEnvReference(dbConfig.password, DEFAULT_ENV_KEYS.password);
|
|
31
|
+
var databaseRef = resolveEnvReference(dbConfig.database, DEFAULT_ENV_KEYS.database);
|
|
32
|
+
var sslCode = dbConfig.ssl === false
|
|
33
|
+
? 'false'
|
|
34
|
+
: dbConfig.sslConfig
|
|
35
|
+
? "{\n ".concat(dbConfig.sslConfig.ca
|
|
36
|
+
? "ca: ".concat(resolveEnvReference(dbConfig.sslConfig.ca, 'TELEPORT_DB_SSL_CA'), ",")
|
|
37
|
+
: '', "\n ").concat(dbConfig.sslConfig.cert
|
|
38
|
+
? "cert: ".concat(resolveEnvReference(dbConfig.sslConfig.cert, 'TELEPORT_DB_SSL_CERT'), ",")
|
|
39
|
+
: '', "\n ").concat(dbConfig.sslConfig.key
|
|
40
|
+
? "key: ".concat(resolveEnvReference(dbConfig.sslConfig.key, 'TELEPORT_DB_SSL_KEY'), ",")
|
|
41
|
+
: '', "\n rejectUnauthorized: false\n }")
|
|
42
|
+
: "(process.env.".concat(DEFAULT_ENV_KEYS.ssl, " === 'false' ? false : process.env.").concat(DEFAULT_ENV_KEYS.ssl, " === 'true' ? { rejectUnauthorized: false } : undefined)");
|
|
43
|
+
return "import { Client } from 'pg'\n\nfunction normalizePostgresConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n if (/^postgresql:\\/(?!\\/)/i.test(connectionString)) {\n return connectionString.replace(/^postgresql:\\//i, 'postgresql://');\n }\n return connectionString;\n}\n\nfunction stripSslQueryParamsFromConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n try {\n var u = new URL(connectionString.replace(/^postgresql:/i, 'postgres:'));\n u.searchParams.delete('sslmode');\n u.searchParams.delete('ssl');\n u.searchParams.delete('sslrootcert');\n u.searchParams.delete('sslcert');\n u.searchParams.delete('sslkey');\n return u.toString().replace(/^postgres:/i, 'postgresql:');\n } catch (e) {\n return connectionString;\n }\n}\n\nconst getClient = () => {\n var ssl = ".concat(sslCode, ";\n var connStr = process.env.TELEPORT_DB_CONNECTION_STRING;\n if (connStr) {\n connStr = normalizePostgresConnectionString(connStr);\n }\n if (ssl === false && connStr) {\n connStr = stripSslQueryParamsFromConnectionString(connStr);\n }\n if (connStr) {\n return new Client(Object.assign(\n { connectionString: connStr },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n }\n return new Client(Object.assign(\n {\n host: ").concat(hostRef, ",\n port: parseInt(").concat(portRef, " || '5432', 10),\n user: ").concat(userRef, ",\n password: ").concat(passwordRef, ",\n database: ").concat(databaseRef, ",\n },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n}\n\n").concat(generateSafeJSONParseCode(), "\n\n").concat(generateSearchEscapeHelpersCode(), "\n").concat(getTransformationCode(tableName), "\n").concat(getTransformWrapperCode(tableName), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n parsedFilters.forEach((filter) => {\n if (!filter.source || filter.destination === undefined) return\n \n const field = filter.source\n const value = filter.destination\n const operand = filter.operand || '='\n \n if (Array.isArray(value)) {\n if (value.length === 0) return\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n if (operand === '!=') {\n conditions.push(`${field} NOT IN (${placeholders.join(', ')})`)\n } else {\n conditions.push(`${field} IN (${placeholders.join(', ')})`)\n }\n } else {\n if (value === null) {\n if (operand === '=') {\n conditions.push(`${field} IS NULL`)\n } else if (operand === '!=') {\n conditions.push(`${field} IS NOT NULL`)\n }\n } else {\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n conditions.push(`${field} ${sqlOperator} $${paramIndex}`)\n queryParams.push(value)\n paramIndex++\n }\n }\n })\n } else {\n Object.entries(parsedFilters).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n conditions.push(`${key} IN (${placeholders.join(', ')})`)\n } else {\n conditions.push(`${key} = $${paramIndex}`)\n queryParams.push(value)\n paramIndex++\n }\n })\n }\n \n return paramIndex\n}\n\n").concat(generateDateFormatterCode(), "\n\n// Matches DDL / dangerous statements the raw-query branch should refuse.\n// Keep this list conservative \u2014 anything destructive or schema-changing is\n// out of scope for a client-triggered fetch. SELECT and CTEs (WITH ...) are\n// the only shapes consumers legitimately need.\nconst BLOCKED_RAW_QUERY_PATTERNS = [\n /\\bcreate\\s+(?:temporary\\s+|temp\\s+|unlogged\\s+|global\\s+|local\\s+)?table\\b/i,\n /\\bcreate\\s+(?:unique\\s+)?index\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:materialized\\s+)?view\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?trigger\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:aggregate\\s+)?function\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?procedure\\b/i,\n /\\bcreate\\s+(?:database|schema|sequence|extension|type|role|user)\\b/i,\n /\\bdrop\\s+(?:table|view|index|schema|database|sequence|trigger|function|procedure|role|user|extension|type|materialized)\\b/i,\n /\\balter\\s+(?:table|view|index|schema|database|sequence|role|user|system)\\b/i,\n /\\btruncate\\b/i,\n /\\bgrant\\b/i,\n /\\brevoke\\b/i,\n /\\binsert\\b/i,\n /\\bupdate\\b/i,\n /\\bdelete\\b/i,\n /\\bcopy\\b/i,\n /\\bvacuum\\b/i,\n /\\breindex\\b/i,\n /\\bcluster\\b/i,\n]\n\nfunction assertRawQuerySafe(rawQuery) {\n if (typeof rawQuery !== 'string' || rawQuery.length === 0) {\n throw new Error('rawQuery must be a non-empty string')\n }\n // Reject multi-statement payloads \u2014 only single SELECT / WITH statements.\n // A trailing semicolon is tolerated but any content after it fails.\n var trimmed = rawQuery.trim().replace(/;\\s*$/, '')\n if (trimmed.indexOf(';') !== -1) {\n throw new Error('rawQuery must contain exactly one statement')\n }\n for (var i = 0; i < BLOCKED_RAW_QUERY_PATTERNS.length; i++) {\n if (BLOCKED_RAW_QUERY_PATTERNS[i].test(trimmed)) {\n throw new Error('rawQuery contains a blocked statement')\n }\n }\n}\n\nexport default async function handler(req, res) {\n const client = getClient()\n\n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n\n // If the caller supplied a rawQuery, run it verbatim (after a safety\n // guard). The schema-driven branch below builds `SELECT * FROM ").concat(tableName, "`\n // with optional filters \u2014 it's the default read path and ignores\n // rawQuery. Page-load workflows and other consumers that need a JOIN\n // or a user-scoped filter pass their fully-rendered SQL here via\n // `fetchData({ rawQuery })` and expect it to execute verbatim.\n if (req.query && typeof req.query.rawQuery === 'string' && req.query.rawQuery.length > 0) {\n assertRawQuerySafe(req.query.rawQuery)\n const rawResult = await client.query(req.query.rawQuery)\n const rawRows = Array.isArray(rawResult?.rows) ? rawResult.rows : []\n const rawPlain = rawRows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const rawSafe = JSON.parse(JSON.stringify(rawPlain, dateReplacer))\n return res.status(200).json({\n success: true,\n data: rawSafe,\n timestamp: Date.now()\n })\n }\n\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(schema
|
|
44
|
+
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(schema), "]")
|
|
45
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n \n if (columns.length > 0) {\n const pattern = '%' + escapeLikePattern(query) + '%'\n const placeholder = '$' + paramIndex\n paramIndex++\n queryParams.push(pattern)\n const searchConditions = columns.map(\n (col) => '\"' + sanitizeSearchIdentifier(col) + '\"::text ILIKE ' + placeholder + \" ESCAPE '|'\"\n )\n conditions.push('(' + searchConditions.join(' OR ') + ')')\n }\n }\n \n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n \n let sql = `SELECT * FROM ").concat(tableName, "`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const orderClauses = parsedSorts.map((sort) => {\n if (!sort.field) return null\n const order = (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n return `${sort.field} ${order}`\n }).filter(Boolean)\n\n if (orderClauses.length > 0) {\n sql += ` ORDER BY ${orderClauses.join(', ')}`\n }\n }\n } else if (sortBy) {\n sql += ` ORDER BY ${sortBy} ${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}`\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (limitValue) {\n sql += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sql += ` OFFSET ${offsetValue}`\n }\n \n const result = await client.query(sql, queryParams)\n const rows = Array.isArray(result?.rows) ? result.rows : []\n const plainRows = rows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n ").concat(getTransformExpression(tableName)
|
|
46
|
+
? "const transformedData = ".concat(getTransformExpression(tableName))
|
|
47
|
+
: '', "\n\n return res.status(200).json({\n success: true,\n data: ").concat(getTransformExpression(tableName) ? 'transformedData' : 'safeData', ",\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Teleport DB fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing database client:', error)\n }\n }\n }\n}\n");
|
|
48
|
+
};
|
|
49
|
+
export var generateTeleportCountFetcher = function (config, tableName) {
|
|
50
|
+
var _a;
|
|
51
|
+
var dbConfig = config;
|
|
52
|
+
var hasSchema = !!((_a = dbConfig.options) === null || _a === void 0 ? void 0 : _a.schema);
|
|
53
|
+
return "\nasync function getCount(req, res) {\n const client = getClient()\n\n try {\n await client.connect()\n const { query, queryColumns, filters } = req.query\n const conditions = []\n const queryParams = []\n let paramIndex = 1\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ".concat(hasSchema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(hasSchema
|
|
54
|
+
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(dbConfig.options.schema), "]")
|
|
55
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n \n if (columns.length > 0) {\n const pattern = '%' + escapeLikePattern(query) + '%'\n const placeholder = '$' + paramIndex\n paramIndex++\n queryParams.push(pattern)\n const searchConditions = columns\n .map(\n (col) => '\"' + sanitizeSearchIdentifier(col) + '\"::text ILIKE ' + placeholder + \" ESCAPE '|'\"\n )\n .join(' OR ')\n conditions.push('(' + searchConditions + ')')\n }\n }\n\n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n\n let countSql = `SELECT COUNT(*) FROM ").concat(tableName, "`\n if (conditions.length > 0) {\n countSql += ` WHERE ${conditions.join(' AND ')}`\n }\n\n const result = await client.query(countSql, queryParams)\n const count = parseInt(result.rows[0].count, 10)\n\n return res.status(200).json({\n success: true,\n count: count,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to get count',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing database client:', error)\n }\n }\n }\n}\n");
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=teleport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teleport.js","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turso.d.ts","sourceRoot":"","sources":["../../../src/fetchers/turso.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"turso.d.ts","sourceRoot":"","sources":["../../../src/fetchers/turso.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,mBAAmB,WACtB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAcpC,CAAA;AASD,eAAO,MAAM,oBAAoB,WACvB,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA8MF,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { replaceSecretReference, generateDateFormatterCode, generateSafeJSONParseCode, } from '../utils';
|
|
1
|
+
import { replaceSecretReference, generateDateFormatterCode, generateSafeJSONParseCode, generateSearchEscapeHelpersCode, } from '../utils';
|
|
2
2
|
export var validateTursoConfig = function (config) {
|
|
3
3
|
if (!config || typeof config !== 'object') {
|
|
4
4
|
return { isValid: false, error: 'Config must be a valid object' };
|
|
@@ -15,6 +15,6 @@ export var generateTursoFetcher = function (config, tableName) {
|
|
|
15
15
|
var tursoConfig = config;
|
|
16
16
|
var databaseUrl = tursoConfig.databaseUrl;
|
|
17
17
|
var token = tursoConfig.token;
|
|
18
|
-
return "import { createClient } from '@libsql/client'\n\n".concat(generateSafeJSONParseCode(), "\n\n").concat(generateDateFormatterCode(), "\n\nexport default async function handler(req, res) {\n let client = null\n try {\n client = createClient({\n url: ").concat(JSON.stringify(databaseUrl), ",\n authToken: ").concat(replaceSecretReference(token), "\n })\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n let sql = `SELECT * FROM ").concat(tableName, "`\n const whereClauses = []\n const queryParams = []\n let searchQueryColumns = null\n \n if (query) {\n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n const columns = Array.isArray(parsed) ? parsed : [parsed]\n // Cast columns to TEXT
|
|
18
|
+
return "import { createClient } from '@libsql/client'\n\n".concat(generateSafeJSONParseCode(), "\n\n").concat(generateSearchEscapeHelpersCode(), "\n\n").concat(generateDateFormatterCode(), "\n\nexport default async function handler(req, res) {\n let client = null\n try {\n client = createClient({\n url: ").concat(JSON.stringify(databaseUrl), ",\n authToken: ").concat(replaceSecretReference(token), "\n })\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n let sql = `SELECT * FROM ").concat(tableName, "`\n const whereClauses = []\n const queryParams = []\n let searchQueryColumns = null\n \n if (query) {\n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n const columns = Array.isArray(parsed) ? parsed : [parsed]\n // Cast columns to TEXT and LOWER both sides so the match is\n // case-insensitive regardless of SQLite collation.\n const pattern = \"%\" + escapeLikePattern(query) + \"%\"\n const searchConditions = columns.map(\n (col) =>\n 'LOWER(CAST(\"' + sanitizeSearchIdentifier(col) + '\" AS TEXT)) LIKE LOWER(?) ESCAPE ' + \"'|'\"\n )\n whereClauses.push(\"(\" + searchConditions.join(\" OR \") + \")\")\n columns.forEach(() => {\n queryParams.push(pattern)\n })\n } else {\n // Store query for post-filtering if columns not specified\n searchQueryColumns = query\n }\n }\n \n // Helper to sanitize identifier (prevent SQL injection in column names)\n const sanitizeIdentifier = (name) => {\n // Only allow alphanumeric and underscore\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(`Invalid identifier: ${name}`)\n }\n return `\"${name}\"`\n }\n \n if (filters) {\n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n parsedFilters.forEach((filter) => {\n if (!filter.source || filter.destination === undefined) return\n \n const field = sanitizeIdentifier(filter.source)\n const value = filter.destination\n const operand = filter.operand || '='\n \n if (Array.isArray(value)) {\n if (value.length === 0) return\n const placeholders = value.map(() => '?').join(', ')\n queryParams.push(...value)\n if (operand === '!=') {\n whereClauses.push(`${field} NOT IN (${placeholders})`)\n } else {\n whereClauses.push(`${field} IN (${placeholders})`)\n }\n } else {\n if (value === null) {\n if (operand === '=') {\n whereClauses.push(`${field} IS NULL`)\n } else if (operand === '!=') {\n whereClauses.push(`${field} IS NOT NULL`)\n }\n } else {\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n whereClauses.push(`${field} ${sqlOperator} ?`)\n queryParams.push(value)\n }\n }\n })\n } else {\n Object.entries(parsedFilters).forEach(([key, value]) => {\n const field = sanitizeIdentifier(key)\n if (Array.isArray(value)) {\n const placeholders = value.map(() => '?').join(', ')\n queryParams.push(...value)\n whereClauses.push(`${field} IN (${placeholders})`)\n } else {\n whereClauses.push(`${field} = ?`)\n queryParams.push(value)\n }\n })\n }\n }\n \n if (whereClauses.length > 0) {\n sql += ` WHERE ${whereClauses.join(' AND ')}`\n }\n \n // Handle sorts - new array format\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const orderClauses = parsedSorts.map((sort) => {\n if (!sort.field) return null\n const order = (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n return `${sanitizeIdentifier(sort.field)} ${order}`\n }).filter(Boolean)\n\n if (orderClauses.length > 0) {\n sql += ` ORDER BY ${orderClauses.join(', ')}`\n }\n }\n } else if (sortBy) {\n const sortOrderValue = (sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n sql += ` ORDER BY ${sanitizeIdentifier(sortBy)} ${sortOrderValue}`\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n // Only apply SQL pagination if we're not doing post-filtering\n if (!searchQueryColumns) {\n if (limitValue) {\n sql += ` LIMIT ?`\n queryParams.push(parseInt(limitValue))\n }\n \n if (offsetValue !== undefined) {\n sql += ` OFFSET ?`\n queryParams.push(offsetValue)\n }\n }\n \n const result = await client.execute({\n sql,\n args: queryParams\n })\n \n let data = result.rows.map((row) => {\n const obj = {}\n result.columns.forEach((col, idx) => {\n obj[col] = row[col]\n })\n return obj\n })\n \n // Apply post-filtering for search without queryColumns\n if (searchQueryColumns) {\n const searchQuery = searchQueryColumns.toLowerCase()\n data = data.filter((item) => {\n try {\n const stringified = JSON.stringify(item).toLowerCase()\n return stringified.includes(searchQuery)\n } catch {\n return false\n }\n })\n \n // Apply pagination after filtering\n if (limitValue) {\n const start = offsetValue || 0\n data = data.slice(start, start + parseInt(limitValue))\n } else if (offsetValue) {\n data = data.slice(offsetValue)\n }\n }\n \n const safeData = JSON.parse(JSON.stringify(data, dateReplacer))\n \n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Turso fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.close()\n } catch (error) {\n console.error('Error closing Turso client:', error)\n }\n }\n }\n}\n");
|
|
19
19
|
};
|
|
20
20
|
//# sourceMappingURL=turso.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turso.js","sourceRoot":"","sources":["../../../src/fetchers/turso.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"turso.js","sourceRoot":"","sources":["../../../src/fetchers/turso.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,UAAU,CAAA;AAEjB,MAAM,CAAC,IAAM,mBAAmB,GAAG,UACjC,MAA+B;IAE/B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QACzC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAA;KAClE;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;QACjE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAA;KACnE;IAED,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE;QACrD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,wCAAwC,EAAE,CAAA;KAC3E;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA;AASD,MAAM,CAAC,IAAM,oBAAoB,GAAG,UAClC,MAA+B,EAC/B,SAAiB;IAEjB,IAAM,WAAW,GAAG,MAAqB,CAAA;IACzC,IAAM,WAAW,GAAG,WAAW,CAAC,WAAW,CAAA;IAC3C,IAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAA;IAE/B,OAAO,2DAEP,yBAAyB,EAAE,iBAE3B,+BAA+B,EAAE,iBAEjC,yBAAyB,EAAE,0IAMhB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,iCACrB,sBAAsB,CAAC,KAAK,CAAC,gLAKhB,SAAS,g/LAsLxC,CAAA;AACD,CAAC,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as types from '@babel/types';
|
|
2
|
+
export declare const buildFiltersStringifyCall: (filters: Array<{
|
|
3
|
+
source?: string;
|
|
4
|
+
operand?: string;
|
|
5
|
+
destination: unknown;
|
|
6
|
+
}>, buildDestinationExpression: (destination: unknown) => types.Expression) => types.CallExpression;
|
|
7
|
+
export declare const appendFiltersParam: (paramsProps: types.ObjectProperty[], filters: Array<{
|
|
8
|
+
source?: string;
|
|
9
|
+
operand?: string;
|
|
10
|
+
destination: unknown;
|
|
11
|
+
}> | undefined, buildDestinationExpression: (destination: unknown) => types.Expression) => void;
|
|
12
|
+
export declare const pushStateIdsAsDeps: (deps: types.Expression[], seen: Set<string>, stateIds: string[]) => void;
|
|
13
|
+
//# sourceMappingURL=filter-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-utils.d.ts","sourceRoot":"","sources":["../../src/filter-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAkDrC,eAAO,MAAM,yBAAyB,YAC3B,MAAM;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,4CACjC,OAAO,KAAK,MAAM,UAAU,KACrE,MAAM,cAkBR,CAAA;AAGD,eAAO,MAAM,kBAAkB,gBAChB,MAAM,cAAc,EAAE,WAC1B,MAAM;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,SAAS,4CAC7C,OAAO,KAAK,MAAM,UAAU,KACrE,IAUF,CAAA;AAUD,eAAO,MAAM,kBAAkB,SACvB,MAAM,UAAU,EAAE,QAClB,IAAI,MAAM,CAAC,YACP,MAAM,EAAE,KACjB,IAQF,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as types from '@babel/types';
|
|
2
|
+
// Build a single filter entry: { source: '<src>', destination: <destExpr>, operand: '<op>' }
|
|
3
|
+
var buildFilterEntry = function (source, destinationExpr, operand) {
|
|
4
|
+
return types.objectExpression([
|
|
5
|
+
types.objectProperty(types.identifier('source'), types.stringLiteral(source)),
|
|
6
|
+
types.objectProperty(types.identifier('destination'), destinationExpr),
|
|
7
|
+
types.objectProperty(types.identifier('operand'), types.stringLiteral(operand)),
|
|
8
|
+
]);
|
|
9
|
+
};
|
|
10
|
+
// Predicate `(__f) => __f.destination !== '' && __f.destination !== null && __f.destination !== undefined`
|
|
11
|
+
// Used at runtime to drop filter conditions whose destination resolves to an
|
|
12
|
+
// empty value. Keeps the page generic: state-bound filters (e.g.
|
|
13
|
+
// `selectedCategory === ''` after picking "All Categories") and url-bound
|
|
14
|
+
// filters (e.g. no `?categoryFilter=` in URL → `router?.query?.categoryFilter`
|
|
15
|
+
// is `undefined`) both fall away cleanly instead of being sent to the API as
|
|
16
|
+
// `destination: ''`, which the SQL backend interprets as a literal empty
|
|
17
|
+
// string and returns zero rows.
|
|
18
|
+
var buildNonEmptyDestinationPredicate = function () {
|
|
19
|
+
var f = types.identifier('__f');
|
|
20
|
+
var dest = types.memberExpression(f, types.identifier('destination'));
|
|
21
|
+
return types.arrowFunctionExpression([f], types.logicalExpression('&&', types.logicalExpression('&&', types.binaryExpression('!==', dest, types.stringLiteral('')), types.binaryExpression('!==', dest, types.nullLiteral())), types.binaryExpression('!==', dest, types.identifier('undefined'))));
|
|
22
|
+
};
|
|
23
|
+
// Build the AST for:
|
|
24
|
+
// JSON.stringify(
|
|
25
|
+
// [ {source, destination, operand}, ... ]
|
|
26
|
+
// .filter(__f => __f.destination !== '' && __f.destination !== null && __f.destination !== undefined)
|
|
27
|
+
// )
|
|
28
|
+
//
|
|
29
|
+
// The `.filter(...)` step is unconditional even when every entry is a static
|
|
30
|
+
// (always-truthy) destination — the cost is negligible at runtime and it
|
|
31
|
+
// keeps the generated code uniform, so future regressions where a new filter
|
|
32
|
+
// type is added but its empty-value behavior is forgotten don't silently
|
|
33
|
+
// re-introduce empty-string filters.
|
|
34
|
+
export var buildFiltersStringifyCall = function (filters, buildDestinationExpression) {
|
|
35
|
+
var entries = filters.map(function (filter) {
|
|
36
|
+
return buildFilterEntry(filter.source || '', buildDestinationExpression(filter.destination), filter.operand || '');
|
|
37
|
+
});
|
|
38
|
+
var filteredArray = types.callExpression(types.memberExpression(types.arrayExpression(entries), types.identifier('filter')), [buildNonEmptyDestinationPredicate()]);
|
|
39
|
+
return types.callExpression(types.memberExpression(types.identifier('JSON'), types.identifier('stringify')), [filteredArray]);
|
|
40
|
+
};
|
|
41
|
+
// Convenience: push `filters: <call>` onto a paramsProps array.
|
|
42
|
+
export var appendFiltersParam = function (paramsProps, filters, buildDestinationExpression) {
|
|
43
|
+
if (!filters || filters.length === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
paramsProps.push(types.objectProperty(types.identifier('filters'), buildFiltersStringifyCall(filters, buildDestinationExpression)));
|
|
47
|
+
};
|
|
48
|
+
// Appends each id in `stateIds` as a bare `Identifier` onto `deps`, skipping
|
|
49
|
+
// any id already tracked in `seen`. Mutates both `deps` and `seen`.
|
|
50
|
+
//
|
|
51
|
+
// Used by every count-fetch and params-`useMemo` builder in the pagination
|
|
52
|
+
// plugin to wire state-bound filter destinations into React's deps array.
|
|
53
|
+
// Centralising the loop here means a new dep-source (sort, future filter
|
|
54
|
+
// shapes) can hook into the same dedup-by-name set without re-implementing
|
|
55
|
+
// it at four call sites.
|
|
56
|
+
export var pushStateIdsAsDeps = function (deps, seen, stateIds) {
|
|
57
|
+
for (var _i = 0, stateIds_1 = stateIds; _i < stateIds_1.length; _i++) {
|
|
58
|
+
var id = stateIds_1[_i];
|
|
59
|
+
if (seen.has(id)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
seen.add(id);
|
|
63
|
+
deps.push(types.identifier(id));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=filter-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-utils.js","sourceRoot":"","sources":["../../src/filter-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAKvB,MAAM,4BAA4B,CAAA;AA6anC,eAAO,MAAM,+BAA+B,EAAE,sBAAsB,CAAC,EAAE,CAietE,CAAA;AAiOD,eAAO,MAAM,mCAAmC,EAAE,sBAAsB,CAAC,EAAE,CA0O1E,CAAA;AAED,cAAc,wBAAwB,CAAA;AACtC,cAAc,SAAS,CAAA;AACvB,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA"}
|
package/dist/esm/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
36
36
|
};
|
|
37
37
|
import { FileType, ChunkType, } from '@teleporthq/teleport-types';
|
|
38
38
|
import { UIDLUtils, StringUtils } from '@teleporthq/teleport-shared';
|
|
39
|
-
import { extractDataSourceIntoNextAPIFolder, extractDataSourceIntoGetStaticProps, sanitizeFileName, } from './utils';
|
|
39
|
+
import { extractDataSourceIntoNextAPIFolder, extractDataSourceIntoGetStaticProps, sanitizeFileName, hasUnresolvableDynamicParams, } from './utils';
|
|
40
40
|
import { createNextArrayMapperPaginationPlugin } from './pagination-plugin';
|
|
41
41
|
import * as types from '@babel/types';
|
|
42
42
|
// Prefix used to clearly differentiate wrapped data source expression props
|
|
@@ -283,8 +283,8 @@ function extractPaginationConfigEarly(uidlNode, resources) {
|
|
|
283
283
|
export var createNextPagesDataSourcePlugin = function () {
|
|
284
284
|
var nextPagesDataSourcePlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
|
|
285
285
|
var uidl, chunks, options, dependencies, dataSources, componentChunk, opts, pageConfig, getStaticPropsChunk, firstDataSourceInfo, fetcherImportName, dataProviderPositionCounters, existingPropKeys, funcDecl, tryStmt, retStmt, propsProp, propsVal, _i, _a, prop, keyName, wrapContext, functionDeclaration, functionBody, tryBlock, returnStatement, propsObject, propsValue, meta, _b, _c, wrapped, fetchCallExpression, safeFetchExpression, existingIndex, promiseAllCall, arrayPattern, hasPaginatedOrSearchDataSources, getStaticPropsAST, paginationPlugin;
|
|
286
|
-
var _d;
|
|
287
|
-
return __generator(this, function (
|
|
286
|
+
var _d, _e;
|
|
287
|
+
return __generator(this, function (_f) {
|
|
288
288
|
uidl = structure.uidl, chunks = structure.chunks, options = structure.options, dependencies = structure.dependencies;
|
|
289
289
|
// Early return if no options or dataSources
|
|
290
290
|
if (!options || !options.dataSources) {
|
|
@@ -333,7 +333,7 @@ export var createNextPagesDataSourcePlugin = function () {
|
|
|
333
333
|
// Data source nodes can be either:
|
|
334
334
|
// 1. Direct: node.type === 'data-source-item' or 'data-source-list'
|
|
335
335
|
// 2. Wrapped in element: node.type === 'element' && node.content.type === 'data-source-item' or 'data-source-list'
|
|
336
|
-
var _a;
|
|
336
|
+
var _a, _b, _c;
|
|
337
337
|
var dataSourceNode = null;
|
|
338
338
|
if (node.type === 'data-source-item' || node.type === 'data-source-list') {
|
|
339
339
|
// Direct data source node
|
|
@@ -404,19 +404,20 @@ export var createNextPagesDataSourcePlugin = function () {
|
|
|
404
404
|
// Skip this node but position counter is already incremented above
|
|
405
405
|
return;
|
|
406
406
|
}
|
|
407
|
-
// Check if resource has dynamic parameters
|
|
407
|
+
// Check if resource has dynamic parameters that can't be resolved server-side
|
|
408
|
+
var dynamicRouteAttr = (_a = uidl.outputOptions) === null || _a === void 0 ? void 0 : _a.dynamicRouteAttribute;
|
|
408
409
|
// tslint:disable-next-line:no-any
|
|
409
|
-
var hasResourceDynamicParams = ((
|
|
410
|
-
?
|
|
410
|
+
var hasResourceDynamicParams = ((_b = dataSourceNode.content.resource) === null || _b === void 0 ? void 0 : _b.params)
|
|
411
|
+
? hasUnresolvableDynamicParams(dataSourceNode.content.resource.params, dynamicRouteAttr)
|
|
411
412
|
: false;
|
|
412
|
-
// If no dynamic params, extract to getStaticProps (server-side)
|
|
413
|
+
// If no dynamic params (or all are route-resolvable), extract to getStaticProps (server-side)
|
|
413
414
|
// Otherwise, extract to API route (client-side)
|
|
414
415
|
if (!hasResourceDynamicParams) {
|
|
415
416
|
// extractDataSourceIntoGetStaticProps is called for every UIDL data-source node.
|
|
416
417
|
// When multiple nodes share the same dataSourceKey (same resource ID), the function is
|
|
417
418
|
// idempotent for getStaticProps (skips duplicate fetches via existingInFetchMeta) but
|
|
418
419
|
// still updates the next uninitialized DataProvider JSX node on each call.
|
|
419
|
-
var result = extractDataSourceIntoGetStaticProps(dataSourceNode, dataSources, componentChunk, getStaticPropsChunk, chunks, options.extractedResources, dependencies);
|
|
420
|
+
var result = extractDataSourceIntoGetStaticProps(dataSourceNode, dataSources, componentChunk, getStaticPropsChunk, chunks, options.extractedResources, dependencies, dynamicRouteAttr, (_c = uidl.outputOptions) === null || _c === void 0 ? void 0 : _c.folderPath);
|
|
420
421
|
if (result.success && result.chunk) {
|
|
421
422
|
getStaticPropsChunk = result.chunk;
|
|
422
423
|
// Track the first data source info for wrapping dataSourceData expressions
|
|
@@ -471,7 +472,7 @@ export var createNextPagesDataSourcePlugin = function () {
|
|
|
471
472
|
}
|
|
472
473
|
}
|
|
473
474
|
}
|
|
474
|
-
catch (
|
|
475
|
+
catch (_g) {
|
|
475
476
|
// Ignore errors in parsing existing props
|
|
476
477
|
}
|
|
477
478
|
}
|
|
@@ -576,12 +577,114 @@ export var createNextPagesDataSourcePlugin = function () {
|
|
|
576
577
|
chunks.push(getStaticPropsChunk);
|
|
577
578
|
}
|
|
578
579
|
}
|
|
580
|
+
// For pages with dynamicRouteAttribute and dynamic SEO references,
|
|
581
|
+
// add the SEO-referenced fields as direct page props from the fetched data.
|
|
582
|
+
// The head config plugin generates props?.metaTitle from refPath: ['metaTitle'],
|
|
583
|
+
// so we need those fields at the top level of props.
|
|
584
|
+
if (((_e = uidl.outputOptions) === null || _e === void 0 ? void 0 : _e.dynamicRouteAttribute) && getStaticPropsChunk && uidl.seo) {
|
|
585
|
+
addDynamicSeoPropsToGetStaticProps(uidl.seo, getStaticPropsChunk);
|
|
586
|
+
}
|
|
579
587
|
paginationPlugin = createNextArrayMapperPaginationPlugin();
|
|
580
588
|
return [2 /*return*/, paginationPlugin(structure)];
|
|
581
589
|
});
|
|
582
590
|
}); };
|
|
583
591
|
return nextPagesDataSourcePlugin;
|
|
584
592
|
};
|
|
593
|
+
/**
|
|
594
|
+
* Extracts dynamic SEO reference paths from UIDLComponentSEO and returns the
|
|
595
|
+
* refPath arrays for prop-based dynamic references.
|
|
596
|
+
*/
|
|
597
|
+
function extractDynamicSeoRefPaths(seo) {
|
|
598
|
+
var paths = [];
|
|
599
|
+
var extractFromValue = function (value) {
|
|
600
|
+
var _a;
|
|
601
|
+
if (typeof value === 'object' &&
|
|
602
|
+
value !== null &&
|
|
603
|
+
'type' in value &&
|
|
604
|
+
value.type === 'dynamic') {
|
|
605
|
+
var dynRef = value;
|
|
606
|
+
if (dynRef.content.referenceType === 'prop' && ((_a = dynRef.content.refPath) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
607
|
+
paths.push(dynRef.content.refPath);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
if (seo.title) {
|
|
612
|
+
extractFromValue(seo.title);
|
|
613
|
+
}
|
|
614
|
+
if (seo.metaTags) {
|
|
615
|
+
seo.metaTags.forEach(function (tag) {
|
|
616
|
+
Object.values(tag).forEach(extractFromValue);
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
return paths;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* For pages with dynamic route attributes and dynamic SEO prop references,
|
|
623
|
+
* augments the getStaticProps return to include SEO fields extracted from
|
|
624
|
+
* the first fetched entity. This enables server-side SEO resolution.
|
|
625
|
+
*/
|
|
626
|
+
function addDynamicSeoPropsToGetStaticProps(seo, getStaticPropsChunk) {
|
|
627
|
+
var seoRefPaths = extractDynamicSeoRefPaths(seo);
|
|
628
|
+
if (seoRefPaths.length === 0) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
var exportDecl = getStaticPropsChunk.content;
|
|
633
|
+
if (!(exportDecl === null || exportDecl === void 0 ? void 0 : exportDecl.declaration) || exportDecl.declaration.type !== 'FunctionDeclaration') {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
var funcBody = exportDecl.declaration.body;
|
|
637
|
+
var tryStmt = funcBody.body.find(function (s) { return s.type === 'TryStatement'; });
|
|
638
|
+
if (!tryStmt) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
var returnStmt = tryStmt.block.body.find(function (s) { return s.type === 'ReturnStatement'; });
|
|
642
|
+
if (!(returnStmt === null || returnStmt === void 0 ? void 0 : returnStmt.argument) || returnStmt.argument.type !== 'ObjectExpression') {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
var propsProperty = returnStmt.argument.properties.find(function (p) {
|
|
646
|
+
var _a, _b;
|
|
647
|
+
return p.type === 'ObjectProperty' &&
|
|
648
|
+
(((_a = p.key) === null || _a === void 0 ? void 0 : _a.name) === 'props' ||
|
|
649
|
+
((_b = p.key) === null || _b === void 0 ? void 0 : _b.value) === 'props');
|
|
650
|
+
});
|
|
651
|
+
if (!propsProperty || propsProperty.value.type !== 'ObjectExpression') {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
var propsObj = propsProperty.value;
|
|
655
|
+
// Find the first data prop (non-spread, non-string-keyed) as the source entity.
|
|
656
|
+
// Exclude spread elements and meta spreads that the data-source plugin adds.
|
|
657
|
+
var firstDataProp = propsObj.properties.find(function (p) { var _a; return p.type === 'ObjectProperty' && ((_a = p.key) === null || _a === void 0 ? void 0 : _a.name) !== undefined; });
|
|
658
|
+
if (!firstDataProp) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
var addedSeoFields = new Set();
|
|
662
|
+
for (var _i = 0, seoRefPaths_1 = seoRefPaths; _i < seoRefPaths_1.length; _i++) {
|
|
663
|
+
var refPath = seoRefPaths_1[_i];
|
|
664
|
+
var fieldName = refPath[refPath.length - 1];
|
|
665
|
+
if (addedSeoFields.has(fieldName)) {
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
addedSeoFields.add(fieldName);
|
|
669
|
+
var memberExpr = types.cloneNode(firstDataProp.value, true);
|
|
670
|
+
// If the prop value is an array (no index access), get the first element
|
|
671
|
+
var valueStr = JSON.stringify(firstDataProp.value);
|
|
672
|
+
var hasIndexAccess = valueStr.includes('"computed":true');
|
|
673
|
+
if (!hasIndexAccess) {
|
|
674
|
+
memberExpr = types.optionalMemberExpression(memberExpr, types.numericLiteral(0), true, true);
|
|
675
|
+
}
|
|
676
|
+
for (var _a = 0, refPath_1 = refPath; _a < refPath_1.length; _a++) {
|
|
677
|
+
var segment = refPath_1[_a];
|
|
678
|
+
memberExpr = types.optionalMemberExpression(memberExpr, types.identifier(segment), false, true);
|
|
679
|
+
}
|
|
680
|
+
var seoValue = types.logicalExpression('??', memberExpr, types.stringLiteral(''));
|
|
681
|
+
propsObj.properties.push(types.objectProperty(types.identifier(fieldName), seoValue));
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
catch (_b) {
|
|
685
|
+
// If AST manipulation fails, skip SEO prop injection silently
|
|
686
|
+
}
|
|
687
|
+
}
|
|
585
688
|
// Helper function to stringify complex params (sorts, filters) in DataProvider components
|
|
586
689
|
function stringifyComplexParamsInDataProviders(componentChunk) {
|
|
587
690
|
if (!componentChunk || !componentChunk.content) {
|