openxiangda-devkit-core 2.69.1 → 2.71.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.
@@ -2911,6 +2911,8 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
2911
2911
  const allowedAccessKeys = new Set([
2912
2912
  'directory',
2913
2913
  'selectedDepartments',
2914
+ 'directoryClasses',
2915
+ 'transactionTime',
2914
2916
  'managedFiles',
2915
2917
  'managedFileCopies',
2916
2918
  'notification',
@@ -2993,6 +2995,21 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
2993
2995
  fields.some(field => !allowedFields.has(string(field))) ||
2994
2996
  Object.keys(directory).some(key => !['mode', 'fields'].includes(key));
2995
2997
  }
2998
+ if (access.directoryClasses !== undefined) {
2999
+ const classes = object(access.directoryClasses);
3000
+ invalid ||= !isRecord(access.directoryClasses) ||
3001
+ !Number.isSafeInteger(classes.maxIds) ||
3002
+ Number(classes.maxIds) < 1 || Number(classes.maxIds) > 20 ||
3003
+ Object.keys(classes).some(key => key !== 'maxIds');
3004
+ }
3005
+ if (access.transactionTime !== undefined) {
3006
+ const time = object(access.transactionTime);
3007
+ invalid ||= !isRecord(access.transactionTime) ||
3008
+ time.mode !== 'database-accepted' ||
3009
+ object(access.dataCommands).mode !== 'recoverable-native' ||
3010
+ (time.recordSetAssertions !== undefined && time.recordSetAssertions !== true) ||
3011
+ Object.keys(time).some(key => !['mode', 'recordSetAssertions'].includes(key));
3012
+ }
2996
3013
  if (access.selectedDepartments !== undefined) {
2997
3014
  const departments = object(access.selectedDepartments);
2998
3015
  const fields = Array.isArray(departments.fields) ? departments.fields : [];
@@ -3710,6 +3727,7 @@ const ANONYMOUS_PUBLIC_OPERATIONS = new Set([
3710
3727
  'own.read',
3711
3728
  'public.list',
3712
3729
  'public.read',
3730
+ 'public.query',
3713
3731
  ]);
3714
3732
  function validateAnonymousPublicAccess(config, diagnostics) {
3715
3733
  const frontend = object(config.frontend);
@@ -3859,6 +3877,90 @@ function validateAnonymousPublicAccess(config, diagnostics) {
3859
3877
  childFields.some(field => !childResource.fields.has(field)) ||
3860
3878
  childFields.some(field => ['signature', 'subtable'].includes(childResource.fields.get(field) || '')));
3861
3879
  });
3880
+ const publicQueries = Array.isArray(policy.publicQueries)
3881
+ ? policy.publicQueries
3882
+ : [];
3883
+ const publicQueryInvalid = publicQueries.length > 16 ||
3884
+ new Set(publicQueries.map(item => string(object(item).code))).size !== publicQueries.length ||
3885
+ publicQueries.some(rawQuery => {
3886
+ const query = object(rawQuery);
3887
+ const queryFields = Array.isArray(query.fields)
3888
+ ? query.fields.map(string)
3889
+ : [];
3890
+ const sort = Array.isArray(query.sort) ? query.sort : [];
3891
+ const parameters = Array.isArray(query.parameters) ? query.parameters : [];
3892
+ const sortFields = sort.map(item => string(object(item).field));
3893
+ const parameterNames = parameters.map(item => string(object(item).name));
3894
+ const aggregate = query.aggregate === undefined ? undefined : object(query.aggregate);
3895
+ const dimensions = aggregate && Array.isArray(aggregate.dimensions) ? aggregate.dimensions : [];
3896
+ const measures = aggregate && Array.isArray(aggregate.measures) ? aggregate.measures : [];
3897
+ const aggregateOrder = aggregate && Array.isArray(aggregate.order) ? aggregate.order : [];
3898
+ const aggregateAliases = [
3899
+ ...dimensions.map(item => string(object(item).as)),
3900
+ ...measures.map(item => string(object(item).as)),
3901
+ ];
3902
+ const aggregateInvalid = query.aggregate !== undefined && (string(query.kind) !== 'aggregate' ||
3903
+ Object.keys(aggregate || {}).some(key => !['dimensions', 'measures', 'order'].includes(key)) ||
3904
+ !Array.isArray(aggregate?.measures) ||
3905
+ dimensions.length > 3 ||
3906
+ measures.length < 1 || measures.length > 8 ||
3907
+ new Set(aggregateAliases).size !== aggregateAliases.length ||
3908
+ aggregateAliases.some(alias => !/^[A-Za-z][A-Za-z0-9_]{0,62}$/.test(alias)) ||
3909
+ dimensions.some(item => {
3910
+ const value = object(item);
3911
+ return Object.keys(value).some(key => !['field', 'as', 'bucket'].includes(key)) ||
3912
+ !queryFields.includes(string(value.field)) ||
3913
+ !['day', 'week', 'month', 'quarter', 'year'].includes(string(value.bucket)) && value.bucket !== undefined;
3914
+ }) ||
3915
+ measures.some(item => {
3916
+ const value = object(item);
3917
+ const type = string(value.type);
3918
+ return Object.keys(value).some(key => !['type', 'field', 'as'].includes(key)) ||
3919
+ !['count', 'countDistinct', 'sum', 'avg', 'min', 'max'].includes(type) ||
3920
+ (type === 'count' ? value.field !== undefined : !queryFields.includes(string(value.field)));
3921
+ }) ||
3922
+ aggregateOrder.length > 10 ||
3923
+ new Set(aggregateOrder.map(item => string(object(item).field))).size !== aggregateOrder.length ||
3924
+ aggregateOrder.some(item => {
3925
+ const value = object(item);
3926
+ return Object.keys(value).some(key => !['field', 'direction', 'nulls'].includes(key)) ||
3927
+ !aggregateAliases.includes(string(value.field)) ||
3928
+ value.direction !== undefined && !['asc', 'desc'].includes(string(value.direction)) ||
3929
+ value.nulls !== undefined && !['first', 'last'].includes(string(value.nulls));
3930
+ }));
3931
+ return (Object.keys(query).some(key => !['code', 'kind', 'resourceCode', 'fields', 'defaultLimit', 'maxLimit', 'sort', 'parameters', 'aggregate'].includes(key)) ||
3932
+ !OPERATION_CODE_PATTERN.test(string(query.code)) ||
3933
+ !['records', 'aggregate'].includes(string(query.kind)) ||
3934
+ string(query.resourceCode) !== resourceCode ||
3935
+ queryFields.length < 1 ||
3936
+ queryFields.length > 64 ||
3937
+ new Set(queryFields).size !== queryFields.length ||
3938
+ queryFields.some(field => !fields.includes(field) || !Array.isArray(policy.publicRecordFields) || !policy.publicRecordFields.map(string).includes(field)) ||
3939
+ !Number.isSafeInteger(query.defaultLimit) ||
3940
+ !Number.isSafeInteger(query.maxLimit) ||
3941
+ Number(query.defaultLimit) < 1 ||
3942
+ Number(query.maxLimit) < Number(query.defaultLimit) ||
3943
+ Number(query.maxLimit) > 100 ||
3944
+ sort.length > 4 ||
3945
+ new Set(sortFields).size !== sortFields.length ||
3946
+ sort.some(item => {
3947
+ const value = object(item);
3948
+ return Object.keys(value).some(key => !['field', 'direction'].includes(key)) ||
3949
+ !queryFields.includes(string(value.field)) ||
3950
+ !['asc', 'desc'].includes(string(value.direction));
3951
+ }) ||
3952
+ parameters.length > 8 ||
3953
+ new Set(parameterNames).size !== parameterNames.length ||
3954
+ parameters.some(item => {
3955
+ const value = object(item);
3956
+ return Object.keys(value).some(key => !['name', 'field', 'operator', 'type'].includes(key)) ||
3957
+ !OPERATION_CODE_PATTERN.test(string(value.name)) ||
3958
+ !queryFields.includes(string(value.field)) ||
3959
+ !['eq', 'contains', 'in'].includes(string(value.operator)) ||
3960
+ !['string', 'number', 'boolean', 'string[]'].includes(string(value.type));
3961
+ }) ||
3962
+ aggregateInvalid);
3963
+ });
3862
3964
  const draft = object(policy.draft);
3863
3965
  const validations = Array.isArray(policy.validations)
3864
3966
  ? policy.validations
@@ -3877,6 +3979,7 @@ function validateAnonymousPublicAccess(config, diagnostics) {
3877
3979
  'serverGeneratedFields',
3878
3980
  'schedule',
3879
3981
  'publicSubtableFields',
3982
+ 'publicQueries',
3880
3983
  'draft',
3881
3984
  'validations',
3882
3985
  ];
@@ -3979,6 +4082,13 @@ function validateAnonymousPublicAccess(config, diagnostics) {
3979
4082
  pointer: `${path}.publicSubtableFields`,
3980
4083
  });
3981
4084
  }
4085
+ if (publicQueryInvalid) {
4086
+ anonymousPolicyProblems.push({
4087
+ code: 'APP_CONFIG_ANONYMOUS_PUBLIC_POLICY_QUERIES_INVALID',
4088
+ message: 'publicQueries 必须是同一资源的有界公开字段查询,limit 不超过 100,参数和排序只能引用查询字段',
4089
+ pointer: path + '.publicQueries',
4090
+ });
4091
+ }
3982
4092
  if (publicFilterInvalid) {
3983
4093
  anonymousPolicyProblems.push({
3984
4094
  code: 'APP_CONFIG_ANONYMOUS_PUBLIC_POLICY_FILTERS_INVALID',