@soulbatical/tetra-core 0.1.13 → 0.1.15
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.
Potentially problematic release.
This version of @soulbatical/tetra-core might be problematic. Click here for more details.
- package/dist/generators/rls-auditor.d.ts +39 -0
- package/dist/generators/rls-auditor.d.ts.map +1 -0
- package/dist/generators/rls-auditor.js +505 -0
- package/dist/generators/rls-auditor.js.map +1 -0
- package/dist/generators/rls-checker.d.ts +94 -0
- package/dist/generators/rls-checker.d.ts.map +1 -0
- package/dist/generators/rls-checker.js +215 -0
- package/dist/generators/rls-checker.js.map +1 -0
- package/dist/generators/rls-generator.d.ts +77 -0
- package/dist/generators/rls-generator.d.ts.map +1 -0
- package/dist/generators/rls-generator.js +402 -0
- package/dist/generators/rls-generator.js.map +1 -0
- package/dist/generators/rpc/detail-rpc-generator.d.ts +58 -0
- package/dist/generators/rpc/detail-rpc-generator.d.ts.map +1 -0
- package/dist/generators/rpc/detail-rpc-generator.js +163 -0
- package/dist/generators/rpc/detail-rpc-generator.js.map +1 -0
- package/dist/generators/rpc/index.d.ts +24 -0
- package/dist/generators/rpc/index.d.ts.map +1 -0
- package/dist/generators/rpc/index.js +20 -0
- package/dist/generators/rpc/index.js.map +1 -0
- package/dist/generators/rpc/rpc-generator.d.ts +150 -0
- package/dist/generators/rpc/rpc-generator.d.ts.map +1 -0
- package/dist/generators/rpc/rpc-generator.js +743 -0
- package/dist/generators/rpc/rpc-generator.js.map +1 -0
- package/dist/generators/rpc/templates/array.d.ts +29 -0
- package/dist/generators/rpc/templates/array.d.ts.map +1 -0
- package/dist/generators/rpc/templates/array.js +40 -0
- package/dist/generators/rpc/templates/array.js.map +1 -0
- package/dist/generators/rpc/templates/auth.d.ts +85 -0
- package/dist/generators/rpc/templates/auth.d.ts.map +1 -0
- package/dist/generators/rpc/templates/auth.js +233 -0
- package/dist/generators/rpc/templates/auth.js.map +1 -0
- package/dist/generators/rpc/templates/column.d.ts +39 -0
- package/dist/generators/rpc/templates/column.d.ts.map +1 -0
- package/dist/generators/rpc/templates/column.js +97 -0
- package/dist/generators/rpc/templates/column.js.map +1 -0
- package/dist/generators/rpc/templates/enum.d.ts +33 -0
- package/dist/generators/rpc/templates/enum.d.ts.map +1 -0
- package/dist/generators/rpc/templates/enum.js +93 -0
- package/dist/generators/rpc/templates/enum.js.map +1 -0
- package/dist/generators/rpc/templates/nullable.d.ts +31 -0
- package/dist/generators/rpc/templates/nullable.d.ts.map +1 -0
- package/dist/generators/rpc/templates/nullable.js +50 -0
- package/dist/generators/rpc/templates/nullable.js.map +1 -0
- package/dist/generators/rpc/templates/related.d.ts +47 -0
- package/dist/generators/rpc/templates/related.d.ts.map +1 -0
- package/dist/generators/rpc/templates/related.js +182 -0
- package/dist/generators/rpc/templates/related.js.map +1 -0
- package/dist/generators/rpc/templates/search.d.ts +42 -0
- package/dist/generators/rpc/templates/search.d.ts.map +1 -0
- package/dist/generators/rpc/templates/search.js +81 -0
- package/dist/generators/rpc/templates/search.js.map +1 -0
- package/dist/generators/rpc/templates/time.d.ts +44 -0
- package/dist/generators/rpc/templates/time.d.ts.map +1 -0
- package/dist/generators/rpc/templates/time.js +143 -0
- package/dist/generators/rpc/templates/time.js.map +1 -0
- package/dist/generators/rpc/utils.d.ts +58 -0
- package/dist/generators/rpc/utils.d.ts.map +1 -0
- package/dist/generators/rpc/utils.js +92 -0
- package/dist/generators/rpc/utils.js.map +1 -0
- package/dist/generators/rpc/validator.d.ts +21 -0
- package/dist/generators/rpc/validator.d.ts.map +1 -0
- package/dist/generators/rpc/validator.js +398 -0
- package/dist/generators/rpc/validator.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/shared/auth/index.d.ts +1 -1
- package/dist/shared/auth/index.d.ts.map +1 -1
- package/dist/shared/auth/routes.d.ts +4 -1
- package/dist/shared/auth/routes.d.ts.map +1 -1
- package/dist/shared/auth/routes.js +83 -1
- package/dist/shared/auth/routes.js.map +1 -1
- package/dist/shared/auth/types.d.ts +24 -0
- package/dist/shared/auth/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enum Filter Template
|
|
3
|
+
* For filters with predefined values (status, phase, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { escapeIdentifier, getTableAlias } from '../utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for enum filter
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Input: { name: 'status', column: 'status', values: ['paid', 'pending'] }
|
|
11
|
+
* // Output: AND (p_status IS NULL OR p_status = 'all' OR status = p_status)
|
|
12
|
+
*/
|
|
13
|
+
export function generateEnumWhere(filter, tableName, aliasMap) {
|
|
14
|
+
const paramName = `p_${filter.rpcParam || filter.name}`;
|
|
15
|
+
const column = escapeIdentifier(filter.column);
|
|
16
|
+
const alias = getTableAlias(tableName, aliasMap);
|
|
17
|
+
// Check if this is a boolean column with mapping
|
|
18
|
+
const booleanMapping = filter.sqlGeneration?.booleanMapping;
|
|
19
|
+
if (booleanMapping) {
|
|
20
|
+
// Generate conditions for boolean mapping (e.g., 'yes' → true, 'no' → false)
|
|
21
|
+
const conditions = Object.entries(booleanMapping)
|
|
22
|
+
.map(([textValue, boolValue]) => `(${paramName} = '${textValue}' AND ${alias}.${column} = ${boolValue})`)
|
|
23
|
+
.join('\n OR ');
|
|
24
|
+
return `
|
|
25
|
+
-- ${filter.name} (enum filter - boolean column)
|
|
26
|
+
AND (
|
|
27
|
+
${paramName} IS NULL OR ${paramName} = 'all'
|
|
28
|
+
OR ${conditions}
|
|
29
|
+
)`;
|
|
30
|
+
}
|
|
31
|
+
return `
|
|
32
|
+
-- ${filter.name} (enum filter)
|
|
33
|
+
AND (${paramName} IS NULL OR ${paramName} = 'all' OR ${alias}.${column}::text = ${paramName})`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate counts query for enum filter
|
|
37
|
+
* Uses UNION ALL pattern to ensure all values are present (even with count 0)
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Input: { name: 'status', values: ['paid', 'pending', 'cancelled'] }
|
|
41
|
+
* // Output:
|
|
42
|
+
* // 'byStatus', (
|
|
43
|
+
* // SELECT jsonb_object_agg(status_value, cnt)
|
|
44
|
+
* // FROM (
|
|
45
|
+
* // SELECT 'paid' as status_value, COUNT(*)::int as cnt FROM filtered_items WHERE status = 'paid'
|
|
46
|
+
* // UNION ALL
|
|
47
|
+
* // SELECT 'pending' as status_value, COUNT(*)::int as cnt FROM filtered_items WHERE status = 'pending'
|
|
48
|
+
* // UNION ALL
|
|
49
|
+
* // SELECT 'cancelled' as status_value, COUNT(*)::int as cnt FROM filtered_items WHERE status = 'cancelled'
|
|
50
|
+
* // )
|
|
51
|
+
* // )
|
|
52
|
+
*/
|
|
53
|
+
export function generateEnumCounts(filter) {
|
|
54
|
+
const countsKey = filter.countsKey || `by${filter.name.charAt(0).toUpperCase()}${filter.name.slice(1)}`;
|
|
55
|
+
let column = filter.column;
|
|
56
|
+
// Map joined columns to CTE aliases
|
|
57
|
+
if (column === 'o.status') {
|
|
58
|
+
column = 'order_status';
|
|
59
|
+
}
|
|
60
|
+
else if (column === 'o.source') {
|
|
61
|
+
column = 'order_source';
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
column = escapeIdentifier(column);
|
|
65
|
+
}
|
|
66
|
+
if (!filter.values || filter.values.length === 0) {
|
|
67
|
+
throw new Error(`Enum filter "${filter.name}" missing values array`);
|
|
68
|
+
}
|
|
69
|
+
// Check if this is a boolean column with mapping
|
|
70
|
+
const booleanMapping = filter.sqlGeneration?.booleanMapping;
|
|
71
|
+
// Generate UNION ALL queries for each value
|
|
72
|
+
const unionQueries = filter.values.map(value => {
|
|
73
|
+
if (booleanMapping && booleanMapping[value] !== undefined) {
|
|
74
|
+
// Boolean column - compare with true/false
|
|
75
|
+
const boolValue = booleanMapping[value];
|
|
76
|
+
return ` SELECT '${value}' as ${filter.name}_value, COUNT(*)::int as cnt
|
|
77
|
+
FROM filtered_items WHERE ${column} = ${boolValue}`;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Regular text/enum column - compare with string (cast to text for enum types)
|
|
81
|
+
return ` SELECT '${value}' as ${filter.name}_value, COUNT(*)::int as cnt
|
|
82
|
+
FROM filtered_items WHERE ${column}::text = '${value}'`;
|
|
83
|
+
}
|
|
84
|
+
}).join('\n UNION ALL\n');
|
|
85
|
+
return `
|
|
86
|
+
'${countsKey}', (
|
|
87
|
+
SELECT jsonb_object_agg(${filter.name}_value, cnt)
|
|
88
|
+
FROM (
|
|
89
|
+
${unionQueries}
|
|
90
|
+
) subquery
|
|
91
|
+
)`;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/enum.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nullable Filter Template
|
|
3
|
+
* For filters checking if field has value or is NULL (user presence, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { FilterConfig } from '../../../shared/types/feature-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for nullable filter
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Input: { name: 'user', column: 'user_id' }
|
|
11
|
+
* // Output:
|
|
12
|
+
* // AND (
|
|
13
|
+
* // p_user IS NULL OR p_user = 'all'
|
|
14
|
+
* // OR (p_user = 'with' AND user_id IS NOT NULL)
|
|
15
|
+
* // OR (p_user = 'without' AND user_id IS NULL)
|
|
16
|
+
* // )
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateNullableWhere(filter: FilterConfig, tableName: string, aliasMap?: Record<string, string>): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate counts query for nullable filter
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Input: { name: 'user', column: 'user_id' }
|
|
24
|
+
* // Output:
|
|
25
|
+
* // 'byUser', jsonb_build_object(
|
|
26
|
+
* // 'with', (SELECT COUNT(*)::int FROM filtered_items WHERE user_id IS NOT NULL),
|
|
27
|
+
* // 'without', (SELECT COUNT(*)::int FROM filtered_items WHERE user_id IS NULL)
|
|
28
|
+
* // )
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateNullableCounts(filter: FilterConfig): string;
|
|
31
|
+
//# sourceMappingURL=nullable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nullable.d.ts","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/nullable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAGvE;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAYxH;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CASnE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nullable Filter Template
|
|
3
|
+
* For filters checking if field has value or is NULL (user presence, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { escapeIdentifier, getTableAlias } from '../utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for nullable filter
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Input: { name: 'user', column: 'user_id' }
|
|
11
|
+
* // Output:
|
|
12
|
+
* // AND (
|
|
13
|
+
* // p_user IS NULL OR p_user = 'all'
|
|
14
|
+
* // OR (p_user = 'with' AND user_id IS NOT NULL)
|
|
15
|
+
* // OR (p_user = 'without' AND user_id IS NULL)
|
|
16
|
+
* // )
|
|
17
|
+
*/
|
|
18
|
+
export function generateNullableWhere(filter, tableName, aliasMap) {
|
|
19
|
+
const paramName = `p_${filter.rpcParam || filter.name}`;
|
|
20
|
+
const column = escapeIdentifier(filter.column);
|
|
21
|
+
const alias = getTableAlias(tableName, aliasMap);
|
|
22
|
+
return `
|
|
23
|
+
-- ${filter.name} (nullable filter - with/without)
|
|
24
|
+
AND (
|
|
25
|
+
${paramName} IS NULL OR ${paramName} = 'all'
|
|
26
|
+
OR (${paramName} = 'with' AND ${alias}.${column} IS NOT NULL)
|
|
27
|
+
OR (${paramName} = 'without' AND ${alias}.${column} IS NULL)
|
|
28
|
+
)`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generate counts query for nullable filter
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Input: { name: 'user', column: 'user_id' }
|
|
35
|
+
* // Output:
|
|
36
|
+
* // 'byUser', jsonb_build_object(
|
|
37
|
+
* // 'with', (SELECT COUNT(*)::int FROM filtered_items WHERE user_id IS NOT NULL),
|
|
38
|
+
* // 'without', (SELECT COUNT(*)::int FROM filtered_items WHERE user_id IS NULL)
|
|
39
|
+
* // )
|
|
40
|
+
*/
|
|
41
|
+
export function generateNullableCounts(filter) {
|
|
42
|
+
const countsKey = filter.countsKey || `by${filter.name.charAt(0).toUpperCase()}${filter.name.slice(1)}`;
|
|
43
|
+
const column = escapeIdentifier(filter.column);
|
|
44
|
+
return `
|
|
45
|
+
'${countsKey}', jsonb_build_object(
|
|
46
|
+
'with', (SELECT COUNT(*)::int FROM filtered_items WHERE ${column} IS NOT NULL),
|
|
47
|
+
'without', (SELECT COUNT(*)::int FROM filtered_items WHERE ${column} IS NULL)
|
|
48
|
+
)`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=nullable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nullable.js","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/nullable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAoB,EAAE,SAAiB,EAAE,QAAiC;IAC9G,MAAM,SAAS,GAAG,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IACxD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAO,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEjD,OAAO;WACE,MAAM,CAAC,IAAI;;UAEZ,SAAS,eAAe,SAAS;cAC7B,SAAS,iBAAiB,KAAK,IAAI,MAAM;cACzC,SAAS,oBAAoB,KAAK,IAAI,MAAM;QAClD,CAAC;AACT,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAoB;IACzD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACxG,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAO,CAAC,CAAC;IAEhD,OAAO;OACF,SAAS;gEACgD,MAAM;mEACH,MAAM;MACnE,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Related Filter Template
|
|
3
|
+
* For filters checking relationship existence (voucher, production, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { FilterConfig } from '../../../shared/types/feature-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for related filter
|
|
8
|
+
* Uses EXISTS/NOT EXISTS subqueries
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Input:
|
|
12
|
+
* // {
|
|
13
|
+
* // name: 'voucher',
|
|
14
|
+
* // relatedTable: 'orderitems',
|
|
15
|
+
* // foreignKey: 'orderid',
|
|
16
|
+
* // sqlGeneration: {
|
|
17
|
+
* // joins: [
|
|
18
|
+
* // { table: 'orderitems oi', on: 'oi.orderid = o.id', type: 'LEFT' },
|
|
19
|
+
* // { table: 'vouchers v', on: 'v.orderitemid = oi.id', type: 'LEFT' }
|
|
20
|
+
* // ]
|
|
21
|
+
* // }
|
|
22
|
+
* // }
|
|
23
|
+
* //
|
|
24
|
+
* // Output:
|
|
25
|
+
* // AND (
|
|
26
|
+
* // p_voucher IS NULL OR p_voucher = 'all'
|
|
27
|
+
* // OR (p_voucher = 'with' AND EXISTS (
|
|
28
|
+
* // SELECT 1 FROM vouchers v
|
|
29
|
+
* // JOIN orderitems oi ON v.orderitemid = oi.id
|
|
30
|
+
* // WHERE oi.orderid = o.id
|
|
31
|
+
* // ))
|
|
32
|
+
* // OR (p_voucher = 'without' AND NOT EXISTS (...))
|
|
33
|
+
* // )
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateRelatedWhere(filter: FilterConfig, tableName: string, aliasMap?: Record<string, string>): string;
|
|
36
|
+
/**
|
|
37
|
+
* Generate counts query for related filter
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Output:
|
|
41
|
+
* // 'byVoucher', jsonb_build_object(
|
|
42
|
+
* // 'with', (SELECT COUNT(*)::int FROM filtered_items WHERE EXISTS (...)),
|
|
43
|
+
* // 'without', (SELECT COUNT(*)::int FROM filtered_items WHERE NOT EXISTS (...))
|
|
44
|
+
* // )
|
|
45
|
+
*/
|
|
46
|
+
export declare function generateRelatedCounts(filter: FilterConfig, tableName: string, aliasMap?: Record<string, string>): string;
|
|
47
|
+
//# sourceMappingURL=related.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related.d.ts","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/related.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAGvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAcvH;AA8ED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAUxH"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Related Filter Template
|
|
3
|
+
* For filters checking relationship existence (voucher, production, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { getTableAlias } from '../utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for related filter
|
|
8
|
+
* Uses EXISTS/NOT EXISTS subqueries
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Input:
|
|
12
|
+
* // {
|
|
13
|
+
* // name: 'voucher',
|
|
14
|
+
* // relatedTable: 'orderitems',
|
|
15
|
+
* // foreignKey: 'orderid',
|
|
16
|
+
* // sqlGeneration: {
|
|
17
|
+
* // joins: [
|
|
18
|
+
* // { table: 'orderitems oi', on: 'oi.orderid = o.id', type: 'LEFT' },
|
|
19
|
+
* // { table: 'vouchers v', on: 'v.orderitemid = oi.id', type: 'LEFT' }
|
|
20
|
+
* // ]
|
|
21
|
+
* // }
|
|
22
|
+
* // }
|
|
23
|
+
* //
|
|
24
|
+
* // Output:
|
|
25
|
+
* // AND (
|
|
26
|
+
* // p_voucher IS NULL OR p_voucher = 'all'
|
|
27
|
+
* // OR (p_voucher = 'with' AND EXISTS (
|
|
28
|
+
* // SELECT 1 FROM vouchers v
|
|
29
|
+
* // JOIN orderitems oi ON v.orderitemid = oi.id
|
|
30
|
+
* // WHERE oi.orderid = o.id
|
|
31
|
+
* // ))
|
|
32
|
+
* // OR (p_voucher = 'without' AND NOT EXISTS (...))
|
|
33
|
+
* // )
|
|
34
|
+
*/
|
|
35
|
+
export function generateRelatedWhere(filter, tableName, aliasMap) {
|
|
36
|
+
const paramName = `p_${filter.rpcParam || filter.name}`;
|
|
37
|
+
const alias = getTableAlias(tableName, aliasMap);
|
|
38
|
+
// Check for custom joins configuration
|
|
39
|
+
const joins = filter.sqlGeneration?.joins;
|
|
40
|
+
if (joins && joins.length > 0) {
|
|
41
|
+
// Complex related filter with custom joins
|
|
42
|
+
return generateComplexRelatedWhere(filter, tableName, alias, paramName, joins);
|
|
43
|
+
}
|
|
44
|
+
// Simple related filter (single table JOIN)
|
|
45
|
+
return generateSimpleRelatedWhere(filter, alias, paramName);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generate simple related WHERE clause (single table)
|
|
49
|
+
* v3.11: Add public. prefix for Postgres v17 compatibility
|
|
50
|
+
*/
|
|
51
|
+
function generateSimpleRelatedWhere(filter, alias, paramName) {
|
|
52
|
+
const relatedTable = filter.relatedTable;
|
|
53
|
+
const foreignKey = filter.foreignKey;
|
|
54
|
+
return `
|
|
55
|
+
-- ${filter.name} (related filter - with/without)
|
|
56
|
+
AND (
|
|
57
|
+
${paramName} IS NULL OR ${paramName} = 'all'
|
|
58
|
+
OR (${paramName} = 'with' AND EXISTS (
|
|
59
|
+
SELECT 1 FROM public.${relatedTable}
|
|
60
|
+
WHERE ${foreignKey} = ${alias}.id
|
|
61
|
+
))
|
|
62
|
+
OR (${paramName} = 'without' AND NOT EXISTS (
|
|
63
|
+
SELECT 1 FROM public.${relatedTable}
|
|
64
|
+
WHERE ${foreignKey} = ${alias}.id
|
|
65
|
+
))
|
|
66
|
+
)`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate complex related WHERE clause (multiple JOINs)
|
|
70
|
+
* v3.11: Add public. prefix for Postgres v17 compatibility
|
|
71
|
+
*/
|
|
72
|
+
function generateComplexRelatedWhere(filter, _tableName, _alias, paramName, joins) {
|
|
73
|
+
// Build JOIN chain - add public. prefix to table names
|
|
74
|
+
const firstJoin = joins[0];
|
|
75
|
+
const firstJoinTable = addPublicPrefix(firstJoin.table);
|
|
76
|
+
const remainingJoins = joins.slice(1);
|
|
77
|
+
const joinChain = remainingJoins
|
|
78
|
+
.map(join => ` ${join.type} JOIN ${addPublicPrefix(join.table)} ON ${join.on}`)
|
|
79
|
+
.join('\n');
|
|
80
|
+
return `
|
|
81
|
+
-- ${filter.name} (related filter - complex with joins)
|
|
82
|
+
AND (
|
|
83
|
+
${paramName} IS NULL OR ${paramName} = 'all'
|
|
84
|
+
OR (${paramName} = 'with' AND EXISTS (
|
|
85
|
+
SELECT 1 FROM ${firstJoinTable}
|
|
86
|
+
${joinChain}
|
|
87
|
+
WHERE ${firstJoin.on}
|
|
88
|
+
))
|
|
89
|
+
OR (${paramName} = 'without' AND NOT EXISTS (
|
|
90
|
+
SELECT 1 FROM ${firstJoinTable}
|
|
91
|
+
${joinChain}
|
|
92
|
+
WHERE ${firstJoin.on}
|
|
93
|
+
))
|
|
94
|
+
)`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Add public. prefix to table reference (handles "tablename alias" format)
|
|
98
|
+
*/
|
|
99
|
+
function addPublicPrefix(tableWithAlias) {
|
|
100
|
+
// Handle "tablename alias" format (e.g., "orderitems oi" → "public.orderitems oi")
|
|
101
|
+
const parts = tableWithAlias.trim().split(/\s+/);
|
|
102
|
+
if (parts.length >= 1) {
|
|
103
|
+
parts[0] = `public.${parts[0]}`;
|
|
104
|
+
}
|
|
105
|
+
return parts.join(' ');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Generate counts query for related filter
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* // Output:
|
|
112
|
+
* // 'byVoucher', jsonb_build_object(
|
|
113
|
+
* // 'with', (SELECT COUNT(*)::int FROM filtered_items WHERE EXISTS (...)),
|
|
114
|
+
* // 'without', (SELECT COUNT(*)::int FROM filtered_items WHERE NOT EXISTS (...))
|
|
115
|
+
* // )
|
|
116
|
+
*/
|
|
117
|
+
export function generateRelatedCounts(filter, tableName, aliasMap) {
|
|
118
|
+
const countsKey = filter.countsKey || `by${filter.name.charAt(0).toUpperCase()}${filter.name.slice(1)}`;
|
|
119
|
+
const alias = getTableAlias(tableName, aliasMap);
|
|
120
|
+
const joins = filter.sqlGeneration?.joins;
|
|
121
|
+
if (joins && joins.length > 0) {
|
|
122
|
+
return generateComplexRelatedCounts(filter, countsKey, alias, joins);
|
|
123
|
+
}
|
|
124
|
+
return generateSimpleRelatedCounts(filter, countsKey, alias);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Generate simple related counts (single table)
|
|
128
|
+
* v3.11: Add public. prefix for Postgres v17 compatibility
|
|
129
|
+
*/
|
|
130
|
+
function generateSimpleRelatedCounts(filter, countsKey, alias) {
|
|
131
|
+
const relatedTable = filter.relatedTable;
|
|
132
|
+
const foreignKey = filter.foreignKey;
|
|
133
|
+
return `
|
|
134
|
+
'${countsKey}', jsonb_build_object(
|
|
135
|
+
'with', (
|
|
136
|
+
SELECT COUNT(*)::int FROM filtered_items ${alias}
|
|
137
|
+
WHERE EXISTS (
|
|
138
|
+
SELECT 1 FROM public.${relatedTable}
|
|
139
|
+
WHERE ${foreignKey} = ${alias}.id
|
|
140
|
+
)
|
|
141
|
+
),
|
|
142
|
+
'without', (
|
|
143
|
+
SELECT COUNT(*)::int FROM filtered_items ${alias}
|
|
144
|
+
WHERE NOT EXISTS (
|
|
145
|
+
SELECT 1 FROM public.${relatedTable}
|
|
146
|
+
WHERE ${foreignKey} = ${alias}.id
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
)`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Generate complex related counts (multiple JOINs)
|
|
153
|
+
* v3.11: Add public. prefix for Postgres v17 compatibility
|
|
154
|
+
*/
|
|
155
|
+
function generateComplexRelatedCounts(filter, countsKey, alias, joins) {
|
|
156
|
+
const firstJoin = joins[0];
|
|
157
|
+
const firstJoinTable = addPublicPrefix(firstJoin.table);
|
|
158
|
+
const remainingJoins = joins.slice(1);
|
|
159
|
+
const joinChain = remainingJoins
|
|
160
|
+
.map(join => ` ${join.type} JOIN ${addPublicPrefix(join.table)} ON ${join.on}`)
|
|
161
|
+
.join('\n');
|
|
162
|
+
return `
|
|
163
|
+
'${countsKey}', jsonb_build_object(
|
|
164
|
+
'with', (
|
|
165
|
+
SELECT COUNT(*)::int FROM filtered_items ${alias}
|
|
166
|
+
WHERE EXISTS (
|
|
167
|
+
SELECT 1 FROM ${firstJoinTable}
|
|
168
|
+
${joinChain}
|
|
169
|
+
WHERE ${firstJoin.on}
|
|
170
|
+
)
|
|
171
|
+
),
|
|
172
|
+
'without', (
|
|
173
|
+
SELECT COUNT(*)::int FROM filtered_items ${alias}
|
|
174
|
+
WHERE NOT EXISTS (
|
|
175
|
+
SELECT 1 FROM ${firstJoinTable}
|
|
176
|
+
${joinChain}
|
|
177
|
+
WHERE ${firstJoin.on}
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
)`;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=related.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related.js","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/related.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Filter Template
|
|
3
|
+
* For multi-field text search filters
|
|
4
|
+
*/
|
|
5
|
+
import { FilterConfig } from '../../../shared/types/feature-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for search filter
|
|
8
|
+
* Uses ILIKE for case-insensitive search + pg_trgm for fuzzy matching
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - ILIKE: Fast wildcard search (e.g., 'test' finds 'Test Product')
|
|
12
|
+
* - pg_trgm similarity: Fuzzy matching (e.g., 'tst' finds 'test')
|
|
13
|
+
* - Threshold: 0.2 for trigram similarity (lower = more fuzzy)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Input:
|
|
17
|
+
* // {
|
|
18
|
+
* // name: 'search',
|
|
19
|
+
* // sqlGeneration: {
|
|
20
|
+
* // searchFields: ['reference', 'first_name', 'last_name', 'user_email']
|
|
21
|
+
* // }
|
|
22
|
+
* // }
|
|
23
|
+
* //
|
|
24
|
+
* // Output:
|
|
25
|
+
* // AND (
|
|
26
|
+
* // p_search IS NULL OR p_search = '' OR p_search = 'all'
|
|
27
|
+
* // OR (
|
|
28
|
+
* // o.reference ILIKE '%' || p_search || '%'
|
|
29
|
+
* // OR similarity(o.reference, p_search) > 0.2
|
|
30
|
+
* // OR o.first_name ILIKE '%' || p_search || '%'
|
|
31
|
+
* // OR similarity(o.first_name, p_search) > 0.2
|
|
32
|
+
* // ...
|
|
33
|
+
* // )
|
|
34
|
+
* // )
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateSearchWhere(filter: FilterConfig, tableName: string, aliasMap?: Record<string, string>): string;
|
|
37
|
+
/**
|
|
38
|
+
* Search filters don't have counts (no aggregation needed)
|
|
39
|
+
* Returns empty string
|
|
40
|
+
*/
|
|
41
|
+
export declare function generateSearchCounts(_filter: FilterConfig): string;
|
|
42
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAGvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAwCtH;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAElE"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Filter Template
|
|
3
|
+
* For multi-field text search filters
|
|
4
|
+
*/
|
|
5
|
+
import { escapeIdentifier, getTableAlias } from '../utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for search filter
|
|
8
|
+
* Uses ILIKE for case-insensitive search + pg_trgm for fuzzy matching
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - ILIKE: Fast wildcard search (e.g., 'test' finds 'Test Product')
|
|
12
|
+
* - pg_trgm similarity: Fuzzy matching (e.g., 'tst' finds 'test')
|
|
13
|
+
* - Threshold: 0.2 for trigram similarity (lower = more fuzzy)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Input:
|
|
17
|
+
* // {
|
|
18
|
+
* // name: 'search',
|
|
19
|
+
* // sqlGeneration: {
|
|
20
|
+
* // searchFields: ['reference', 'first_name', 'last_name', 'user_email']
|
|
21
|
+
* // }
|
|
22
|
+
* // }
|
|
23
|
+
* //
|
|
24
|
+
* // Output:
|
|
25
|
+
* // AND (
|
|
26
|
+
* // p_search IS NULL OR p_search = '' OR p_search = 'all'
|
|
27
|
+
* // OR (
|
|
28
|
+
* // o.reference ILIKE '%' || p_search || '%'
|
|
29
|
+
* // OR similarity(o.reference, p_search) > 0.2
|
|
30
|
+
* // OR o.first_name ILIKE '%' || p_search || '%'
|
|
31
|
+
* // OR similarity(o.first_name, p_search) > 0.2
|
|
32
|
+
* // ...
|
|
33
|
+
* // )
|
|
34
|
+
* // )
|
|
35
|
+
*/
|
|
36
|
+
export function generateSearchWhere(filter, tableName, aliasMap) {
|
|
37
|
+
const paramName = `p_${filter.rpcParam || filter.name}`;
|
|
38
|
+
const alias = getTableAlias(tableName, aliasMap);
|
|
39
|
+
// Get search fields from sqlGeneration config
|
|
40
|
+
const searchFields = filter.sqlGeneration?.searchFields;
|
|
41
|
+
if (!searchFields || searchFields.length === 0) {
|
|
42
|
+
throw new Error(`Search filter "${filter.name}" missing searchFields in sqlGeneration config`);
|
|
43
|
+
}
|
|
44
|
+
// Generate ILIKE + similarity conditions for each field
|
|
45
|
+
// Support format: 'field' or 'relation.field' (e.g., 'user.firstname')
|
|
46
|
+
// v3.11: Use extensions.similarity() for Postgres v17 compatibility (pg_trgm is in extensions schema)
|
|
47
|
+
const searchCond = searchFields
|
|
48
|
+
.map(field => {
|
|
49
|
+
if (field.includes('.')) {
|
|
50
|
+
// Relational field (e.g., 'user.firstname')
|
|
51
|
+
const [relation, relField] = field.split('.');
|
|
52
|
+
const relAlias = relation.substring(0, 1); // 'u' for 'user'
|
|
53
|
+
const escapedField = escapeIdentifier(relField);
|
|
54
|
+
// Both ILIKE (exact substring) AND fuzzy similarity
|
|
55
|
+
return ` ${relAlias}.${escapedField} ILIKE '%' || ${paramName} || '%'\n OR extensions.similarity(${relAlias}.${escapedField}, ${paramName}) > 0.2`;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Direct field on main table
|
|
59
|
+
const escapedField = escapeIdentifier(field);
|
|
60
|
+
// Both ILIKE (exact substring) AND fuzzy similarity
|
|
61
|
+
return ` ${alias}.${escapedField} ILIKE '%' || ${paramName} || '%'\n OR extensions.similarity(${alias}.${escapedField}, ${paramName}) > 0.2`;
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.join('\n OR ');
|
|
65
|
+
return `
|
|
66
|
+
-- ${filter.name} (search filter - ILIKE + fuzzy matching with pg_trgm)
|
|
67
|
+
AND (
|
|
68
|
+
${paramName} IS NULL OR ${paramName} = '' OR ${paramName} = 'all'
|
|
69
|
+
OR (
|
|
70
|
+
${searchCond}
|
|
71
|
+
)
|
|
72
|
+
)`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Search filters don't have counts (no aggregation needed)
|
|
76
|
+
* Returns empty string
|
|
77
|
+
*/
|
|
78
|
+
export function generateSearchCounts(_filter) {
|
|
79
|
+
return ''; // Search filters don't generate counts
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAoB,EAAE,SAAiB,EAAE,QAAiC;IAC5G,MAAM,SAAS,GAAG,KAAK,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IACxD,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEjD,8CAA8C;IAC9C,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC;IAExD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,CAAC,IAAI,gDAAgD,CAAC,CAAC;IACjG,CAAC;IAED,wDAAwD;IACxD,uEAAuE;IACvE,sGAAsG;IACtG,MAAM,UAAU,GAAG,YAAY;SAC5B,GAAG,CAAC,KAAK,CAAC,EAAE;QACX,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,4CAA4C;YAC5C,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB;YAC5D,MAAM,YAAY,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAChD,oDAAoD;YACpD,OAAO,WAAW,QAAQ,IAAI,YAAY,iBAAiB,SAAS,6CAA6C,QAAQ,IAAI,YAAY,KAAK,SAAS,SAAS,CAAC;QACnK,CAAC;aAAM,CAAC;YACN,6BAA6B;YAC7B,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAC7C,oDAAoD;YACpD,OAAO,WAAW,KAAK,IAAI,YAAY,iBAAiB,SAAS,6CAA6C,KAAK,IAAI,YAAY,KAAK,SAAS,SAAS,CAAC;QAC7J,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,eAAe,CAAC,CAAC;IAEzB,OAAO;WACE,MAAM,CAAC,IAAI;;UAEZ,SAAS,eAAe,SAAS,YAAY,SAAS;;EAE9D,UAAU;;QAEJ,CAAC;AACT,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,OAAO,EAAE,CAAC,CAAC,uCAAuC;AACpD,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Filter Template
|
|
3
|
+
* For filters based on date ranges (this-week, this-month, older)
|
|
4
|
+
*/
|
|
5
|
+
import { FilterConfig } from '../../../shared/types/feature-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate WHERE clause for time filter
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Input:
|
|
11
|
+
* // {
|
|
12
|
+
* // name: 'time_period',
|
|
13
|
+
* // dateField: 'order_date',
|
|
14
|
+
* // sqlGeneration: {
|
|
15
|
+
* // timePeriods: {
|
|
16
|
+
* // thisWeek: { interval: '7 days', operator: '>=' },
|
|
17
|
+
* // thisMonth: { interval: '30 days', operator: '>=' },
|
|
18
|
+
* // older: { interval: '30 days', operator: '<' }
|
|
19
|
+
* // }
|
|
20
|
+
* // }
|
|
21
|
+
* // }
|
|
22
|
+
* //
|
|
23
|
+
* // Output:
|
|
24
|
+
* // AND (
|
|
25
|
+
* // p_time_period IS NULL OR p_time_period = 'all'
|
|
26
|
+
* // OR (p_time_period = 'thisWeek' AND order_date >= CURRENT_DATE - INTERVAL '7 days')
|
|
27
|
+
* // OR (p_time_period = 'thisMonth' AND order_date >= CURRENT_DATE - INTERVAL '30 days')
|
|
28
|
+
* // OR (p_time_period = 'older' AND order_date < CURRENT_DATE - INTERVAL '30 days')
|
|
29
|
+
* // )
|
|
30
|
+
*/
|
|
31
|
+
export declare function generateTimeWhere(filter: FilterConfig, tableName: string, aliasMap?: Record<string, string>): string;
|
|
32
|
+
/**
|
|
33
|
+
* Generate counts query for time filter
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Output:
|
|
37
|
+
* // 'byTimePeriod', jsonb_build_object(
|
|
38
|
+
* // 'thisWeek', (SELECT COUNT(*)::int FROM filtered_items WHERE order_date >= CURRENT_DATE - INTERVAL '7 days'),
|
|
39
|
+
* // 'thisMonth', (SELECT COUNT(*)::int FROM filtered_items WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'),
|
|
40
|
+
* // 'older', (SELECT COUNT(*)::int FROM filtered_items WHERE order_date < CURRENT_DATE - INTERVAL '30 days')
|
|
41
|
+
* // )
|
|
42
|
+
*/
|
|
43
|
+
export declare function generateTimeCounts(filter: FilterConfig): string;
|
|
44
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../../src/generators/rpc/templates/time.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAGvE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAcpH;AAgDD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAa/D"}
|