vona-module-a-web 5.1.23 → 5.1.25

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.
@@ -1,10 +1,11 @@
1
+ import type { types as t } from '@babel/core';
1
2
  import type { IMetadataCustomGenerateOptions } from '@cabloy/cli';
2
3
 
3
4
  import { combineApiPathControllerAndActionRaw } from '@cabloy/utils';
4
5
  import { toUpperCaseFirstChar } from '@cabloy/word-utils';
5
6
 
6
7
  export default async function (options: IMetadataCustomGenerateOptions): Promise<string> {
7
- const { sceneName, moduleName, globFiles } = options;
8
+ const { sceneName, moduleName, globFiles, cli } = options;
8
9
  const contentResources: string[] = [];
9
10
  const contentImports: string[] = [];
10
11
  const contentActions: string[] = [];
@@ -22,11 +23,25 @@ export default async function (options: IMetadataCustomGenerateOptions): Promise
22
23
  export interface ${opionsName} {
23
24
  actions?: TypeControllerOptionsActions<${className}>;
24
25
  }`);
26
+ const file = cli.helper.gogocode(fileContent, {
27
+ parseOptions: { sourceType: 'module', plugins: ['typescript', 'decorators-legacy'] },
28
+ }).node as t.File;
29
+ const declaration = file.program.body
30
+ .map(statement =>
31
+ statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration'
32
+ ? statement.declaration
33
+ : statement,
34
+ )
35
+ .find(
36
+ (node): node is t.ClassDeclaration =>
37
+ node?.type === 'ClassDeclaration' && node.id?.name === className,
38
+ );
39
+ if (!declaration) continue;
25
40
  // controllerPath
26
- const controllerPath = __parseControllerPath(fileContent);
41
+ const controllerPath = __parseControllerPath(declaration);
27
42
  if (controllerPath === false) continue;
28
43
  // actionPath;
29
- const actionPaths = __parseActionPaths(fileContent);
44
+ const actionPaths = __parseActionPaths(declaration);
30
45
  for (const [method, actionPath] of actionPaths) {
31
46
  if (!contentPaths[method]) contentPaths[method] = [];
32
47
  const apiPath = __combineApiPath(moduleName, controllerPath, actionPath);
@@ -81,36 +96,65 @@ ${contentResources2}
81
96
  return content;
82
97
  }
83
98
 
84
- function __parseControllerPath(fileContent: string): string | false {
85
- let matched = fileContent.match(
86
- /@Controller<.*?>\(\{[\s\S]*?path: ('[^']*')[\s\S]*?\}[\s\S]*?\)\s*export class/,
87
- );
88
- if (!matched) {
89
- matched = fileContent.match(/@Controller<.*?>\(([^)]*)\)/);
99
+ function __parseControllerPath(declaration: t.ClassDeclaration): string | false {
100
+ for (const decorator of declaration.decorators ?? []) {
101
+ const call = decorator.expression;
102
+ if (
103
+ call.type !== 'CallExpression' ||
104
+ call.callee.type !== 'Identifier' ||
105
+ call.callee.name !== 'Controller'
106
+ ) {
107
+ continue;
108
+ }
109
+ const argument = call.arguments[0];
110
+ if (!argument) return '';
111
+ if (argument.type === 'StringLiteral') return argument.value;
112
+ if (argument.type !== 'ObjectExpression') return false;
113
+ // Read the last explicit path; a later spread/computed key makes it dynamic.
114
+ for (const property of argument.properties.toReversed()) {
115
+ if (property.type === 'SpreadElement' || property.computed) return false;
116
+ const key = property.key;
117
+ if (
118
+ (key.type === 'Identifier' && key.name === 'path') ||
119
+ (key.type === 'StringLiteral' && key.value === 'path')
120
+ ) {
121
+ return property.type === 'ObjectProperty' && property.value.type === 'StringLiteral'
122
+ ? property.value.value
123
+ : false;
124
+ }
125
+ }
126
+ return '';
90
127
  }
91
- if (!matched) return false;
92
- const controllerPath = matched[1];
93
- if (controllerPath === '') return '';
94
- return controllerPath.split(',')[0].replaceAll("'", '');
128
+ return false;
95
129
  }
96
130
 
97
- function __parseActionPaths(fileContent: string): [string, string][] {
131
+ function __parseActionPaths(declaration: t.ClassDeclaration): [string, string][] {
98
132
  const actionPaths: [string, string][] = [];
99
- const matches = fileContent.match(
100
- /(@Web\.get|@Web\.post|@Web\.delete|@Web\.put|@Web\.patch)\([\s\S]*?\)/g,
101
- );
102
- if (!matches) return [];
103
- for (const match of matches) {
104
- const matches2 = match.match(/@([^(]*)\(([\s\S]*?)\)/);
105
- if (!matches2) throw new Error('parse action path error');
106
- let actionPath = matches2[2];
107
- if (actionPath !== '' && !actionPath.startsWith("'")) continue; // exclude regexp
108
- if (actionPath.startsWith("'")) {
109
- const pos = actionPath.indexOf("'", 1);
110
- actionPath = actionPath.substring(1, pos);
133
+ for (const member of declaration.body.body) {
134
+ if (member.type !== 'ClassMethod') continue;
135
+ for (const decorator of member.decorators ?? []) {
136
+ const call = decorator.expression;
137
+ if (call.type !== 'CallExpression') continue;
138
+ const callee = call.callee;
139
+ if (
140
+ callee.type !== 'MemberExpression' ||
141
+ callee.computed ||
142
+ callee.object.type !== 'Identifier' ||
143
+ callee.object.name !== 'Web' ||
144
+ callee.property.type !== 'Identifier' ||
145
+ !['get', 'post', 'delete', 'put', 'patch'].includes(callee.property.name)
146
+ ) {
147
+ continue;
148
+ }
149
+ const argument = call.arguments[0];
150
+ // Options-only and no-argument decorators map to the root action route.
151
+ // RegExp and other dynamic expressions cannot supply static route metadata.
152
+ if (!argument || argument.type === 'ObjectExpression') {
153
+ actionPaths.push([toUpperCaseFirstChar(callee.property.name), '']);
154
+ } else if (argument.type === 'StringLiteral') {
155
+ actionPaths.push([toUpperCaseFirstChar(callee.property.name), argument.value]);
156
+ }
111
157
  }
112
- const method = toUpperCaseFirstChar(matches2[1].substring(4));
113
- actionPaths.push([method, actionPath]);
114
158
  }
115
159
  return actionPaths;
116
160
  }
@@ -1,3 +1,32 @@
1
+ /** filterTransform: begin */
2
+ export * from '../bean/filterTransform.base.ts';
3
+ export * from '../bean/filterTransform.dateRange.ts';
4
+ import type { IFilterTransformOptionsBase } from '../bean/filterTransform.base.ts';
5
+ import type { IFilterTransformOptionsDateRange } from '../bean/filterTransform.dateRange.ts';
6
+ import 'vona-module-a-web';
7
+ declare module 'vona-module-a-web' {
8
+ interface IFilterTransformRecord {
9
+ 'a-web:base': IFilterTransformOptionsBase;
10
+ 'a-web:dateRange': IFilterTransformOptionsDateRange;
11
+ }
12
+ }
13
+ declare module 'vona-module-a-web' {
14
+ interface FilterTransformBase {
15
+ }
16
+ interface FilterTransformBase {
17
+ get $beanFullName(): 'a-web.filterTransform.base';
18
+ get $onionName(): 'a-web:base';
19
+ get $onionOptions(): IFilterTransformOptionsBase;
20
+ }
21
+ interface FilterTransformDateRange {
22
+ }
23
+ interface FilterTransformDateRange {
24
+ get $beanFullName(): 'a-web.filterTransform.dateRange';
25
+ get $onionName(): 'a-web:dateRange';
26
+ get $onionOptions(): IFilterTransformOptionsDateRange;
27
+ }
28
+ }
29
+ /** filterTransform: end */
1
30
  /** pipe: begin */
2
31
  export * from '../bean/pipe.filter.ts';
3
32
  export * from '../bean/pipe.valid.ts';
@@ -120,35 +149,6 @@ declare module 'vona-module-a-web' {
120
149
  }
121
150
  }
122
151
  /** startup: end */
123
- /** filterTransform: begin */
124
- export * from '../bean/filterTransform.base.ts';
125
- export * from '../bean/filterTransform.dateRange.ts';
126
- import type { IFilterTransformOptionsBase } from '../bean/filterTransform.base.ts';
127
- import type { IFilterTransformOptionsDateRange } from '../bean/filterTransform.dateRange.ts';
128
- import 'vona-module-a-web';
129
- declare module 'vona-module-a-web' {
130
- interface IFilterTransformRecord {
131
- 'a-web:base': IFilterTransformOptionsBase;
132
- 'a-web:dateRange': IFilterTransformOptionsDateRange;
133
- }
134
- }
135
- declare module 'vona-module-a-web' {
136
- interface FilterTransformBase {
137
- }
138
- interface FilterTransformBase {
139
- get $beanFullName(): 'a-web.filterTransform.base';
140
- get $onionName(): 'a-web:base';
141
- get $onionOptions(): IFilterTransformOptionsBase;
142
- }
143
- interface FilterTransformDateRange {
144
- }
145
- interface FilterTransformDateRange {
146
- get $beanFullName(): 'a-web.filterTransform.dateRange';
147
- get $onionName(): 'a-web:dateRange';
148
- get $onionOptions(): IFilterTransformOptionsDateRange;
149
- }
150
- }
151
- /** filterTransform: end */
152
152
  /** config: begin */
153
153
  export * from '../config/config.ts';
154
154
  import type { config } from '../config/config.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/.metadata/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/.metadata/index.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,18 @@
1
1
  import type { IDecoratorPipeOptions, IDecoratorPipeOptionsArgument, IPipeTransform } from 'vona-module-a-aspect';
2
2
  import type { ITableQuery, RouteHandlerArgumentMeta } from 'vona-module-a-openapi';
3
+ import type { IAnalysisPolicy, TypeAnalysisMode } from 'vona-module-a-orm';
3
4
  import type { ValidatorOptions } from 'vona-module-a-validation';
4
5
  import { BeanBase } from 'vona';
5
6
  import type { TypeQueryParamsPatch } from '../types/filterTransform.ts';
6
7
  export type TypePipeFilterData = ITableQuery;
7
8
  export type TypePipeFilterResult = TypeQueryParamsPatch;
8
9
  export interface IPipeOptionsFilter extends IDecoratorPipeOptions, IDecoratorPipeOptionsArgument, ValidatorOptions {
10
+ analysis?: IAnalysisPolicy;
11
+ analysisMode?: TypeAnalysisMode;
9
12
  }
10
13
  export declare class PipeFilter extends BeanBase implements IPipeTransform<TypePipeFilterData, TypePipeFilterResult> {
11
14
  transform(value: TypePipeFilterData, metadata: RouteHandlerArgumentMeta, options: IPipeOptionsFilter): Promise<TypePipeFilterResult>;
15
+ private _prepareAnalysisControls;
12
16
  private _transform;
13
17
  private _transformSystem;
14
18
  private _transformOrders;
@@ -1 +1 @@
1
- {"version":3,"file":"pipe.filter.d.ts","sourceRoot":"","sources":["../../src/bean/pipe.filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAEV,WAAW,EACX,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAKjE,OAAO,EAAE,QAAQ,EAAmC,MAAM,MAAM,CAAC;AAGjE,OAAO,KAAK,EAGV,oBAAoB,EACrB,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAGxD,MAAM,WAAW,kBACf,SAAQ,qBAAqB,EAAE,6BAA6B,EAAE,gBAAgB;CAAG;AAInF,qBAOa,UACX,SAAQ,QACR,YAAW,cAAc,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;IAE7D,SAAS,CACb,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,wBAAwB,EAClC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;YAmBlB,UAAU;IAexB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,aAAa;IAyCrB,OAAO,CAAC,QAAQ;YAQF,eAAe;YAuDf,gBAAgB;CAW/B;AAED,eAAO,MAAM,YAAY,4DAAqC,CAAC"}
1
+ {"version":3,"file":"pipe.filter.d.ts","sourceRoot":"","sources":["../../src/bean/pipe.filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAEV,WAAW,EACX,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAKjE,OAAO,EAAE,QAAQ,EAA4D,MAAM,MAAM,CAAC;AAI1F,OAAO,KAAK,EAGV,oBAAoB,EACrB,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAGxD,MAAM,WAAW,kBACf,SAAQ,qBAAqB,EAAE,6BAA6B,EAAE,gBAAgB;IAC9E,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAKD,qBAOa,UACX,SAAQ,QACR,YAAW,cAAc,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;IAE7D,SAAS,CACb,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,wBAAwB,EAClC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAoBhC,OAAO,CAAC,wBAAwB;YA6BlB,UAAU;IAmBxB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,aAAa;IAyCrB,OAAO,CAAC,QAAQ;YAQF,eAAe;YAwDf,gBAAgB;CAW/B;AAED,eAAO,MAAM,YAAY,4DAAqC,CAAC"}
package/dist/index.js CHANGED
@@ -1,28 +1,117 @@
1
- import { BeanInfo, BeanBase, cast, beanFullNameFromOnionName, appMetadata, appResource, deepExtend, compose, createBeanDecorator, BeanSimple, BeanScopeBase, onionNameFromBeanFullName, useApp } from 'vona';
1
+ import { createBeanDecorator, BeanInfo, BeanBase, runWithZodQueryCoercion, cast, beanFullNameFromOnionName, appMetadata, appResource, deepExtend, compose, BeanSimple, BeanScopeBase, onionNameFromBeanFullName, useApp } from 'vona';
2
2
  import { __decorate } from 'tslib';
3
+ import { DateTime } from 'luxon';
4
+ import 'vona-module-a-web';
3
5
  import { isNil, isNilOrEmptyString } from '@cabloy/utils';
4
6
  import { ZodMetadata } from '@cabloy/zod-openapi';
5
7
  import { isNullableSchema } from '@cabloy/zod-query';
6
8
  import { Pipe, createArgumentPipe, SymbolCacheComposeGuards, SymbolCacheComposeInterceptors, SymbolCacheComposePipes, SymbolCacheComposeMiddlewares, clearCacheComposesRouter, SymbolCacheComposeMiddlewareSystems, setArgumentPipe } from 'vona-module-a-aspect';
9
+ import { analysisFailure, resolveAnalysis, analysisControlSchemas, analysisCapabilities } from 'vona-module-a-orm';
7
10
  import { Bean, Service, Scope } from 'vona-module-a-bean';
8
11
  import { SymbolUseOnionOptions, SymbolUseOnionOptionsRouteReal } from 'vona-module-a-onion';
9
- import { SymbolRouteHandlersArgumentsValue, SymbolRouteHandlersArgumentsMeta, makeSchemaLikes, $schema, SymbolOpenApiOptions, SymbolControllerResource, mergeDtoFieldsOpenapiMetadata, getTargetDecoratorDtoOpenapi, mergeTargetDecoratorDtoOpenapi, getTargetDecoratorDtoPipes, setTargetDecoratorDtoPipes } from 'vona-module-a-openapiutils';
12
+ import { SymbolRouteHandlersArgumentsValue, SymbolRouteHandlersArgumentsMeta, $schema, makeSchemaLikes, SymbolOpenApiOptions, SymbolControllerResource, mergeDtoFieldsOpenapiMetadata, getTargetDecoratorDtoOpenapi, mergeTargetDecoratorDtoOpenapi, getTargetDecoratorDtoPipes, setTargetDecoratorDtoPipes } from 'vona-module-a-openapiutils';
10
13
  import { SymbolUploadValue } from 'vona-module-a-upload';
11
14
  import { Hmr } from 'vona-module-a-hmrbase';
12
15
  import http from 'node:http';
13
16
  import { Startup } from 'vona-module-a-startup';
14
- import { DateTime } from 'luxon';
15
- import 'vona-module-a-web';
16
17
  import Router from 'find-my-way';
17
18
  import { SymbolRouterMiddleware } from 'vona-module-a-executor';
18
19
  import { z } from 'zod';
19
20
 
21
+ function FilterTransform(options) {
22
+ return createBeanDecorator('filterTransform', options);
23
+ }
24
+
25
+ let FilterTransformBase = class FilterTransformBase extends BeanBase {
26
+ async where(info, _options) {
27
+ const {
28
+ value,
29
+ type,
30
+ openapi
31
+ } = info;
32
+ let op = openapi?.filter?.op;
33
+ if (!op) {
34
+ if (type === 'string') {
35
+ op = '_includesI_';
36
+ } else {
37
+ op = '_eq_';
38
+ }
39
+ }
40
+ if (value === null) {
41
+ if (op === '_is_' || op === '_isNot_') {
42
+ return {
43
+ [op]: null
44
+ };
45
+ }
46
+ return null;
47
+ }
48
+ let where;
49
+ if (op === '_eq_') {
50
+ where = value;
51
+ } else {
52
+ where = {
53
+ [op]: value
54
+ };
55
+ }
56
+ return where;
57
+ }
58
+ };
59
+ FilterTransformBase = __decorate([FilterTransform(), BeanInfo({
60
+ module: "a-web"
61
+ })], FilterTransformBase);
62
+
63
+ let FilterTransformDateRange = class FilterTransformDateRange extends BeanBase {
64
+ async where(info, options) {
65
+ const {
66
+ value
67
+ } = info;
68
+ if (value === null) return null;
69
+ const [dateStartStr, dateEndStr] = value.split(options.separator);
70
+ if (!dateStartStr && !dateEndStr) return;
71
+ //
72
+ const where = {};
73
+ if (dateStartStr) {
74
+ const dateStart = this._parseDate(dateStartStr);
75
+ where._gte_ = dateStart.toJSDate();
76
+ }
77
+ if (dateEndStr) {
78
+ const dateEnd = this._parseDate(dateEndStr).plus({
79
+ day: 1
80
+ });
81
+ where._lt_ = dateEnd.toJSDate();
82
+ }
83
+ return where;
84
+ }
85
+ _parseDate(str) {
86
+ let date = DateTime.fromISO(str, {
87
+ zone: this.ctx.tz
88
+ });
89
+ if (!date.isValid) {
90
+ for (const format of ['yyyy/MM/dd', 'yyyy/MM/d', 'yyyy/M/dd', 'yyyy/M/d', 'MM/dd/yyyy', 'MM/d/yyyy', 'M/dd/yyyy', 'M/d/yyyy']) {
91
+ date = DateTime.fromFormat(str, format, {
92
+ zone: this.ctx.tz
93
+ });
94
+ if (date.isValid) break;
95
+ }
96
+ }
97
+ if (!date.isValid) throw new Error(`invalid date: ${str}`);
98
+ return date;
99
+ }
100
+ };
101
+ FilterTransformDateRange = __decorate([FilterTransform({
102
+ separator: '~'
103
+ }), BeanInfo({
104
+ module: "a-web"
105
+ })], FilterTransformDateRange);
106
+
20
107
  const __FieldsSystem = ['columns', 'where', 'orders', 'pageNo', 'pageSize'];
108
+ const __AnalysisControls = ['groups', 'aggrs', 'having', 'joins', 'distinct', 'include', 'with'];
21
109
  let PipeFilter = class PipeFilter extends BeanBase {
22
110
  async transform(value, metadata, options) {
23
111
  if (!options.schema) {
24
112
  throw new Error(`should specify the schema of pipeFilter: ${metadata.controller.name}.${metadata.method}#${metadata.index}`);
25
113
  }
114
+ value = this._prepareAnalysisControls(value, options);
26
115
  // validateSchema
27
116
  value = await this.bean.validator.validateSchema(options.schema, value, options, metadata.field);
28
117
  // transform
@@ -30,6 +119,34 @@ let PipeFilter = class PipeFilter extends BeanBase {
30
119
  // ok
31
120
  return params;
32
121
  }
122
+ _prepareAnalysisControls(value, options) {
123
+ if (!!options.analysis !== !!options.analysisMode) {
124
+ throw new Error('Analysis filter requires both a policy and an action mode');
125
+ }
126
+ if (!value || typeof value !== 'object') return value;
127
+ const prepared = {
128
+ ...value
129
+ };
130
+ for (const key of __AnalysisControls) {
131
+ if (!Object.hasOwn(value, key)) continue;
132
+ if (!options.analysisMode) analysisFailure(`Select does not support ${key}`);
133
+ if (key !== 'aggrs' && !(key === 'groups' && options.analysisMode === 'group')) {
134
+ analysisFailure(`Analysis ${options.analysisMode} does not support ${key}`);
135
+ }
136
+ if (typeof value[key] === 'string') {
137
+ try {
138
+ prepared[key] = JSON.parse(value[key]);
139
+ } catch {
140
+ analysisFailure(`Analysis ${key} must be valid JSON`);
141
+ }
142
+ }
143
+ }
144
+ if (options.analysis && options.analysisMode) {
145
+ // Validate native shapes before permissive generic query coercion runs.
146
+ runWithZodQueryCoercion(false, () => resolveAnalysis(options.analysis, options.analysisMode, prepared));
147
+ }
148
+ return prepared;
149
+ }
33
150
  async _transform(value, options) {
34
151
  // 1. system: columns/where/orders/pageNo/pageSize
35
152
  const params = this._transformSystem(value);
@@ -38,6 +155,10 @@ let PipeFilter = class PipeFilter extends BeanBase {
38
155
  await this._transformFields(params, value, options, rootOpenapi);
39
156
  // 3. system: orders
40
157
  this._transformOrders(params, options, rootOpenapi);
158
+ if (options.analysis && options.analysisMode) {
159
+ if (params.joins?.length) analysisFailure('Analysis does not support joined filters');
160
+ Object.assign(params, resolveAnalysis(options.analysis, options.analysisMode, value));
161
+ }
41
162
  // ok
42
163
  return params;
43
164
  }
@@ -124,7 +245,7 @@ let PipeFilter = class PipeFilter extends BeanBase {
124
245
  }
125
246
  }
126
247
  async _transformField(key, fieldValue, params, value, options, rootOpenapi) {
127
- if (__FieldsSystem.includes(key)) return;
248
+ if (__FieldsSystem.includes(key) || options.analysisMode && __AnalysisControls.includes(key)) return;
128
249
  const info = this._resolveField(key, options, rootOpenapi);
129
250
  if (!info) return;
130
251
  const {
@@ -644,92 +765,6 @@ StartupListen = __decorate([Startup({
644
765
  module: "a-web"
645
766
  })], StartupListen);
646
767
 
647
- function FilterTransform(options) {
648
- return createBeanDecorator('filterTransform', options);
649
- }
650
-
651
- let FilterTransformBase = class FilterTransformBase extends BeanBase {
652
- async where(info, _options) {
653
- const {
654
- value,
655
- type,
656
- openapi
657
- } = info;
658
- let op = openapi?.filter?.op;
659
- if (!op) {
660
- if (type === 'string') {
661
- op = '_includesI_';
662
- } else {
663
- op = '_eq_';
664
- }
665
- }
666
- if (value === null) {
667
- if (op === '_is_' || op === '_isNot_') {
668
- return {
669
- [op]: null
670
- };
671
- }
672
- return null;
673
- }
674
- let where;
675
- if (op === '_eq_') {
676
- where = value;
677
- } else {
678
- where = {
679
- [op]: value
680
- };
681
- }
682
- return where;
683
- }
684
- };
685
- FilterTransformBase = __decorate([FilterTransform(), BeanInfo({
686
- module: "a-web"
687
- })], FilterTransformBase);
688
-
689
- let FilterTransformDateRange = class FilterTransformDateRange extends BeanBase {
690
- async where(info, options) {
691
- const {
692
- value
693
- } = info;
694
- if (value === null) return null;
695
- const [dateStartStr, dateEndStr] = value.split(options.separator);
696
- if (!dateStartStr && !dateEndStr) return;
697
- //
698
- const where = {};
699
- if (dateStartStr) {
700
- const dateStart = this._parseDate(dateStartStr);
701
- where._gte_ = dateStart.toJSDate();
702
- }
703
- if (dateEndStr) {
704
- const dateEnd = this._parseDate(dateEndStr).plus({
705
- day: 1
706
- });
707
- where._lt_ = dateEnd.toJSDate();
708
- }
709
- return where;
710
- }
711
- _parseDate(str) {
712
- let date = DateTime.fromISO(str, {
713
- zone: this.ctx.tz
714
- });
715
- if (!date.isValid) {
716
- for (const format of ['yyyy/MM/dd', 'yyyy/MM/d', 'yyyy/M/dd', 'yyyy/M/d', 'MM/dd/yyyy', 'MM/d/yyyy', 'M/dd/yyyy', 'M/d/yyyy']) {
717
- date = DateTime.fromFormat(str, format, {
718
- zone: this.ctx.tz
719
- });
720
- if (date.isValid) break;
721
- }
722
- }
723
- if (!date.isValid) throw new Error(`invalid date: ${str}`);
724
- return date;
725
- }
726
- };
727
- FilterTransformDateRange = __decorate([FilterTransform({
728
- separator: '~'
729
- }), BeanInfo({
730
- module: "a-web"
731
- })], FilterTransformDateRange);
732
-
733
768
  function config(_app) {
734
769
  return {
735
770
  router: {
@@ -818,6 +853,57 @@ ScopeModuleAWeb = __decorate([Scope(), BeanInfo({
818
853
  })], ScopeModuleAWeb);
819
854
  /** scope: end */
820
855
 
856
+ function prepareAnalysisFilter(schemaLike, options) {
857
+ const schema = $schema(schemaLike);
858
+ if (!options?.mode) return {
859
+ schema
860
+ };
861
+ const policy = appResource.getBean(schemaLike)?.options?.analysis;
862
+ if (!policy) throw new Error('Analysis filter requires a DTO analysis policy');
863
+ return {
864
+ schema: createAnalysisFilterSchema(schema, policy, options.mode),
865
+ analysis: policy,
866
+ analysisMode: options.mode
867
+ };
868
+ }
869
+ function createAnalysisFilterSchema(schema, policy, mode) {
870
+ const inner = ZodMetadata.unwrapChained(schema);
871
+ if (inner.type !== 'object') throw new Error('Analysis filter requires an object DTO schema');
872
+ const object = schema;
873
+ if (typeof object.safeExtend !== 'function') {
874
+ throw new TypeError('Analysis filter requires an extensible object DTO schema');
875
+ }
876
+ for (const key of ['groups', 'aggrs', 'having', 'joins', 'distinct']) {
877
+ if (key in object.shape) throw new Error(`Analysis control conflicts with DTO field: ${key}`);
878
+ }
879
+ const controls = analysisControlSchemas(policy, mode);
880
+ const capabilities = analysisCapabilities(policy, mode);
881
+ const metadata = {
882
+ filter: {
883
+ capabilities: {
884
+ analysis: capabilities
885
+ }
886
+ }
887
+ };
888
+ const shape = {
889
+ aggrs: controls.aggrs.openapi(metadata)
890
+ };
891
+ if (controls.groups) shape.groups = controls.groups.openapi(metadata);
892
+ const derived = object.safeExtend(shape);
893
+ const original = ZodMetadata.getOpenapiMetadata(schema);
894
+ const ref = ZodMetadata.getRefId(schema);
895
+ return derived.openapi(`${ref ?? 'filter'}_${mode}`, {
896
+ ...original,
897
+ filter: {
898
+ ...original?.filter,
899
+ capabilities: {
900
+ ...original?.filter?.capabilities,
901
+ analysis: capabilities
902
+ }
903
+ }
904
+ });
905
+ }
906
+
821
907
  function createPipesArgumentDecorator(paramType, extractValue) {
822
908
  return function (field, ...schemaLikes) {
823
909
  return function (target, prop, index) {
@@ -880,12 +966,12 @@ function User(...schemaLikes) {
880
966
  function RbacScopeCurrent() {
881
967
  return createPipesArgumentDecorator('rbacScopeCurrent')();
882
968
  }
883
- function ArgFilter(schemaLike) {
969
+ function ArgFilter(schemaLike, options) {
884
970
  return function (target, prop, index) {
885
- const schema = $schema(schemaLike);
971
+ const prepared = prepareAnalysisFilter(schemaLike, options);
886
972
  setArgumentPipe('a-web:filter', {
887
973
  type: 'query',
888
- schema
974
+ ...prepared
889
975
  }, target, prop, index);
890
976
  };
891
977
  }
@@ -1044,5 +1130,5 @@ const Web = {
1044
1130
  head: Head
1045
1131
  };
1046
1132
 
1047
- export { Arg, ArgFilterPro, ArgValid, BeanRouter, Controller, Dto, FilterTransform, FilterTransformBase, FilterTransformDateRange, HmrController, HmrDto, Main, PipeFilter, PipeValid, RequestMapping, ScopeModuleAWeb, ServiceWeb, StartupListen, SymbolCacheControllerRoutes, SymbolRequestMappingHandler, Web, composeGuards, config, createPipesArgumentDecorator, getCacheControllerRoutes, mergeControllerActionsOpenapiMetadata, mergeDtoBlocksOpenapiMetadata, mergeDtoPipesOpenapiMetadata, recordResourceNameToRoutePath };
1133
+ export { Arg, ArgFilterPro, ArgValid, BeanRouter, Controller, Dto, FilterTransform, FilterTransformBase, FilterTransformDateRange, HmrController, HmrDto, Main, PipeFilter, PipeValid, RequestMapping, ScopeModuleAWeb, ServiceWeb, StartupListen, SymbolCacheControllerRoutes, SymbolRequestMappingHandler, Web, composeGuards, config, createPipesArgumentDecorator, getCacheControllerRoutes, mergeControllerActionsOpenapiMetadata, mergeDtoBlocksOpenapiMetadata, mergeDtoPipesOpenapiMetadata, prepareAnalysisFilter, recordResourceNameToRoutePath };
1048
1134
  //# sourceMappingURL=index.js.map