openxiangda-devkit-core 2.0.0-alpha.86 → 2.0.0-alpha.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application-services.d.ts +1 -0
- package/dist/application-services.d.ts.map +1 -1
- package/dist/application-services.js +1 -0
- package/dist/application-services.js.map +1 -1
- package/dist/command-registry.d.ts +13 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +6 -6
- package/dist/command-registry.js.map +1 -1
- package/dist/compiler/bundle.d.ts.map +1 -1
- package/dist/compiler/bundle.js +53 -0
- package/dist/compiler/bundle.js.map +1 -1
- package/dist/compiler/config.d.ts +11 -1
- package/dist/compiler/config.d.ts.map +1 -1
- package/dist/compiler/config.js +166 -0
- package/dist/compiler/config.js.map +1 -1
- package/dist/compiler/package-compiler.d.ts.map +1 -1
- package/dist/compiler/package-compiler.js +8 -0
- package/dist/compiler/package-compiler.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +36 -1
- package/dist/workspace.js.map +1 -1
- package/package.json +2 -2
package/dist/compiler/config.js
CHANGED
|
@@ -335,6 +335,9 @@ export const openXiangdaAppConfigSchema = {
|
|
|
335
335
|
properties: {
|
|
336
336
|
root: { type: 'string', minLength: 1 },
|
|
337
337
|
routes: { type: 'array', maxItems: 500, items: { type: 'object' } },
|
|
338
|
+
admin: { type: 'object' },
|
|
339
|
+
authentication: { type: 'object' },
|
|
340
|
+
publicAccess: { type: 'object' },
|
|
338
341
|
},
|
|
339
342
|
},
|
|
340
343
|
backend: {
|
|
@@ -815,6 +818,7 @@ export function validateAppConfig(value) {
|
|
|
815
818
|
}
|
|
816
819
|
validateApplicationAuthentication(config, diagnostics);
|
|
817
820
|
validateFrontendRoutes(config, diagnostics);
|
|
821
|
+
validateAnonymousPublicAccess(config, diagnostics);
|
|
818
822
|
validateAdminNavigation(config, diagnostics);
|
|
819
823
|
validatePerspectives(config.perspectives, object(config.authz).roles, diagnostics);
|
|
820
824
|
const declaredResources = new Map((Array.isArray(object(config.data).resources)
|
|
@@ -2493,6 +2497,168 @@ function validateFrontendRoutes(config, diagnostics) {
|
|
|
2493
2497
|
}
|
|
2494
2498
|
}
|
|
2495
2499
|
}
|
|
2500
|
+
const ANONYMOUS_PUBLIC_OPERATIONS = new Set([
|
|
2501
|
+
'draft.read',
|
|
2502
|
+
'draft.update',
|
|
2503
|
+
'validate',
|
|
2504
|
+
'create',
|
|
2505
|
+
'own.list',
|
|
2506
|
+
'own.read',
|
|
2507
|
+
]);
|
|
2508
|
+
function validateAnonymousPublicAccess(config, diagnostics) {
|
|
2509
|
+
const frontend = object(config.frontend);
|
|
2510
|
+
if (frontend.publicAccess === undefined)
|
|
2511
|
+
return;
|
|
2512
|
+
const publicAccess = object(frontend.publicAccess);
|
|
2513
|
+
const policies = Array.isArray(publicAccess.policies)
|
|
2514
|
+
? publicAccess.policies
|
|
2515
|
+
: [];
|
|
2516
|
+
if (Object.keys(publicAccess).some(key => key !== 'policies') ||
|
|
2517
|
+
!Array.isArray(publicAccess.policies) ||
|
|
2518
|
+
policies.length < 1 ||
|
|
2519
|
+
policies.length > 16) {
|
|
2520
|
+
diagnostics.push(diagnostic('APP_CONFIG_ANONYMOUS_PUBLIC_ACCESS_INVALID', 'frontend.publicAccess 只接受 1 到 16 个 policies', 'frontend.publicAccess'));
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
const routes = new Map((Array.isArray(frontend.routes) ? frontend.routes : []).map(raw => {
|
|
2524
|
+
const route = object(raw);
|
|
2525
|
+
return [string(route.code), route];
|
|
2526
|
+
}));
|
|
2527
|
+
const resourceMap = new Map((Array.isArray(object(config.data).resources)
|
|
2528
|
+
? object(config.data).resources
|
|
2529
|
+
: []).map(raw => {
|
|
2530
|
+
const resource = object(raw);
|
|
2531
|
+
const fields = Array.isArray(object(resource.schema).fields)
|
|
2532
|
+
? object(resource.schema).fields
|
|
2533
|
+
: [];
|
|
2534
|
+
const fieldMap = new Map(fields.map(field => {
|
|
2535
|
+
const declaration = object(field);
|
|
2536
|
+
return [string(declaration.code), string(declaration.type)];
|
|
2537
|
+
}));
|
|
2538
|
+
const requiredCreateFields = new Set(fields
|
|
2539
|
+
.map(object)
|
|
2540
|
+
.filter(field => {
|
|
2541
|
+
const type = string(field.type);
|
|
2542
|
+
return (field.nullable === false &&
|
|
2543
|
+
APP_DATA_FIELD_TYPES.has(type) &&
|
|
2544
|
+
supportsGeneratedMutation({ type }));
|
|
2545
|
+
})
|
|
2546
|
+
.map(field => string(field.code)));
|
|
2547
|
+
const surface = object(resource.surface);
|
|
2548
|
+
const generated = object(surface.generated);
|
|
2549
|
+
const nativeCreate = (string(surface.mutationOwner) || 'native') === 'native' &&
|
|
2550
|
+
generated.create !== false;
|
|
2551
|
+
return [
|
|
2552
|
+
string(resource.code),
|
|
2553
|
+
{ fields: fieldMap, requiredCreateFields, nativeCreate },
|
|
2554
|
+
];
|
|
2555
|
+
}));
|
|
2556
|
+
const policyCodes = new Set();
|
|
2557
|
+
const routeCodes = new Set();
|
|
2558
|
+
policies.forEach((raw, index) => {
|
|
2559
|
+
const policy = object(raw);
|
|
2560
|
+
const path = `frontend.publicAccess.policies[${index}]`;
|
|
2561
|
+
const code = string(policy.code);
|
|
2562
|
+
const routeCode = string(policy.routeCode);
|
|
2563
|
+
const resourceCode = string(policy.resourceCode);
|
|
2564
|
+
const route = routes.get(routeCode);
|
|
2565
|
+
const resource = resourceMap.get(resourceCode);
|
|
2566
|
+
const resourceFields = resource?.fields;
|
|
2567
|
+
const operations = Array.isArray(policy.operations)
|
|
2568
|
+
? policy.operations.map(string)
|
|
2569
|
+
: [];
|
|
2570
|
+
const fields = Array.isArray(policy.fields) ? policy.fields.map(string) : [];
|
|
2571
|
+
const requiredFields = Array.isArray(policy.requiredFields)
|
|
2572
|
+
? policy.requiredFields.map(string)
|
|
2573
|
+
: [];
|
|
2574
|
+
const ownRecordFields = Array.isArray(policy.ownRecordFields)
|
|
2575
|
+
? policy.ownRecordFields.map(string)
|
|
2576
|
+
: fields;
|
|
2577
|
+
const draft = object(policy.draft);
|
|
2578
|
+
const validations = Array.isArray(policy.validations)
|
|
2579
|
+
? policy.validations
|
|
2580
|
+
: [];
|
|
2581
|
+
const exactRootKeys = [
|
|
2582
|
+
'code',
|
|
2583
|
+
'routeCode',
|
|
2584
|
+
'mode',
|
|
2585
|
+
'resourceCode',
|
|
2586
|
+
'operations',
|
|
2587
|
+
'fields',
|
|
2588
|
+
'requiredFields',
|
|
2589
|
+
'ownRecordFields',
|
|
2590
|
+
'draft',
|
|
2591
|
+
'validations',
|
|
2592
|
+
];
|
|
2593
|
+
const invalid = Object.keys(policy).some(key => !exactRootKeys.includes(key)) ||
|
|
2594
|
+
!OPERATION_CODE_PATTERN.test(code) ||
|
|
2595
|
+
policyCodes.has(code) ||
|
|
2596
|
+
routeCodes.has(routeCode) ||
|
|
2597
|
+
policy.mode !== 'anonymous' ||
|
|
2598
|
+
!route ||
|
|
2599
|
+
route.surface !== 'user' ||
|
|
2600
|
+
route.capability !== undefined ||
|
|
2601
|
+
route.access !== undefined ||
|
|
2602
|
+
/[:*]/.test(string(route.path)) ||
|
|
2603
|
+
!resourceFields ||
|
|
2604
|
+
operations.length < 1 ||
|
|
2605
|
+
operations.length > ANONYMOUS_PUBLIC_OPERATIONS.size ||
|
|
2606
|
+
new Set(operations).size !== operations.length ||
|
|
2607
|
+
operations.some(operation => !ANONYMOUS_PUBLIC_OPERATIONS.has(operation)) ||
|
|
2608
|
+
fields.length < 1 ||
|
|
2609
|
+
fields.length > 64 ||
|
|
2610
|
+
new Set(fields).size !== fields.length ||
|
|
2611
|
+
fields.some(field => !resourceFields?.has(field)) ||
|
|
2612
|
+
new Set(requiredFields).size !== requiredFields.length ||
|
|
2613
|
+
requiredFields.some(field => !fields.includes(field)) ||
|
|
2614
|
+
(operations.includes('create') &&
|
|
2615
|
+
(!resource?.nativeCreate ||
|
|
2616
|
+
[...(resource?.requiredCreateFields || [])].some(field => !fields.includes(field) || !requiredFields.includes(field)))) ||
|
|
2617
|
+
new Set(ownRecordFields).size !== ownRecordFields.length ||
|
|
2618
|
+
ownRecordFields.some(field => !fields.includes(field)) ||
|
|
2619
|
+
(operations.includes('draft.read') !== operations.includes('draft.update')) ||
|
|
2620
|
+
(operations.some(operation => operation.startsWith('draft.')) &&
|
|
2621
|
+
(policy.draft === undefined || draft.enabled !== true)) ||
|
|
2622
|
+
(policy.draft !== undefined &&
|
|
2623
|
+
(Object.keys(draft).some(key => !['enabled', 'inactivityTtlSeconds', 'maxBytes'].includes(key)) ||
|
|
2624
|
+
draft.enabled !== true ||
|
|
2625
|
+
(draft.inactivityTtlSeconds !== undefined &&
|
|
2626
|
+
(!Number.isSafeInteger(draft.inactivityTtlSeconds) ||
|
|
2627
|
+
Number(draft.inactivityTtlSeconds) < 3600 ||
|
|
2628
|
+
Number(draft.inactivityTtlSeconds) > 7_776_000)) ||
|
|
2629
|
+
(draft.maxBytes !== undefined &&
|
|
2630
|
+
(!Number.isSafeInteger(draft.maxBytes) ||
|
|
2631
|
+
Number(draft.maxBytes) < 4096 ||
|
|
2632
|
+
Number(draft.maxBytes) > 262_144)))) ||
|
|
2633
|
+
validations.length > 16;
|
|
2634
|
+
if (invalid) {
|
|
2635
|
+
diagnostics.push(diagnostic('APP_CONFIG_ANONYMOUS_PUBLIC_POLICY_INVALID', '匿名公开策略必须绑定唯一静态 user route、已声明资源/字段和有界操作', path));
|
|
2636
|
+
}
|
|
2637
|
+
const validationCodes = new Set();
|
|
2638
|
+
validations.forEach((rawValidation, validationIndex) => {
|
|
2639
|
+
const validation = object(rawValidation);
|
|
2640
|
+
const validationCode = string(validation.code);
|
|
2641
|
+
const validationFields = Array.isArray(validation.fields)
|
|
2642
|
+
? validation.fields.map(string)
|
|
2643
|
+
: [];
|
|
2644
|
+
if (Object.keys(validation).some(key => !['code', 'kind', 'fields', 'result'].includes(key)) ||
|
|
2645
|
+
!OPERATION_CODE_PATTERN.test(validationCode) ||
|
|
2646
|
+
validationCodes.has(validationCode) ||
|
|
2647
|
+
validation.kind !== 'duplicate' ||
|
|
2648
|
+
validation.result !== 'availability' ||
|
|
2649
|
+
validationFields.length < 1 ||
|
|
2650
|
+
validationFields.length > 8 ||
|
|
2651
|
+
new Set(validationFields).size !== validationFields.length ||
|
|
2652
|
+
validationFields.some(field => !fields.includes(field)) ||
|
|
2653
|
+
validationFields.some(field => ['file', 'subtable'].includes(resourceFields?.get(field) || ''))) {
|
|
2654
|
+
diagnostics.push(diagnostic('APP_CONFIG_ANONYMOUS_PUBLIC_VALIDATION_INVALID', '匿名公开校验必须是只使用已授权字段的有界 duplicate availability 校验', `${path}.validations[${validationIndex}]`));
|
|
2655
|
+
}
|
|
2656
|
+
validationCodes.add(validationCode);
|
|
2657
|
+
});
|
|
2658
|
+
policyCodes.add(code);
|
|
2659
|
+
routeCodes.add(routeCode);
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2496
2662
|
function validateApplicationAuthentication(config, diagnostics) {
|
|
2497
2663
|
const frontend = object(config.frontend);
|
|
2498
2664
|
if (frontend.authentication === undefined)
|