@soulbatical/tetra-core 0.1.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.
Potentially problematic release.
This version of @soulbatical/tetra-core might be problematic. Click here for more details.
- package/dist/core/SupabaseUserClient.d.ts +14 -0
- package/dist/core/SupabaseUserClient.d.ts.map +1 -0
- package/dist/core/SupabaseUserClient.js +49 -0
- package/dist/core/SupabaseUserClient.js.map +1 -0
- package/dist/core/adminDb.d.ts +21 -0
- package/dist/core/adminDb.d.ts.map +1 -0
- package/dist/core/adminDb.js +43 -0
- package/dist/core/adminDb.js.map +1 -0
- package/dist/core/publicDb.d.ts +19 -0
- package/dist/core/publicDb.d.ts.map +1 -0
- package/dist/core/publicDb.js +29 -0
- package/dist/core/publicDb.js.map +1 -0
- package/dist/core/superadminDb.d.ts +29 -0
- package/dist/core/superadminDb.d.ts.map +1 -0
- package/dist/core/superadminDb.js +53 -0
- package/dist/core/superadminDb.js.map +1 -0
- package/dist/core/systemDb.d.ts +34 -0
- package/dist/core/systemDb.d.ts.map +1 -0
- package/dist/core/systemDb.js +64 -0
- package/dist/core/systemDb.js.map +1 -0
- package/dist/core/userDb.d.ts +22 -0
- package/dist/core/userDb.d.ts.map +1 -0
- package/dist/core/userDb.js +36 -0
- package/dist/core/userDb.js.map +1 -0
- package/dist/core/webhookDb.d.ts +16 -0
- package/dist/core/webhookDb.d.ts.map +1 -0
- package/dist/core/webhookDb.js +26 -0
- package/dist/core/webhookDb.js.map +1 -0
- package/dist/frontend/components/FeatureFilters.d.ts +81 -0
- package/dist/frontend/components/FeatureFilters.d.ts.map +1 -0
- package/dist/frontend/components/FeatureFilters.js +257 -0
- package/dist/frontend/components/FeatureFilters.js.map +1 -0
- package/dist/frontend/components/FeatureTable.d.ts +96 -0
- package/dist/frontend/components/FeatureTable.d.ts.map +1 -0
- package/dist/frontend/components/FeatureTable.js +279 -0
- package/dist/frontend/components/FeatureTable.js.map +1 -0
- package/dist/frontend/hooks/useFeature.d.ts +108 -0
- package/dist/frontend/hooks/useFeature.d.ts.map +1 -0
- package/dist/frontend/hooks/useFeature.js +354 -0
- package/dist/frontend/hooks/useFeature.js.map +1 -0
- package/dist/frontend/index.d.ts +17 -0
- package/dist/frontend/index.d.ts.map +1 -0
- package/dist/frontend/index.js +14 -0
- package/dist/frontend/index.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/authMiddleware.d.ts +137 -0
- package/dist/middleware/authMiddleware.d.ts.map +1 -0
- package/dist/middleware/authMiddleware.js +292 -0
- package/dist/middleware/authMiddleware.js.map +1 -0
- package/dist/middleware/autoRegisterValidators.d.ts +31 -0
- package/dist/middleware/autoRegisterValidators.d.ts.map +1 -0
- package/dist/middleware/autoRegisterValidators.js +125 -0
- package/dist/middleware/autoRegisterValidators.js.map +1 -0
- package/dist/middleware/validateQueryParams.d.ts +62 -0
- package/dist/middleware/validateQueryParams.d.ts.map +1 -0
- package/dist/middleware/validateQueryParams.js +321 -0
- package/dist/middleware/validateQueryParams.js.map +1 -0
- package/dist/shared/controllers/BaseMutationController.d.ts +79 -0
- package/dist/shared/controllers/BaseMutationController.d.ts.map +1 -0
- package/dist/shared/controllers/BaseMutationController.js +241 -0
- package/dist/shared/controllers/BaseMutationController.js.map +1 -0
- package/dist/shared/controllers/BaseQueryController.d.ts +72 -0
- package/dist/shared/controllers/BaseQueryController.d.ts.map +1 -0
- package/dist/shared/controllers/BaseQueryController.js +375 -0
- package/dist/shared/controllers/BaseQueryController.js.map +1 -0
- package/dist/shared/controllers/FilterConfigController.d.ts +37 -0
- package/dist/shared/controllers/FilterConfigController.d.ts.map +1 -0
- package/dist/shared/controllers/FilterConfigController.js +94 -0
- package/dist/shared/controllers/FilterConfigController.js.map +1 -0
- package/dist/shared/controllers/index.d.ts +5 -0
- package/dist/shared/controllers/index.d.ts.map +1 -0
- package/dist/shared/controllers/index.js +4 -0
- package/dist/shared/controllers/index.js.map +1 -0
- package/dist/shared/controllers/types.d.ts +57 -0
- package/dist/shared/controllers/types.d.ts.map +1 -0
- package/dist/shared/controllers/types.js +2 -0
- package/dist/shared/controllers/types.js.map +1 -0
- package/dist/shared/factories/BatchRouteFactory.d.ts +33 -0
- package/dist/shared/factories/BatchRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/BatchRouteFactory.js +54 -0
- package/dist/shared/factories/BatchRouteFactory.js.map +1 -0
- package/dist/shared/factories/MutationRouteFactory.d.ts +27 -0
- package/dist/shared/factories/MutationRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/MutationRouteFactory.js +39 -0
- package/dist/shared/factories/MutationRouteFactory.js.map +1 -0
- package/dist/shared/factories/PhaseRouteFactory.d.ts +33 -0
- package/dist/shared/factories/PhaseRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/PhaseRouteFactory.js +67 -0
- package/dist/shared/factories/PhaseRouteFactory.js.map +1 -0
- package/dist/shared/factories/QueryRouteFactory.d.ts +37 -0
- package/dist/shared/factories/QueryRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/QueryRouteFactory.js +244 -0
- package/dist/shared/factories/QueryRouteFactory.js.map +1 -0
- package/dist/shared/factories/QueryServiceFactory.d.ts +282 -0
- package/dist/shared/factories/QueryServiceFactory.d.ts.map +1 -0
- package/dist/shared/factories/QueryServiceFactory.js +277 -0
- package/dist/shared/factories/QueryServiceFactory.js.map +1 -0
- package/dist/shared/factories/index.d.ts +8 -0
- package/dist/shared/factories/index.d.ts.map +1 -0
- package/dist/shared/factories/index.js +6 -0
- package/dist/shared/factories/index.js.map +1 -0
- package/dist/shared/factories/types.d.ts +98 -0
- package/dist/shared/factories/types.d.ts.map +1 -0
- package/dist/shared/factories/types.js +8 -0
- package/dist/shared/factories/types.js.map +1 -0
- package/dist/shared/ownership/types.d.ts +84 -0
- package/dist/shared/ownership/types.d.ts.map +1 -0
- package/dist/shared/ownership/types.js +8 -0
- package/dist/shared/ownership/types.js.map +1 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts +54 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -0
- package/dist/shared/rfc7807ErrorResponse.js +98 -0
- package/dist/shared/rfc7807ErrorResponse.js.map +1 -0
- package/dist/shared/services/BaseCronService.d.ts +171 -0
- package/dist/shared/services/BaseCronService.d.ts.map +1 -0
- package/dist/shared/services/BaseCronService.js +444 -0
- package/dist/shared/services/BaseCronService.js.map +1 -0
- package/dist/shared/services/BasePhaseService.d.ts +170 -0
- package/dist/shared/services/BasePhaseService.d.ts.map +1 -0
- package/dist/shared/services/BasePhaseService.js +409 -0
- package/dist/shared/services/BasePhaseService.js.map +1 -0
- package/dist/shared/services/CronRegistry.d.ts +67 -0
- package/dist/shared/services/CronRegistry.d.ts.map +1 -0
- package/dist/shared/services/CronRegistry.js +68 -0
- package/dist/shared/services/CronRegistry.js.map +1 -0
- package/dist/shared/services/SimpleRPCQueryService.d.ts +111 -0
- package/dist/shared/services/SimpleRPCQueryService.d.ts.map +1 -0
- package/dist/shared/services/SimpleRPCQueryService.js +265 -0
- package/dist/shared/services/SimpleRPCQueryService.js.map +1 -0
- package/dist/shared/types/feature-config.d.ts +611 -0
- package/dist/shared/types/feature-config.d.ts.map +1 -0
- package/dist/shared/types/feature-config.js +85 -0
- package/dist/shared/types/feature-config.js.map +1 -0
- package/dist/shared/types/query-config.d.ts +41 -0
- package/dist/shared/types/query-config.d.ts.map +1 -0
- package/dist/shared/types/query-config.js +6 -0
- package/dist/shared/types/query-config.js.map +1 -0
- package/dist/shared/utils/config-driven-filters.d.ts +215 -0
- package/dist/shared/utils/config-driven-filters.d.ts.map +1 -0
- package/dist/shared/utils/config-driven-filters.js +451 -0
- package/dist/shared/utils/config-driven-filters.js.map +1 -0
- package/dist/shared/utils/controllerErrorHandler.d.ts +44 -0
- package/dist/shared/utils/controllerErrorHandler.d.ts.map +1 -0
- package/dist/shared/utils/controllerErrorHandler.js +126 -0
- package/dist/shared/utils/controllerErrorHandler.js.map +1 -0
- package/dist/shared/utils/parseInclude.d.ts +14 -0
- package/dist/shared/utils/parseInclude.d.ts.map +1 -0
- package/dist/shared/utils/parseInclude.js +28 -0
- package/dist/shared/utils/parseInclude.js.map +1 -0
- package/dist/shared/utils/queryHelpers.d.ts +62 -0
- package/dist/shared/utils/queryHelpers.d.ts.map +1 -0
- package/dist/shared/utils/queryHelpers.js +61 -0
- package/dist/shared/utils/queryHelpers.js.map +1 -0
- package/dist/shared/utils/response-mapper.d.ts +42 -0
- package/dist/shared/utils/response-mapper.d.ts.map +1 -0
- package/dist/shared/utils/response-mapper.js +176 -0
- package/dist/shared/utils/response-mapper.js.map +1 -0
- package/dist/shared/utils/responseBuilder.d.ts +103 -0
- package/dist/shared/utils/responseBuilder.d.ts.map +1 -0
- package/dist/shared/utils/responseBuilder.js +131 -0
- package/dist/shared/utils/responseBuilder.js.map +1 -0
- package/dist/shared/validators/featureConfigValidator.d.ts +23 -0
- package/dist/shared/validators/featureConfigValidator.d.ts.map +1 -0
- package/dist/shared/validators/featureConfigValidator.js +143 -0
- package/dist/shared/validators/featureConfigValidator.js.map +1 -0
- package/dist/shared/validators/organizationValidator.d.ts +53 -0
- package/dist/shared/validators/organizationValidator.d.ts.map +1 -0
- package/dist/shared/validators/organizationValidator.js +69 -0
- package/dist/shared/validators/organizationValidator.js.map +1 -0
- package/dist/shared/validators/uuidValidator.d.ts +57 -0
- package/dist/shared/validators/uuidValidator.d.ts.map +1 -0
- package/dist/shared/validators/uuidValidator.js +77 -0
- package/dist/shared/validators/uuidValidator.js.map +1 -0
- package/dist/utils/logger.d.ts +40 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse ?include= query parameter
|
|
3
|
+
*
|
|
4
|
+
* Supports: ?include=booktemplate,humandesignchart,productionitem
|
|
5
|
+
* Returns array of include strings
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* parseIncludeParam('users,orders') // ['users', 'orders']
|
|
9
|
+
* parseIncludeParam(['users', 'orders']) // ['users', 'orders']
|
|
10
|
+
* parseIncludeParam(undefined) // []
|
|
11
|
+
*/
|
|
12
|
+
export function parseIncludeParam(include) {
|
|
13
|
+
if (!include)
|
|
14
|
+
return [];
|
|
15
|
+
// Handle array (already parsed by Express)
|
|
16
|
+
if (Array.isArray(include)) {
|
|
17
|
+
return include.filter((item) => typeof item === 'string');
|
|
18
|
+
}
|
|
19
|
+
// Handle comma-separated string
|
|
20
|
+
if (typeof include === 'string') {
|
|
21
|
+
return include
|
|
22
|
+
.split(',')
|
|
23
|
+
.map(s => s.trim())
|
|
24
|
+
.filter(s => s.length > 0);
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=parseInclude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseInclude.js","sourceRoot":"","sources":["../../../src/shared/utils/parseInclude.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAyE;IACzG,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,2CAA2C;IAC3C,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAa,CAAC;IACxF,CAAC;IAED,gCAAgC;IAChC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO;aACX,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
interface RequestWithValidatedQuery {
|
|
2
|
+
validatedQuery?: any;
|
|
3
|
+
query: any;
|
|
4
|
+
}
|
|
5
|
+
type AuthenticatedRequest = RequestWithValidatedQuery;
|
|
6
|
+
/**
|
|
7
|
+
* Query Parameter Extraction Utilities
|
|
8
|
+
*
|
|
9
|
+
* Provides consistent fallback between validatedQuery and query
|
|
10
|
+
* Prevents inconsistent query parameter handling across controllers
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Get query parameters with fallback to validated query
|
|
14
|
+
*
|
|
15
|
+
* Use this at the start of controller methods for consistent behavior
|
|
16
|
+
*
|
|
17
|
+
* @param req - Authenticated request
|
|
18
|
+
* @returns Query parameters (validated if available, otherwise raw)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const querySource = getValidatedQuery(req);
|
|
22
|
+
* const { limit = 50, offset = 0, search } = querySource;
|
|
23
|
+
*/
|
|
24
|
+
export declare function getValidatedQuery(req: AuthenticatedRequest): any;
|
|
25
|
+
/**
|
|
26
|
+
* Parse query parameters with defaults
|
|
27
|
+
*
|
|
28
|
+
* Merges default values with query parameters
|
|
29
|
+
*
|
|
30
|
+
* @param query - Query object (from req.query or req.validatedQuery)
|
|
31
|
+
* @param defaults - Default values for parameters
|
|
32
|
+
* @returns Merged parameters with defaults
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* const params = parseQueryParams(req.query, {
|
|
36
|
+
* limit: 50,
|
|
37
|
+
* offset: 0,
|
|
38
|
+
* sortBy: 'created_at'
|
|
39
|
+
* });
|
|
40
|
+
* // Returns: { limit: 50, offset: 0, sortBy: 'created_at', ...req.query }
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseQueryParams<T extends Record<string, any>>(query: any, defaults: T): T;
|
|
43
|
+
/**
|
|
44
|
+
* Parse pagination parameters from query
|
|
45
|
+
*
|
|
46
|
+
* Extracts and validates limit/offset with defaults
|
|
47
|
+
*
|
|
48
|
+
* @param query - Query object
|
|
49
|
+
* @param defaultLimit - Default limit (default: 50)
|
|
50
|
+
* @param maxLimit - Maximum allowed limit (default: 100)
|
|
51
|
+
* @returns Pagination parameters
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const { limit, offset } = parsePaginationParams(req.query);
|
|
55
|
+
* // Returns: { limit: 50, offset: 0 } (or values from query)
|
|
56
|
+
*/
|
|
57
|
+
export declare function parsePaginationParams(query: any, defaultLimit?: number, maxLimit?: number): {
|
|
58
|
+
limit: number;
|
|
59
|
+
offset: number;
|
|
60
|
+
};
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=queryHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryHelpers.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/queryHelpers.ts"],"names":[],"mappings":"AAAA,UAAU,yBAAyB;IACjC,cAAc,CAAC,EAAE,GAAG,CAAC;IACrB,KAAK,EAAE,GAAG,CAAC;CACZ;AAED,KAAK,oBAAoB,GAAG,yBAAyB,CAAC;AAEtD;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,oBAAoB,GAAG,GAAG,CAEhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5D,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,CAAC,GACV,CAAC,CAEH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,GAAG,EACV,YAAY,GAAE,MAAW,EACzB,QAAQ,GAAE,MAAY,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAQnC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Parameter Extraction Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent fallback between validatedQuery and query
|
|
5
|
+
* Prevents inconsistent query parameter handling across controllers
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Get query parameters with fallback to validated query
|
|
9
|
+
*
|
|
10
|
+
* Use this at the start of controller methods for consistent behavior
|
|
11
|
+
*
|
|
12
|
+
* @param req - Authenticated request
|
|
13
|
+
* @returns Query parameters (validated if available, otherwise raw)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const querySource = getValidatedQuery(req);
|
|
17
|
+
* const { limit = 50, offset = 0, search } = querySource;
|
|
18
|
+
*/
|
|
19
|
+
export function getValidatedQuery(req) {
|
|
20
|
+
return req.validatedQuery || req.query;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse query parameters with defaults
|
|
24
|
+
*
|
|
25
|
+
* Merges default values with query parameters
|
|
26
|
+
*
|
|
27
|
+
* @param query - Query object (from req.query or req.validatedQuery)
|
|
28
|
+
* @param defaults - Default values for parameters
|
|
29
|
+
* @returns Merged parameters with defaults
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const params = parseQueryParams(req.query, {
|
|
33
|
+
* limit: 50,
|
|
34
|
+
* offset: 0,
|
|
35
|
+
* sortBy: 'created_at'
|
|
36
|
+
* });
|
|
37
|
+
* // Returns: { limit: 50, offset: 0, sortBy: 'created_at', ...req.query }
|
|
38
|
+
*/
|
|
39
|
+
export function parseQueryParams(query, defaults) {
|
|
40
|
+
return { ...defaults, ...query };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse pagination parameters from query
|
|
44
|
+
*
|
|
45
|
+
* Extracts and validates limit/offset with defaults
|
|
46
|
+
*
|
|
47
|
+
* @param query - Query object
|
|
48
|
+
* @param defaultLimit - Default limit (default: 50)
|
|
49
|
+
* @param maxLimit - Maximum allowed limit (default: 100)
|
|
50
|
+
* @returns Pagination parameters
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* const { limit, offset } = parsePaginationParams(req.query);
|
|
54
|
+
* // Returns: { limit: 50, offset: 0 } (or values from query)
|
|
55
|
+
*/
|
|
56
|
+
export function parsePaginationParams(query, defaultLimit = 50, maxLimit = 100) {
|
|
57
|
+
const limit = Math.min(parseInt(String(query.limit)) || defaultLimit, maxLimit);
|
|
58
|
+
const offset = parseInt(String(query.offset)) || 0;
|
|
59
|
+
return { limit, offset };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=queryHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryHelpers.js","sourceRoot":"","sources":["../../../src/shared/utils/queryHelpers.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAyB;IACzD,OAAO,GAAG,CAAC,cAAc,IAAI,GAAG,CAAC,KAAK,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAU,EACV,QAAW;IAEX,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAU,EACV,eAAuB,EAAE,EACzB,WAAmB,GAAG;IAEtB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,YAAY,EAC7C,QAAQ,CACT,CAAC;IACF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;IAEnD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* response-mapper.ts
|
|
3
|
+
*
|
|
4
|
+
* Generic response mapper for config-driven counts responses.
|
|
5
|
+
* Keeps RPC JSON structure nested - NO FLATTENING!
|
|
6
|
+
*
|
|
7
|
+
* ✅ Works for ALL features (orders, vouchers, products, etc.)
|
|
8
|
+
* ✅ 100% config-driven - no hardcoding
|
|
9
|
+
* ✅ Single source of truth
|
|
10
|
+
* ✅ Consistent nested format: byXxx: { option1: count1, option2: count2 }
|
|
11
|
+
*/
|
|
12
|
+
import { FeatureConfig } from '../types/feature-config.js';
|
|
13
|
+
/**
|
|
14
|
+
* Map RPC counts response to API response format
|
|
15
|
+
*
|
|
16
|
+
* ✨ ENFORCES POSTGRESQL STANDARD: snake_case keys in RPC (SQL industry standard)
|
|
17
|
+
*
|
|
18
|
+
* RPC MUST return snake_case keys, mapper transforms to option.value for frontend:
|
|
19
|
+
* - Enum filters: byStatus: { 'paid': 10, 'pending': 0 } → API: { paid: 10, pending: 0 }
|
|
20
|
+
* - Related filters: byVoucher: { 'with': 10, 'without': 0 } → API: { with: 10, without: 0 }
|
|
21
|
+
* - Time filters: byTimePeriod: { 'this_week': 10, 'this_month': 20 } → API: { 'this-week': 10, 'this-month': 20 }
|
|
22
|
+
*
|
|
23
|
+
* ✅ STRICT VALIDATION - Fails fast with clear error if RPC keys don't match expected format
|
|
24
|
+
*
|
|
25
|
+
* @param rpcData - Raw data from RPC function (snake_case keys)
|
|
26
|
+
* @param featureConfig - Feature configuration with filter definitions
|
|
27
|
+
* @returns Nested response object (option.value keys for frontend)
|
|
28
|
+
* @throws Error if RPC missing required counts or using wrong key format
|
|
29
|
+
*/
|
|
30
|
+
export declare function mapCountsFromRPC(rpcData: any, featureConfig: FeatureConfig): Record<string, any>;
|
|
31
|
+
/**
|
|
32
|
+
* Example usage:
|
|
33
|
+
*
|
|
34
|
+
* // In OrderQueryService:
|
|
35
|
+
* const rpcData = await this.supabase.rpc('get_order_counts_simple', rpcParams);
|
|
36
|
+
* return mapCountsFromRPC(rpcData, ordersFeatureConfig);
|
|
37
|
+
*
|
|
38
|
+
* // Works for ANY feature!
|
|
39
|
+
* const rpcData = await this.supabase.rpc('get_voucher_counts_simple', rpcParams);
|
|
40
|
+
* return mapCountsFromRPC(rpcData, vouchersFeatureConfig);
|
|
41
|
+
*/
|
|
42
|
+
//# sourceMappingURL=response-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-mapper.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/response-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAgB,MAAM,4BAA4B,CAAC;AA0BzE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,GAAG,EACZ,aAAa,EAAE,aAAa,GAC3B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAyIrB;AAED;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* response-mapper.ts
|
|
3
|
+
*
|
|
4
|
+
* Generic response mapper for config-driven counts responses.
|
|
5
|
+
* Keeps RPC JSON structure nested - NO FLATTENING!
|
|
6
|
+
*
|
|
7
|
+
* ✅ Works for ALL features (orders, vouchers, products, etc.)
|
|
8
|
+
* ✅ 100% config-driven - no hardcoding
|
|
9
|
+
* ✅ Single source of truth
|
|
10
|
+
* ✅ Consistent nested format: byXxx: { option1: count1, option2: count2 }
|
|
11
|
+
*/
|
|
12
|
+
import { createLogger } from '../../utils/logger.js';
|
|
13
|
+
const logger = createLogger('utils:response-mapper');
|
|
14
|
+
/**
|
|
15
|
+
* Capitalize first letter of string
|
|
16
|
+
*/
|
|
17
|
+
// Removed unused capitalize function
|
|
18
|
+
/**
|
|
19
|
+
* Convert option.value (kebab-case) to RPC key (snake_case)
|
|
20
|
+
*
|
|
21
|
+
* PostgreSQL Standard: Database functions use snake_case (industry standard for SQL)
|
|
22
|
+
* Config uses kebab-case for UI ('this-week'), RPC uses snake_case ('this_week')
|
|
23
|
+
*
|
|
24
|
+
* Examples:
|
|
25
|
+
* - 'this-week' → 'this_week'
|
|
26
|
+
* - 'with' → 'with' (no change)
|
|
27
|
+
* - 'paid' → 'paid' (no change)
|
|
28
|
+
*/
|
|
29
|
+
function optionValueToRpcKey(optionValue) {
|
|
30
|
+
return optionValue.replace(/-/g, '_');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Map RPC counts response to API response format
|
|
34
|
+
*
|
|
35
|
+
* ✨ ENFORCES POSTGRESQL STANDARD: snake_case keys in RPC (SQL industry standard)
|
|
36
|
+
*
|
|
37
|
+
* RPC MUST return snake_case keys, mapper transforms to option.value for frontend:
|
|
38
|
+
* - Enum filters: byStatus: { 'paid': 10, 'pending': 0 } → API: { paid: 10, pending: 0 }
|
|
39
|
+
* - Related filters: byVoucher: { 'with': 10, 'without': 0 } → API: { with: 10, without: 0 }
|
|
40
|
+
* - Time filters: byTimePeriod: { 'this_week': 10, 'this_month': 20 } → API: { 'this-week': 10, 'this-month': 20 }
|
|
41
|
+
*
|
|
42
|
+
* ✅ STRICT VALIDATION - Fails fast with clear error if RPC keys don't match expected format
|
|
43
|
+
*
|
|
44
|
+
* @param rpcData - Raw data from RPC function (snake_case keys)
|
|
45
|
+
* @param featureConfig - Feature configuration with filter definitions
|
|
46
|
+
* @returns Nested response object (option.value keys for frontend)
|
|
47
|
+
* @throws Error if RPC missing required counts or using wrong key format
|
|
48
|
+
*/
|
|
49
|
+
export function mapCountsFromRPC(rpcData, featureConfig) {
|
|
50
|
+
if (!rpcData) {
|
|
51
|
+
logger.warn('No RPC data provided to mapper');
|
|
52
|
+
return { total: 0 };
|
|
53
|
+
}
|
|
54
|
+
const result = {
|
|
55
|
+
total: rpcData.total || 0
|
|
56
|
+
};
|
|
57
|
+
logger.debug({
|
|
58
|
+
filterCount: featureConfig.filters.length,
|
|
59
|
+
rpcKeys: Object.keys(rpcData)
|
|
60
|
+
}, '🗺️ Mapping RPC response to API format');
|
|
61
|
+
// Process each filter in config
|
|
62
|
+
featureConfig.filters.forEach((filter) => {
|
|
63
|
+
const countsKey = filter.countsKey;
|
|
64
|
+
if (!countsKey) {
|
|
65
|
+
// Filter has no counts (e.g., search filter)
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const countsData = rpcData[countsKey];
|
|
69
|
+
// ✨ UNIVERSAL PATTERN - Works for ALL filter types!
|
|
70
|
+
// Initialize all options from config with 0, then merge RPC data
|
|
71
|
+
// RPC: { byXxx: { option1: 10 } }
|
|
72
|
+
// API: { byXxx: { option1: 10, option2: 0, option3: 0 } }
|
|
73
|
+
if (!filter.ui?.options) {
|
|
74
|
+
// No options defined - just copy data as-is
|
|
75
|
+
result[countsKey] = countsData || {};
|
|
76
|
+
logger.debug({ filter: filter.name, countsKey }, '✅ Mapped filter (no options)');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Initialize with 0 for all defined options
|
|
80
|
+
const initializedCounts = {};
|
|
81
|
+
filter.ui.options.forEach(option => {
|
|
82
|
+
// Skip 'all' option - it's not a real count
|
|
83
|
+
if (option.value === 'all')
|
|
84
|
+
return;
|
|
85
|
+
// ✅ KEEP EXACT MATCH - No transformation!
|
|
86
|
+
// AutoFilter does direct lookup: countsData[option.value]
|
|
87
|
+
// So keys MUST match option values exactly:
|
|
88
|
+
// 'this-week' → 'this-week', 'with' → 'with', 'paid' → 'paid'
|
|
89
|
+
const optionKey = option.value;
|
|
90
|
+
initializedCounts[optionKey] = 0;
|
|
91
|
+
});
|
|
92
|
+
// ✅ STRICT VALIDATION - Enforce industry standard (camelCase keys in JSON)
|
|
93
|
+
const transformedData = {};
|
|
94
|
+
if (countsData && typeof countsData === 'object') {
|
|
95
|
+
filter.ui.options.forEach(option => {
|
|
96
|
+
if (option.value === 'all')
|
|
97
|
+
return;
|
|
98
|
+
const optionKey = option.value; // e.g., 'this-week', 'paid', 'with', 'EN-GB'
|
|
99
|
+
const rpcKey = optionValueToRpcKey(optionKey); // e.g., 'this_week', 'paid', 'with', 'EN_GB'
|
|
100
|
+
// ✅ RPC can return snake_case OR original keys (for special values like ISO language codes)
|
|
101
|
+
// Try snake_case first (PostgreSQL standard), then fallback to original key
|
|
102
|
+
const actualRpcKey = countsData[rpcKey] !== undefined ? rpcKey : optionKey;
|
|
103
|
+
if (countsData[actualRpcKey] === undefined) {
|
|
104
|
+
// ❌ FAIL FAST - Missing count in RPC response!
|
|
105
|
+
throw new Error(`❌ RPC response missing count for filter option!\n` +
|
|
106
|
+
`Filter: "${filter.name}" (${filter.type})\n` +
|
|
107
|
+
`Expected counts key: "${countsKey}"\n` +
|
|
108
|
+
`Missing RPC key: "${rpcKey}" (for option "${optionKey}")\n` +
|
|
109
|
+
`RPC returned keys in ${countsKey}: ${Object.keys(countsData).join(', ')}\n` +
|
|
110
|
+
`\n` +
|
|
111
|
+
`💡 FIX: Update your RPC function to use snake_case keys (PostgreSQL standard):\n` +
|
|
112
|
+
` ${countsKey}: jsonb_build_object(\n` +
|
|
113
|
+
` ${filter.ui.options.filter(o => o.value !== 'all').map(o => `'${optionValueToRpcKey(o.value)}', COUNT(...)`).join(',\n ')}\n` +
|
|
114
|
+
` )\n` +
|
|
115
|
+
`\n` +
|
|
116
|
+
` Example for your filter:\n` +
|
|
117
|
+
` ${countsKey}: jsonb_build_object('${rpcKey}', COUNT(...))`);
|
|
118
|
+
}
|
|
119
|
+
// Store with UI key (option.value) for frontend consistency
|
|
120
|
+
transformedData[optionKey] = countsData[actualRpcKey];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else if (filter.type !== 'search') {
|
|
124
|
+
// ❌ FAIL FAST - RPC missing entire counts object!
|
|
125
|
+
throw new Error(`❌ RPC response missing counts object!\n` +
|
|
126
|
+
`Filter: "${filter.name}" (${filter.type})\n` +
|
|
127
|
+
`Expected counts key: "${countsKey}"\n` +
|
|
128
|
+
`RPC returned keys: ${Object.keys(rpcData).join(', ')}\n` +
|
|
129
|
+
`\n` +
|
|
130
|
+
`💡 FIX: Update your RPC function to return (camelCase keys - industry standard):\n` +
|
|
131
|
+
` ${countsKey}: jsonb_build_object(\n` +
|
|
132
|
+
` ${filter.ui.options.filter(o => o.value !== 'all').map(o => `'${optionValueToRpcKey(o.value)}', COUNT(...)`).join(',\n ')}\n` +
|
|
133
|
+
` )`);
|
|
134
|
+
}
|
|
135
|
+
// Merge transformed RPC data with initialized counts
|
|
136
|
+
result[countsKey] = { ...initializedCounts, ...transformedData };
|
|
137
|
+
logger.debug({
|
|
138
|
+
filter: filter.name,
|
|
139
|
+
type: filter.type,
|
|
140
|
+
countsKey,
|
|
141
|
+
optionsCount: Object.keys(initializedCounts).length
|
|
142
|
+
}, '✅ Mapped filter (nested, zero-initialized)');
|
|
143
|
+
});
|
|
144
|
+
// ✨ ALSO MAP STANDALONE COUNTS (not tied to filters)
|
|
145
|
+
// These are passive counts that are always returned (e.g., time-based counts)
|
|
146
|
+
if (featureConfig.counts) {
|
|
147
|
+
Object.keys(featureConfig.counts).forEach(countKey => {
|
|
148
|
+
// Skip if already mapped from filter or is 'total'
|
|
149
|
+
if (countKey === 'total' || result[countKey] !== undefined) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Copy standalone count directly from RPC response
|
|
153
|
+
if (rpcData[countKey] !== undefined) {
|
|
154
|
+
result[countKey] = rpcData[countKey];
|
|
155
|
+
logger.debug({ countKey, value: rpcData[countKey] }, '✅ Mapped standalone count');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
logger.debug({
|
|
160
|
+
resultKeys: Object.keys(result),
|
|
161
|
+
total: result.total
|
|
162
|
+
}, '✅ Response mapping complete');
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Example usage:
|
|
167
|
+
*
|
|
168
|
+
* // In OrderQueryService:
|
|
169
|
+
* const rpcData = await this.supabase.rpc('get_order_counts_simple', rpcParams);
|
|
170
|
+
* return mapCountsFromRPC(rpcData, ordersFeatureConfig);
|
|
171
|
+
*
|
|
172
|
+
* // Works for ANY feature!
|
|
173
|
+
* const rpcData = await this.supabase.rpc('get_voucher_counts_simple', rpcParams);
|
|
174
|
+
* return mapCountsFromRPC(rpcData, vouchersFeatureConfig);
|
|
175
|
+
*/
|
|
176
|
+
//# sourceMappingURL=response-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-mapper.js","sourceRoot":"","sources":["../../../src/shared/utils/response-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Response Builder Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent response format across all endpoints
|
|
5
|
+
* Prevents:
|
|
6
|
+
* - hasNextPage vs hasMore inconsistency
|
|
7
|
+
* - data: items vs data: { items } wrapping confusion
|
|
8
|
+
* - Missing/inconsistent pagination fields
|
|
9
|
+
* - Different message field usage
|
|
10
|
+
*/
|
|
11
|
+
export interface PaginationMeta {
|
|
12
|
+
total: number;
|
|
13
|
+
limit: number;
|
|
14
|
+
offset: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ResponseOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Wrap data in object with key
|
|
19
|
+
* @example { dataKey: 'books' } → data: { books: [...] }
|
|
20
|
+
* @example undefined → data: [...]
|
|
21
|
+
*/
|
|
22
|
+
dataKey?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Success message to include
|
|
25
|
+
* @example "Books retrieved successfully"
|
|
26
|
+
*/
|
|
27
|
+
message?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Include hasPreviousPage (default: true)
|
|
30
|
+
*/
|
|
31
|
+
includePreviousPage?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build paginated API response with consistent format
|
|
35
|
+
*
|
|
36
|
+
* Standard format:
|
|
37
|
+
* {
|
|
38
|
+
* success: true,
|
|
39
|
+
* data: [...],
|
|
40
|
+
* meta: {
|
|
41
|
+
* total: 100,
|
|
42
|
+
* limit: 20,
|
|
43
|
+
* offset: 0,
|
|
44
|
+
* hasMore: true,
|
|
45
|
+
* hasNextPage: true,
|
|
46
|
+
* hasPreviousPage: false
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* @param data - Array of items to return
|
|
51
|
+
* @param meta - Pagination metadata
|
|
52
|
+
* @param options - Optional customization
|
|
53
|
+
* @returns Standardized API response
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Simple response
|
|
57
|
+
* buildPaginatedResponse(orders, { total: 100, limit: 20, offset: 0 })
|
|
58
|
+
*
|
|
59
|
+
* // With wrapped data
|
|
60
|
+
* buildPaginatedResponse(
|
|
61
|
+
* books,
|
|
62
|
+
* { total: 50, limit: 10, offset: 0 },
|
|
63
|
+
* { dataKey: 'books', message: 'Books retrieved successfully' }
|
|
64
|
+
* )
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildPaginatedResponse<T>(data: T[], meta: PaginationMeta, options?: ResponseOptions): any;
|
|
67
|
+
/**
|
|
68
|
+
* Build single item response
|
|
69
|
+
*
|
|
70
|
+
* Standard format:
|
|
71
|
+
* {
|
|
72
|
+
* success: true,
|
|
73
|
+
* data: { ... }
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* @param data - Single item to return
|
|
77
|
+
* @param message - Optional success message
|
|
78
|
+
* @returns Standardized API response
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* buildSingleResponse(order)
|
|
82
|
+
* buildSingleResponse(book, 'Book retrieved successfully')
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildSingleResponse<T>(data: T, message?: string): any;
|
|
85
|
+
/**
|
|
86
|
+
* Build error response (for RFC 7807 compliance, use RFC7807ErrorResponse instead)
|
|
87
|
+
*
|
|
88
|
+
* Simple error format:
|
|
89
|
+
* {
|
|
90
|
+
* success: false,
|
|
91
|
+
* message: "Error message"
|
|
92
|
+
* }
|
|
93
|
+
*
|
|
94
|
+
* @param message - Error message
|
|
95
|
+
* @param details - Optional error details
|
|
96
|
+
* @returns Standardized error response
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* buildErrorResponse('Resource not found')
|
|
100
|
+
* buildErrorResponse('Validation failed', { field: 'email' })
|
|
101
|
+
*/
|
|
102
|
+
export declare function buildErrorResponse(message: string, details?: any): any;
|
|
103
|
+
//# sourceMappingURL=responseBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseBuilder.d.ts","sourceRoot":"","sources":["../../../src/shared/utils/responseBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,IAAI,EAAE,CAAC,EAAE,EACT,IAAI,EAAE,cAAc,EACpB,OAAO,CAAC,EAAE,eAAe,OAsC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,IAAI,EAAE,CAAC,EACP,OAAO,CAAC,EAAE,MAAM,OAYjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,GAAG,OAYd"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Response Builder Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent response format across all endpoints
|
|
5
|
+
* Prevents:
|
|
6
|
+
* - hasNextPage vs hasMore inconsistency
|
|
7
|
+
* - data: items vs data: { items } wrapping confusion
|
|
8
|
+
* - Missing/inconsistent pagination fields
|
|
9
|
+
* - Different message field usage
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Build paginated API response with consistent format
|
|
13
|
+
*
|
|
14
|
+
* Standard format:
|
|
15
|
+
* {
|
|
16
|
+
* success: true,
|
|
17
|
+
* data: [...],
|
|
18
|
+
* meta: {
|
|
19
|
+
* total: 100,
|
|
20
|
+
* limit: 20,
|
|
21
|
+
* offset: 0,
|
|
22
|
+
* hasMore: true,
|
|
23
|
+
* hasNextPage: true,
|
|
24
|
+
* hasPreviousPage: false
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* @param data - Array of items to return
|
|
29
|
+
* @param meta - Pagination metadata
|
|
30
|
+
* @param options - Optional customization
|
|
31
|
+
* @returns Standardized API response
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Simple response
|
|
35
|
+
* buildPaginatedResponse(orders, { total: 100, limit: 20, offset: 0 })
|
|
36
|
+
*
|
|
37
|
+
* // With wrapped data
|
|
38
|
+
* buildPaginatedResponse(
|
|
39
|
+
* books,
|
|
40
|
+
* { total: 50, limit: 10, offset: 0 },
|
|
41
|
+
* { dataKey: 'books', message: 'Books retrieved successfully' }
|
|
42
|
+
* )
|
|
43
|
+
*/
|
|
44
|
+
export function buildPaginatedResponse(data, meta, options) {
|
|
45
|
+
const hasMore = meta.offset + meta.limit < meta.total;
|
|
46
|
+
const hasPreviousPage = meta.offset > 0;
|
|
47
|
+
const response = {
|
|
48
|
+
success: true
|
|
49
|
+
};
|
|
50
|
+
// Add optional message
|
|
51
|
+
if (options?.message) {
|
|
52
|
+
response.message = options.message;
|
|
53
|
+
}
|
|
54
|
+
// Wrap data if dataKey provided
|
|
55
|
+
if (options?.dataKey) {
|
|
56
|
+
response.data = {
|
|
57
|
+
[options.dataKey]: data
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
response.data = data;
|
|
62
|
+
}
|
|
63
|
+
// Add pagination metadata
|
|
64
|
+
response.meta = {
|
|
65
|
+
total: meta.total,
|
|
66
|
+
limit: meta.limit,
|
|
67
|
+
offset: meta.offset,
|
|
68
|
+
hasMore,
|
|
69
|
+
hasNextPage: hasMore, // Include both for compatibility
|
|
70
|
+
};
|
|
71
|
+
// Add hasPreviousPage if requested (default: true)
|
|
72
|
+
if (options?.includePreviousPage !== false) {
|
|
73
|
+
response.meta.hasPreviousPage = hasPreviousPage;
|
|
74
|
+
}
|
|
75
|
+
return response;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build single item response
|
|
79
|
+
*
|
|
80
|
+
* Standard format:
|
|
81
|
+
* {
|
|
82
|
+
* success: true,
|
|
83
|
+
* data: { ... }
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* @param data - Single item to return
|
|
87
|
+
* @param message - Optional success message
|
|
88
|
+
* @returns Standardized API response
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* buildSingleResponse(order)
|
|
92
|
+
* buildSingleResponse(book, 'Book retrieved successfully')
|
|
93
|
+
*/
|
|
94
|
+
export function buildSingleResponse(data, message) {
|
|
95
|
+
const response = {
|
|
96
|
+
success: true,
|
|
97
|
+
data
|
|
98
|
+
};
|
|
99
|
+
if (message) {
|
|
100
|
+
response.message = message;
|
|
101
|
+
}
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build error response (for RFC 7807 compliance, use RFC7807ErrorResponse instead)
|
|
106
|
+
*
|
|
107
|
+
* Simple error format:
|
|
108
|
+
* {
|
|
109
|
+
* success: false,
|
|
110
|
+
* message: "Error message"
|
|
111
|
+
* }
|
|
112
|
+
*
|
|
113
|
+
* @param message - Error message
|
|
114
|
+
* @param details - Optional error details
|
|
115
|
+
* @returns Standardized error response
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* buildErrorResponse('Resource not found')
|
|
119
|
+
* buildErrorResponse('Validation failed', { field: 'email' })
|
|
120
|
+
*/
|
|
121
|
+
export function buildErrorResponse(message, details) {
|
|
122
|
+
const response = {
|
|
123
|
+
success: false,
|
|
124
|
+
message
|
|
125
|
+
};
|
|
126
|
+
if (details) {
|
|
127
|
+
response.details = details;
|
|
128
|
+
}
|
|
129
|
+
return response;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=responseBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseBuilder.js","sourceRoot":"","sources":["../../../src/shared/utils/responseBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA4BH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAS,EACT,IAAoB,EACpB,OAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACtD,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAExC,MAAM,QAAQ,GAAQ;QACpB,OAAO,EAAE,IAAI;KACd,CAAC;IAEF,uBAAuB;IACvB,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACrC,CAAC;IAED,gCAAgC;IAChC,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,GAAG;YACd,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI;SACxB,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,GAAG;QACd,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO;QACP,WAAW,EAAE,OAAO,EAAE,iCAAiC;KACxD,CAAC;IAEF,mDAAmD;IACnD,IAAI,OAAO,EAAE,mBAAmB,KAAK,KAAK,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IAClD,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAO,EACP,OAAgB;IAEhB,MAAM,QAAQ,GAAQ;QACpB,OAAO,EAAE,IAAI;QACb,IAAI;KACL,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACZ,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;IAC7B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAe,EACf,OAAa;IAEb,MAAM,QAAQ,GAAQ;QACpB,OAAO,EAAE,KAAK;QACd,OAAO;KACR,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACZ,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;IAC7B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Config Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates feature configurations at startup to catch common mistakes early.
|
|
5
|
+
* Runs during RouteManager initialization to fail fast with clear error messages.
|
|
6
|
+
*/
|
|
7
|
+
import { FeatureConfig } from '../types/feature-config.js';
|
|
8
|
+
/**
|
|
9
|
+
* Register known wrong table name mappings for validation.
|
|
10
|
+
* @example addWrongTableNames({ 'userspublic': 'users_public' });
|
|
11
|
+
*/
|
|
12
|
+
export declare function addWrongTableNames(mappings: Record<string, string>): void;
|
|
13
|
+
/**
|
|
14
|
+
* Validate a feature configuration
|
|
15
|
+
* @throws Error if validation fails (fail-fast approach)
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateFeatureConfig(config: FeatureConfig, featureName: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Validate all feature configs
|
|
20
|
+
* Call this during application startup
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateAllFeatureConfigs(configs: Record<string, FeatureConfig>): void;
|
|
23
|
+
//# sourceMappingURL=featureConfigValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"featureConfigValidator.d.ts","sourceRoot":"","sources":["../../../src/shared/validators/featureConfigValidator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAU3D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAEzE;AASD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAkHtF;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,IAAI,CAiBtF"}
|