openxiangda 1.0.85 → 1.0.87
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/README.md +28 -0
- package/lib/cli.js +482 -10
- package/openxiangda-skills/SKILL.md +5 -1
- package/openxiangda-skills/references/architecture-design.md +29 -0
- package/openxiangda-skills/references/openxiangda-api.md +105 -3
- package/openxiangda-skills/references/pages/page-sdk.md +35 -0
- package/openxiangda-skills/references/permissions-settings.md +39 -2
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +70 -4
- package/package.json +2 -1
- package/packages/sdk/dist/runtime/index.cjs +3590 -3388
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +4 -1001
- package/packages/sdk/dist/runtime/index.d.ts +4 -1001
- package/packages/sdk/dist/runtime/index.mjs +3049 -2849
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +2793 -116
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +1394 -2
- package/packages/sdk/dist/runtime/react.d.ts +1394 -2
- package/packages/sdk/dist/runtime/react.mjs +2749 -84
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/AGENTS.md +29 -0
- package/templates/openxiangda-react-spa/src/app/router.tsx +21 -1
- package/templates/openxiangda-react-spa/src/pages/public/PublicRegisterPage.tsx +15 -0
- package/templates/openxiangda-react-spa/src/resources/permissions/page-groups/public-visitor.json +8 -0
- package/templates/openxiangda-react-spa/src/resources/public-access/public-register.json +14 -0
- package/templates/openxiangda-react-spa/src/resources/routes/public-register.json +8 -0
- package/templates/openxiangda-react-spa/tsconfig.app.json +1 -1
- package/templates/openxiangda-react-spa/vite.config.ts +1 -1
- package/packages/sdk/dist/openxiangdaProvider-CaXMpsnK.d.mts +0 -328
- package/packages/sdk/dist/openxiangdaProvider-CaXMpsnK.d.ts +0 -328
package/lib/cli.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const os = require('os');
|
|
5
|
+
const { builtinModules } = require('module');
|
|
5
6
|
const { spawnSync } = require('child_process');
|
|
6
7
|
const { pathToFileURL } = require('url');
|
|
7
8
|
const esbuild = require('esbuild');
|
|
@@ -3375,6 +3376,8 @@ function ensureResourceBuckets(bound) {
|
|
|
3375
3376
|
bound.resources.connectors = bound.resources.connectors || {};
|
|
3376
3377
|
bound.resources.dataViews = bound.resources.dataViews || {};
|
|
3377
3378
|
bound.resources.authConfigs = bound.resources.authConfigs || {};
|
|
3379
|
+
bound.resources.routes = bound.resources.routes || {};
|
|
3380
|
+
bound.resources.publicAccessPolicies = bound.resources.publicAccessPolicies || {};
|
|
3378
3381
|
bound.resources.notifications = bound.resources.notifications || {};
|
|
3379
3382
|
bound.resources.notifications.templates = bound.resources.notifications.templates || {};
|
|
3380
3383
|
bound.resources.notifications.typeConfigs = bound.resources.notifications.typeConfigs || {};
|
|
@@ -3585,6 +3588,22 @@ function saveDataViewResource(target, dataViewCode, dataViewId, extra = {}) {
|
|
|
3585
3588
|
}, keys);
|
|
3586
3589
|
}
|
|
3587
3590
|
|
|
3591
|
+
function saveRouteResource(target, routeCode, routeId, extra = {}) {
|
|
3592
|
+
const keys = ['routeId', 'pathPattern', 'publicAccess', 'publicPolicyCode'];
|
|
3593
|
+
saveStateResource(target, 'routes', routeCode, {
|
|
3594
|
+
...pickStateFields(extra, keys),
|
|
3595
|
+
routeId,
|
|
3596
|
+
}, keys);
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
function savePublicAccessPolicyResource(target, policyCode, policyId, extra = {}) {
|
|
3600
|
+
const keys = ['policyId', 'mode', 'routeCode', 'pathPattern'];
|
|
3601
|
+
saveStateResource(target, 'publicAccessPolicies', policyCode, {
|
|
3602
|
+
...pickStateFields(extra, keys),
|
|
3603
|
+
policyId,
|
|
3604
|
+
}, keys);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3588
3607
|
function saveAuthConfigResource(target, authConfigCode, configId, extra = {}) {
|
|
3589
3608
|
const keys = ['configId', 'status'];
|
|
3590
3609
|
saveStateResource(target, 'authConfigs', authConfigCode, {
|
|
@@ -3701,6 +3720,13 @@ const RESOURCE_SPECS = [
|
|
|
3701
3720
|
{ key: 'functions', dir: 'functions', topFiles: ['functions.json'], pluralKeys: ['functions'] },
|
|
3702
3721
|
{ key: 'dataViews', dir: 'data-views', topFiles: ['data-views.json'], pluralKeys: ['dataViews', 'data-views'] },
|
|
3703
3722
|
{ key: 'authConfigs', dir: 'auth', topFiles: ['auth.json'], pluralKeys: ['authConfigs', 'auth'] },
|
|
3723
|
+
{ key: 'routes', dir: 'routes', topFiles: ['routes.json'], pluralKeys: ['routes'] },
|
|
3724
|
+
{
|
|
3725
|
+
key: 'publicAccessPolicies',
|
|
3726
|
+
dir: 'public-access',
|
|
3727
|
+
topFiles: ['public-access.json'],
|
|
3728
|
+
pluralKeys: ['publicAccessPolicies', 'publicAccess', 'policies'],
|
|
3729
|
+
},
|
|
3704
3730
|
{
|
|
3705
3731
|
key: 'pagePermissionGroups',
|
|
3706
3732
|
dir: path.join('permissions', 'page-groups'),
|
|
@@ -3882,6 +3908,12 @@ function generateResourceTypes(manifest, outputFile) {
|
|
|
3882
3908
|
routeCode: item.routeCode || null,
|
|
3883
3909
|
path: item.path || null,
|
|
3884
3910
|
}));
|
|
3911
|
+
const routes = (manifest.routes || []).map(item => ({
|
|
3912
|
+
code: item.code,
|
|
3913
|
+
pathPattern: item.pathPattern || item.path || null,
|
|
3914
|
+
publicAccess: item.publicAccess || 'none',
|
|
3915
|
+
publicPolicyCode: item.publicPolicyCode || item.policyCode || null,
|
|
3916
|
+
}));
|
|
3885
3917
|
const pagePermissionGroups = (manifest.pagePermissionGroups || []).map(item => ({
|
|
3886
3918
|
code: item.code,
|
|
3887
3919
|
menuCodes: normalizePermissionCodeArray(item.menuCodes),
|
|
@@ -3891,6 +3923,7 @@ function generateResourceTypes(manifest, outputFile) {
|
|
|
3891
3923
|
const routeCodes = unique(
|
|
3892
3924
|
[
|
|
3893
3925
|
...menus.map(item => item.routeCode),
|
|
3926
|
+
...routes.map(item => item.code),
|
|
3894
3927
|
...pagePermissionGroups.flatMap(item => item.routeCodes),
|
|
3895
3928
|
].filter(Boolean)
|
|
3896
3929
|
);
|
|
@@ -3901,6 +3934,7 @@ function generateResourceTypes(manifest, outputFile) {
|
|
|
3901
3934
|
const dataViewCodes = unique((manifest.dataViews || []).map(item => item.code).filter(Boolean));
|
|
3902
3935
|
const functionCodes = unique((manifest.functions || []).map(item => item.code).filter(Boolean));
|
|
3903
3936
|
const authConfigCodes = unique((manifest.authConfigs || []).map(item => item.code).filter(Boolean));
|
|
3937
|
+
const publicAccessPolicyCodes = unique((manifest.publicAccessPolicies || []).map(item => item.code).filter(Boolean));
|
|
3904
3938
|
const content = [
|
|
3905
3939
|
'/* eslint-disable */',
|
|
3906
3940
|
'// Generated by openxiangda resource typegen. Do not edit manually.',
|
|
@@ -3923,8 +3957,13 @@ function generateResourceTypes(manifest, outputFile) {
|
|
|
3923
3957
|
`export const authConfigCodes = ${JSON.stringify(authConfigCodes, null, 2)} as const`,
|
|
3924
3958
|
'export type AuthConfigCode = typeof authConfigCodes[number]',
|
|
3925
3959
|
'',
|
|
3960
|
+
`export const publicAccessPolicyCodes = ${JSON.stringify(publicAccessPolicyCodes, null, 2)} as const`,
|
|
3961
|
+
'export type PublicAccessPolicyCode = typeof publicAccessPolicyCodes[number]',
|
|
3962
|
+
'',
|
|
3926
3963
|
`export const runtimeMenus = ${JSON.stringify(menus, null, 2)} as const`,
|
|
3927
3964
|
'',
|
|
3965
|
+
`export const runtimeRoutes = ${JSON.stringify(routes, null, 2)} as const`,
|
|
3966
|
+
'',
|
|
3928
3967
|
`export const pagePermissionGroups = ${JSON.stringify(pagePermissionGroups, null, 2)} as const`,
|
|
3929
3968
|
'',
|
|
3930
3969
|
].join('\n');
|
|
@@ -3938,6 +3977,7 @@ function generateResourceTypes(manifest, outputFile) {
|
|
|
3938
3977
|
dataViews: dataViewCodes.length,
|
|
3939
3978
|
functions: functionCodes.length,
|
|
3940
3979
|
authConfigs: authConfigCodes.length,
|
|
3980
|
+
publicAccessPolicies: publicAccessPolicyCodes.length,
|
|
3941
3981
|
};
|
|
3942
3982
|
}
|
|
3943
3983
|
|
|
@@ -4042,6 +4082,29 @@ function validateResourceItem(kind, item, errors, warnings) {
|
|
|
4042
4082
|
warnings.push(`${label}: 已开启非默认注册策略 ${registrationMode},请确认身份匹配键、默认角色和审计字段`);
|
|
4043
4083
|
}
|
|
4044
4084
|
}
|
|
4085
|
+
if (kind === 'routes') {
|
|
4086
|
+
if (!item.pathPattern && !item.path) errors.push(`${label}: 缺少 pathPattern`);
|
|
4087
|
+
const mode = item.publicAccess === undefined ? 'none' : String(item.publicAccess);
|
|
4088
|
+
if (!['none', 'guest', 'ticket'].includes(mode)) {
|
|
4089
|
+
errors.push(`${label}: publicAccess 只能是 none、guest 或 ticket`);
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
if (kind === 'publicAccessPolicies') {
|
|
4093
|
+
if (!item.name) warnings.push(`${label}: 未声明 name,将使用 code`);
|
|
4094
|
+
const mode = item.mode === undefined ? 'guest' : String(item.mode);
|
|
4095
|
+
if (!['guest', 'ticket'].includes(mode)) {
|
|
4096
|
+
errors.push(`${label}: mode 只能是 guest 或 ticket`);
|
|
4097
|
+
}
|
|
4098
|
+
if (!item.routeCode && !item.pathPattern && !item.path) {
|
|
4099
|
+
warnings.push(`${label}: 未声明 routeCode/pathPattern,建议绑定到 /view/:appType/public/* 路由`);
|
|
4100
|
+
}
|
|
4101
|
+
if (!item.grants || typeof item.grants !== 'object' || Array.isArray(item.grants)) {
|
|
4102
|
+
errors.push(`${label}: 缺少 grants object;未显式 grant 的 form/dataView/function/connector 默认拒绝`);
|
|
4103
|
+
}
|
|
4104
|
+
if (!Array.isArray(item.externalRoleCodes || item.externalRoles || item.roles || [])) {
|
|
4105
|
+
errors.push(`${label}: externalRoleCodes 必须是数组`);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4045
4108
|
if (kind === 'pagePermissionGroups' && !item.name) errors.push(`${label}: 缺少 name`);
|
|
4046
4109
|
if (kind === 'formPermissionGroups') {
|
|
4047
4110
|
if (!item.name) errors.push(`${label}: 缺少 name`);
|
|
@@ -4444,6 +4507,8 @@ async function buildResourcePlan(config, target, manifest) {
|
|
|
4444
4507
|
await addWorkflowPlanActions(config, target, actions, manifest.workflows, existing.workflows);
|
|
4445
4508
|
addPlanActions(actions, 'function', manifest.functions, existing.functions, (item, current) => functionEquals(target, item, current));
|
|
4446
4509
|
addPlanActions(actions, 'authConfig', manifest.authConfigs, existing.authConfigs, (item, current) => authConfigEquals(item, current));
|
|
4510
|
+
addPlanActions(actions, 'route', manifest.routes, existing.routes, (item, current) => routeEquals(item, current));
|
|
4511
|
+
addPlanActions(actions, 'publicAccessPolicy', manifest.publicAccessPolicies, existing.publicAccessPolicies, (item, current) => publicAccessPolicyEquals(target.bound, item, current));
|
|
4447
4512
|
await addAutomationPlanActions(config, target, actions, manifest.automations, existing.automations);
|
|
4448
4513
|
addPlanActions(actions, 'dataView', manifest.dataViews, existing.dataViews, (item, current) => dataViewEquals(target.bound, item, current));
|
|
4449
4514
|
addPlanActions(actions, 'pagePermissionGroup', manifest.pagePermissionGroups, existing.pagePermissionGroups, (item, current) => pagePermissionGroupEquals(target.bound, item, current));
|
|
@@ -4480,8 +4545,10 @@ async function publishResourceManifest(config, target, manifest, options = {}) {
|
|
|
4480
4545
|
await publishWorkflowResources(config, target, manifest.workflows || [], result);
|
|
4481
4546
|
await publishFunctionResources(config, target, manifest.functions || [], result);
|
|
4482
4547
|
await publishAuthConfigResources(config, target, manifest.authConfigs || [], result);
|
|
4548
|
+
await publishRouteResources(config, target, manifest.routes || [], result);
|
|
4483
4549
|
await publishAutomationResources(config, target, manifest.automations || [], result);
|
|
4484
4550
|
await publishDataViewResources(config, target, manifest.dataViews || [], result);
|
|
4551
|
+
await publishPublicAccessPolicyResources(config, target, manifest.publicAccessPolicies || [], result);
|
|
4485
4552
|
await publishPagePermissionGroupResources(config, target, manifest.pagePermissionGroups || [], result);
|
|
4486
4553
|
await publishFormPermissionGroupResources(config, target, manifest.formPermissionGroups || [], result);
|
|
4487
4554
|
if (options.prune) {
|
|
@@ -4504,6 +4571,8 @@ async function fetchExistingResourceMaps(config, target, manifest) {
|
|
|
4504
4571
|
workflows: new Map(),
|
|
4505
4572
|
functions: new Map(),
|
|
4506
4573
|
authConfigs: new Map(),
|
|
4574
|
+
routes: new Map(),
|
|
4575
|
+
publicAccessPolicies: new Map(),
|
|
4507
4576
|
automations: new Map(),
|
|
4508
4577
|
dataViews: new Map(),
|
|
4509
4578
|
pagePermissionGroups: new Map(),
|
|
@@ -4634,6 +4703,28 @@ async function fetchExistingResourceMaps(config, target, manifest) {
|
|
|
4634
4703
|
});
|
|
4635
4704
|
}
|
|
4636
4705
|
}
|
|
4706
|
+
if ((manifest.routes || []).length > 0) {
|
|
4707
|
+
const data = await requestWithAuth(
|
|
4708
|
+
config,
|
|
4709
|
+
target.profileName,
|
|
4710
|
+
apiPathWithQuery(`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes`, {
|
|
4711
|
+
page: 1,
|
|
4712
|
+
pageSize: 1000,
|
|
4713
|
+
})
|
|
4714
|
+
);
|
|
4715
|
+
indexByCode(maps.routes, normalizeItems(data), item => item.code || item.resourceCode);
|
|
4716
|
+
}
|
|
4717
|
+
if ((manifest.publicAccessPolicies || []).length > 0) {
|
|
4718
|
+
const data = await requestWithAuth(
|
|
4719
|
+
config,
|
|
4720
|
+
target.profileName,
|
|
4721
|
+
apiPathWithQuery(`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies`, {
|
|
4722
|
+
page: 1,
|
|
4723
|
+
pageSize: 1000,
|
|
4724
|
+
})
|
|
4725
|
+
);
|
|
4726
|
+
indexByCode(maps.publicAccessPolicies, normalizeItems(data), item => item.code || item.resourceCode);
|
|
4727
|
+
}
|
|
4637
4728
|
if ((manifest.dataViews || []).length > 0) {
|
|
4638
4729
|
const data = await requestWithAuth(
|
|
4639
4730
|
config,
|
|
@@ -5361,6 +5452,74 @@ async function publishDataViewResources(config, target, dataViews, result) {
|
|
|
5361
5452
|
}
|
|
5362
5453
|
}
|
|
5363
5454
|
|
|
5455
|
+
async function publishRouteResources(config, target, routes, result) {
|
|
5456
|
+
for (const route of routes) {
|
|
5457
|
+
const code = route.code || route.resourceCode;
|
|
5458
|
+
const existing = await findExistingRoute(config, target, code);
|
|
5459
|
+
const body = normalizeRouteManifest(route);
|
|
5460
|
+
const data = existing
|
|
5461
|
+
? await requestWithAuth(
|
|
5462
|
+
config,
|
|
5463
|
+
target.profileName,
|
|
5464
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes/${encodeURIComponent(code)}`,
|
|
5465
|
+
{ method: 'PUT', body }
|
|
5466
|
+
)
|
|
5467
|
+
: await requestWithAuth(
|
|
5468
|
+
config,
|
|
5469
|
+
target.profileName,
|
|
5470
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes`,
|
|
5471
|
+
{ method: 'POST', body }
|
|
5472
|
+
);
|
|
5473
|
+
if (data?.id) {
|
|
5474
|
+
saveRouteResource(target, code, data.id, {
|
|
5475
|
+
pathPattern: data.pathPattern,
|
|
5476
|
+
publicAccess: data.publicAccess,
|
|
5477
|
+
publicPolicyCode: data.publicPolicyCode,
|
|
5478
|
+
});
|
|
5479
|
+
}
|
|
5480
|
+
result.published.push({
|
|
5481
|
+
kind: 'route',
|
|
5482
|
+
code,
|
|
5483
|
+
action: existing ? 'update' : 'create',
|
|
5484
|
+
id: data?.id,
|
|
5485
|
+
});
|
|
5486
|
+
}
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5489
|
+
async function publishPublicAccessPolicyResources(config, target, policies, result) {
|
|
5490
|
+
for (const policy of policies) {
|
|
5491
|
+
const code = policy.code || policy.resourceCode;
|
|
5492
|
+
const existing = await findExistingPublicAccessPolicy(config, target, code);
|
|
5493
|
+
const body = normalizePublicAccessPolicyManifest(target.bound, policy);
|
|
5494
|
+
const data = existing
|
|
5495
|
+
? await requestWithAuth(
|
|
5496
|
+
config,
|
|
5497
|
+
target.profileName,
|
|
5498
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies/${encodeURIComponent(code)}`,
|
|
5499
|
+
{ method: 'PUT', body }
|
|
5500
|
+
)
|
|
5501
|
+
: await requestWithAuth(
|
|
5502
|
+
config,
|
|
5503
|
+
target.profileName,
|
|
5504
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies`,
|
|
5505
|
+
{ method: 'POST', body }
|
|
5506
|
+
);
|
|
5507
|
+
if (data?.id) {
|
|
5508
|
+
savePublicAccessPolicyResource(target, code, data.id, {
|
|
5509
|
+
mode: data.mode,
|
|
5510
|
+
routeCode: data.routeCode,
|
|
5511
|
+
pathPattern: data.pathPattern,
|
|
5512
|
+
});
|
|
5513
|
+
}
|
|
5514
|
+
result.published.push({
|
|
5515
|
+
kind: 'publicAccessPolicy',
|
|
5516
|
+
code,
|
|
5517
|
+
action: existing ? 'update' : 'create',
|
|
5518
|
+
id: data?.id,
|
|
5519
|
+
});
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5364
5523
|
async function findExistingFunction(config, target, code) {
|
|
5365
5524
|
const stateId = target.bound.resources?.functions?.[code]?.functionId;
|
|
5366
5525
|
const byCode = await requestOptionalWithAuth(
|
|
@@ -5373,6 +5532,30 @@ async function findExistingFunction(config, target, code) {
|
|
|
5373
5532
|
return null;
|
|
5374
5533
|
}
|
|
5375
5534
|
|
|
5535
|
+
async function findExistingRoute(config, target, code) {
|
|
5536
|
+
const stateId = target.bound.resources?.routes?.[code]?.routeId;
|
|
5537
|
+
const byCode = await requestOptionalWithAuth(
|
|
5538
|
+
config,
|
|
5539
|
+
target.profileName,
|
|
5540
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes/${encodeURIComponent(code)}`
|
|
5541
|
+
);
|
|
5542
|
+
if (byCode?.id) return byCode;
|
|
5543
|
+
if (stateId) return { id: stateId, code };
|
|
5544
|
+
return null;
|
|
5545
|
+
}
|
|
5546
|
+
|
|
5547
|
+
async function findExistingPublicAccessPolicy(config, target, code) {
|
|
5548
|
+
const stateId = target.bound.resources?.publicAccessPolicies?.[code]?.policyId;
|
|
5549
|
+
const byCode = await requestOptionalWithAuth(
|
|
5550
|
+
config,
|
|
5551
|
+
target.profileName,
|
|
5552
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies/${encodeURIComponent(code)}`
|
|
5553
|
+
);
|
|
5554
|
+
if (byCode?.id) return byCode;
|
|
5555
|
+
if (stateId) return { id: stateId, code };
|
|
5556
|
+
return null;
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5376
5559
|
async function findExistingAuthConfig(config, target, code) {
|
|
5377
5560
|
const stateId = target.bound.resources?.authConfigs?.[code]?.configId;
|
|
5378
5561
|
const byCode = await requestOptionalWithAuth(
|
|
@@ -5536,7 +5719,14 @@ async function pruneResourceManifest(config, target, manifest, result) {
|
|
|
5536
5719
|
await pruneAutomations(config, target, desiredCodes(manifest.automations), result);
|
|
5537
5720
|
await pruneFunctions(config, target, desiredCodes(manifest.functions), result);
|
|
5538
5721
|
await pruneAuthConfigs(config, target, desiredCodes(manifest.authConfigs), result);
|
|
5722
|
+
await pruneRoutes(config, target, desiredCodes(manifest.routes), result);
|
|
5539
5723
|
await pruneDataViews(config, target, desiredCodes(manifest.dataViews), result);
|
|
5724
|
+
await prunePublicAccessPolicies(
|
|
5725
|
+
config,
|
|
5726
|
+
target,
|
|
5727
|
+
desiredCodes(manifest.publicAccessPolicies),
|
|
5728
|
+
result
|
|
5729
|
+
);
|
|
5540
5730
|
await prunePagePermissionGroups(
|
|
5541
5731
|
config,
|
|
5542
5732
|
target,
|
|
@@ -5754,6 +5944,52 @@ async function pruneAuthConfigs(config, target, desired, result) {
|
|
|
5754
5944
|
}
|
|
5755
5945
|
}
|
|
5756
5946
|
|
|
5947
|
+
async function pruneRoutes(config, target, desired, result) {
|
|
5948
|
+
const data = await requestWithAuth(
|
|
5949
|
+
config,
|
|
5950
|
+
target.profileName,
|
|
5951
|
+
apiPathWithQuery(`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes`, {
|
|
5952
|
+
page: 1,
|
|
5953
|
+
pageSize: 1000,
|
|
5954
|
+
})
|
|
5955
|
+
);
|
|
5956
|
+
for (const route of normalizeItems(data)) {
|
|
5957
|
+
const code = route.code || route.resourceCode;
|
|
5958
|
+
if (!code || desired.has(code)) continue;
|
|
5959
|
+
await pruneOne(config, target, result, 'route', code, route.id, async () => {
|
|
5960
|
+
await requestWithAuth(
|
|
5961
|
+
config,
|
|
5962
|
+
target.profileName,
|
|
5963
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes/${encodeURIComponent(code)}`,
|
|
5964
|
+
{ method: 'DELETE' }
|
|
5965
|
+
);
|
|
5966
|
+
});
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
|
|
5970
|
+
async function prunePublicAccessPolicies(config, target, desired, result) {
|
|
5971
|
+
const data = await requestWithAuth(
|
|
5972
|
+
config,
|
|
5973
|
+
target.profileName,
|
|
5974
|
+
apiPathWithQuery(`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies`, {
|
|
5975
|
+
page: 1,
|
|
5976
|
+
pageSize: 1000,
|
|
5977
|
+
})
|
|
5978
|
+
);
|
|
5979
|
+
for (const policy of normalizeItems(data)) {
|
|
5980
|
+
const code = policy.code || policy.resourceCode;
|
|
5981
|
+
if (!code || desired.has(code)) continue;
|
|
5982
|
+
await pruneOne(config, target, result, 'publicAccessPolicy', code, policy.id, async () => {
|
|
5983
|
+
await requestWithAuth(
|
|
5984
|
+
config,
|
|
5985
|
+
target.profileName,
|
|
5986
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies/${encodeURIComponent(code)}`,
|
|
5987
|
+
{ method: 'DELETE' }
|
|
5988
|
+
);
|
|
5989
|
+
});
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5992
|
+
|
|
5757
5993
|
async function prunePagePermissionGroups(config, target, desired, result) {
|
|
5758
5994
|
const data = await requestWithAuth(
|
|
5759
5995
|
config,
|
|
@@ -5830,6 +6066,8 @@ function removeStateResource(target, kind, code) {
|
|
|
5830
6066
|
automation: 'automations',
|
|
5831
6067
|
function: 'functions',
|
|
5832
6068
|
authConfig: 'authConfigs',
|
|
6069
|
+
route: 'routes',
|
|
6070
|
+
publicAccessPolicy: 'publicAccessPolicies',
|
|
5833
6071
|
dataView: 'dataViews',
|
|
5834
6072
|
pagePermissionGroup: 'pagePermissionGroups',
|
|
5835
6073
|
formPermissionGroup: 'formPermissionGroups',
|
|
@@ -6048,6 +6286,36 @@ async function pullResources(config, target) {
|
|
|
6048
6286
|
written.push(path.relative(process.cwd(), filePath));
|
|
6049
6287
|
}
|
|
6050
6288
|
|
|
6289
|
+
const routes = await requestWithAuth(
|
|
6290
|
+
config,
|
|
6291
|
+
target.profileName,
|
|
6292
|
+
apiPathWithQuery(`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/routes`, {
|
|
6293
|
+
page: 1,
|
|
6294
|
+
pageSize: 1000,
|
|
6295
|
+
})
|
|
6296
|
+
);
|
|
6297
|
+
for (const route of normalizeItems(routes)) {
|
|
6298
|
+
const code = route.code || route.resourceCode || route.id;
|
|
6299
|
+
const filePath = path.join(baseDir, 'routes', `${code}.json`);
|
|
6300
|
+
writeResourceJsonFile(filePath, toPulledRoute(route));
|
|
6301
|
+
written.push(path.relative(process.cwd(), filePath));
|
|
6302
|
+
}
|
|
6303
|
+
|
|
6304
|
+
const publicAccessPolicies = await requestWithAuth(
|
|
6305
|
+
config,
|
|
6306
|
+
target.profileName,
|
|
6307
|
+
apiPathWithQuery(`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/public-access/policies`, {
|
|
6308
|
+
page: 1,
|
|
6309
|
+
pageSize: 1000,
|
|
6310
|
+
})
|
|
6311
|
+
);
|
|
6312
|
+
for (const policy of normalizeItems(publicAccessPolicies)) {
|
|
6313
|
+
const code = policy.code || policy.resourceCode || policy.id;
|
|
6314
|
+
const filePath = path.join(baseDir, 'public-access', `${code}.json`);
|
|
6315
|
+
writeResourceJsonFile(filePath, toPulledPublicAccessPolicy(policy, pullLookups));
|
|
6316
|
+
written.push(path.relative(process.cwd(), filePath));
|
|
6317
|
+
}
|
|
6318
|
+
|
|
6051
6319
|
const dataViews = await requestWithAuth(
|
|
6052
6320
|
config,
|
|
6053
6321
|
target.profileName,
|
|
@@ -6263,6 +6531,48 @@ function toPulledDataView(dataView, permissionGroups, lookups) {
|
|
|
6263
6531
|
});
|
|
6264
6532
|
}
|
|
6265
6533
|
|
|
6534
|
+
function toPulledRoute(route) {
|
|
6535
|
+
return stripUndefinedValues({
|
|
6536
|
+
code: route.code || route.resourceCode,
|
|
6537
|
+
title: route.title || route.name,
|
|
6538
|
+
kind: route.kind || 'page',
|
|
6539
|
+
pathPattern: route.pathPattern || route.path,
|
|
6540
|
+
menuCode: route.menuCode || undefined,
|
|
6541
|
+
publicAccess: route.publicAccess || 'none',
|
|
6542
|
+
publicPolicyCode: route.publicPolicyCode || route.policyCode || undefined,
|
|
6543
|
+
meta: route.metaJson || route.meta || undefined,
|
|
6544
|
+
});
|
|
6545
|
+
}
|
|
6546
|
+
|
|
6547
|
+
function toPulledPublicAccessPolicy(policy, lookups) {
|
|
6548
|
+
return stripUndefinedValues({
|
|
6549
|
+
code: policy.code || policy.resourceCode,
|
|
6550
|
+
name: policy.name,
|
|
6551
|
+
description: policy.description || '',
|
|
6552
|
+
enabled: policy.enabled !== false,
|
|
6553
|
+
mode: policy.mode || 'guest',
|
|
6554
|
+
routeCode: policy.routeCode || undefined,
|
|
6555
|
+
pathPattern: policy.pathPattern || policy.path || undefined,
|
|
6556
|
+
externalRoleCodes: policy.externalRoleCodes || policy.externalRoles || [],
|
|
6557
|
+
grants: rewritePublicAccessGrantsForManifest(policy.grantsJson || policy.grants || {}, lookups),
|
|
6558
|
+
ticketConfig: policy.ticketConfigJson || policy.ticketConfig || undefined,
|
|
6559
|
+
rateLimit: policy.rateLimitJson || policy.rateLimit || undefined,
|
|
6560
|
+
expiresAt: policy.expiresAt || undefined,
|
|
6561
|
+
});
|
|
6562
|
+
}
|
|
6563
|
+
|
|
6564
|
+
function rewritePublicAccessGrantsForManifest(grants, lookups) {
|
|
6565
|
+
const formCodes = normalizePermissionCodeArray(grants.forms).map(formUuid =>
|
|
6566
|
+
lookups.formCodeByUuid.get(formUuid) || formUuid
|
|
6567
|
+
);
|
|
6568
|
+
return stripUndefinedValues({
|
|
6569
|
+
formCodes,
|
|
6570
|
+
dataViews: normalizePermissionCodeArray(grants.dataViews),
|
|
6571
|
+
functions: normalizePermissionCodeArray(grants.functions),
|
|
6572
|
+
connectors: normalizePermissionCodeArray(grants.connectors),
|
|
6573
|
+
});
|
|
6574
|
+
}
|
|
6575
|
+
|
|
6266
6576
|
function toPulledAuthConfig(authConfig) {
|
|
6267
6577
|
return stripUndefinedValues({
|
|
6268
6578
|
code: authConfig.code || authConfig.resourceCode,
|
|
@@ -6453,6 +6763,54 @@ function normalizeDataViewManifest(bound, dataView) {
|
|
|
6453
6763
|
});
|
|
6454
6764
|
}
|
|
6455
6765
|
|
|
6766
|
+
function normalizeRouteManifest(route) {
|
|
6767
|
+
const code = route.code || route.resourceCode;
|
|
6768
|
+
return stripUndefinedValues({
|
|
6769
|
+
code,
|
|
6770
|
+
title: route.title || route.name || code,
|
|
6771
|
+
kind: route.kind || 'page',
|
|
6772
|
+
pathPattern: route.pathPattern || route.path,
|
|
6773
|
+
menuCode: route.menuCode,
|
|
6774
|
+
publicAccess: route.publicAccess || 'none',
|
|
6775
|
+
publicPolicyCode: route.publicPolicyCode || route.policyCode,
|
|
6776
|
+
meta: route.meta || route.metaJson,
|
|
6777
|
+
});
|
|
6778
|
+
}
|
|
6779
|
+
|
|
6780
|
+
function normalizePublicAccessPolicyManifest(bound, policy) {
|
|
6781
|
+
const code = policy.code || policy.resourceCode;
|
|
6782
|
+
return stripUndefinedValues({
|
|
6783
|
+
code,
|
|
6784
|
+
name: policy.name || policy.title || code,
|
|
6785
|
+
description: policy.description || '',
|
|
6786
|
+
enabled: policy.enabled !== false,
|
|
6787
|
+
mode: policy.mode || 'guest',
|
|
6788
|
+
routeCode: policy.routeCode,
|
|
6789
|
+
pathPattern: policy.pathPattern || policy.path,
|
|
6790
|
+
externalRoleCodes: normalizePermissionCodeArray(
|
|
6791
|
+
policy.externalRoleCodes || policy.externalRoles || policy.roles
|
|
6792
|
+
),
|
|
6793
|
+
grants: normalizePublicAccessGrants(bound, policy.grants || {}),
|
|
6794
|
+
ticketConfig: policy.ticketConfig,
|
|
6795
|
+
rateLimit: policy.rateLimit,
|
|
6796
|
+
expiresAt: policy.expiresAt,
|
|
6797
|
+
});
|
|
6798
|
+
}
|
|
6799
|
+
|
|
6800
|
+
function normalizePublicAccessGrants(bound, grants = {}) {
|
|
6801
|
+
const formEntries = [
|
|
6802
|
+
...normalizePermissionCodeArray(grants.forms),
|
|
6803
|
+
...normalizePermissionCodeArray(grants.formCodes),
|
|
6804
|
+
...normalizePermissionCodeArray(grants.formUuids),
|
|
6805
|
+
];
|
|
6806
|
+
return stripUndefinedValues({
|
|
6807
|
+
forms: unique(formEntries.map(code => resolveOptionalFormUuid(bound, code)).filter(Boolean)),
|
|
6808
|
+
dataViews: normalizePermissionCodeArray(grants.dataViews),
|
|
6809
|
+
functions: normalizePermissionCodeArray(grants.functions),
|
|
6810
|
+
connectors: normalizePermissionCodeArray(grants.connectors),
|
|
6811
|
+
});
|
|
6812
|
+
}
|
|
6813
|
+
|
|
6456
6814
|
function normalizeAuthConfigManifest(authConfig) {
|
|
6457
6815
|
const code = authConfig.code || authConfig.resourceCode || 'default';
|
|
6458
6816
|
const configJson = clonePlainJson(
|
|
@@ -6968,6 +7326,40 @@ function authConfigEquals(desired, existing) {
|
|
|
6968
7326
|
);
|
|
6969
7327
|
}
|
|
6970
7328
|
|
|
7329
|
+
function routeEquals(desired, existing) {
|
|
7330
|
+
if (!existing) return false;
|
|
7331
|
+
const expected = normalizeRouteManifest(desired);
|
|
7332
|
+
return (
|
|
7333
|
+
String(existing.code || existing.resourceCode || '') === String(expected.code || '') &&
|
|
7334
|
+
String(existing.title || existing.name || '') === String(expected.title || '') &&
|
|
7335
|
+
String(existing.kind || 'page') === String(expected.kind || 'page') &&
|
|
7336
|
+
String(existing.pathPattern || existing.path || '') === String(expected.pathPattern || '') &&
|
|
7337
|
+
String(existing.menuCode || '') === String(expected.menuCode || '') &&
|
|
7338
|
+
String(existing.publicAccess || 'none') === String(expected.publicAccess || 'none') &&
|
|
7339
|
+
String(existing.publicPolicyCode || existing.policyCode || '') === String(expected.publicPolicyCode || '') &&
|
|
7340
|
+
jsonEqualsForPlan(existing.metaJson || existing.meta || {}, expected.meta || {}, desired.__dir)
|
|
7341
|
+
);
|
|
7342
|
+
}
|
|
7343
|
+
|
|
7344
|
+
function publicAccessPolicyEquals(bound, desired, existing) {
|
|
7345
|
+
if (!existing) return false;
|
|
7346
|
+
const expected = normalizePublicAccessPolicyManifest(bound, desired);
|
|
7347
|
+
return (
|
|
7348
|
+
String(existing.code || existing.resourceCode || '') === String(expected.code || '') &&
|
|
7349
|
+
String(existing.name || '') === String(expected.name || '') &&
|
|
7350
|
+
String(existing.description || '') === String(expected.description || '') &&
|
|
7351
|
+
Boolean(existing.enabled) === Boolean(expected.enabled) &&
|
|
7352
|
+
String(existing.mode || 'guest') === String(expected.mode || 'guest') &&
|
|
7353
|
+
String(existing.routeCode || '') === String(expected.routeCode || '') &&
|
|
7354
|
+
String(existing.pathPattern || '') === String(expected.pathPattern || '') &&
|
|
7355
|
+
stringSetEquals(existing.externalRoleCodes || [], expected.externalRoleCodes || []) &&
|
|
7356
|
+
jsonEqualsForPlan(existing.grantsJson || existing.grants || {}, expected.grants || {}, desired.__dir) &&
|
|
7357
|
+
jsonEqualsForPlan(existing.ticketConfigJson || existing.ticketConfig || {}, expected.ticketConfig || {}, desired.__dir) &&
|
|
7358
|
+
jsonEqualsForPlan(existing.rateLimitJson || existing.rateLimit || {}, expected.rateLimit || {}, desired.__dir) &&
|
|
7359
|
+
String(existing.expiresAt || '') === String(expected.expiresAt || '')
|
|
7360
|
+
);
|
|
7361
|
+
}
|
|
7362
|
+
|
|
6971
7363
|
function dataViewEquals(bound, desired, existing) {
|
|
6972
7364
|
if (!existing) return false;
|
|
6973
7365
|
const expected = normalizeDataViewManifest(bound, desired);
|
|
@@ -7107,10 +7499,18 @@ function resolveJsCodeBundlePathForPlan(localPath) {
|
|
|
7107
7499
|
const extension = path.extname(localPath).toLowerCase();
|
|
7108
7500
|
if (extension && extension !== '.ts' && extension !== '.tsx') return null;
|
|
7109
7501
|
const normalized = localPath.replace(/\\/g, '/');
|
|
7110
|
-
const
|
|
7502
|
+
const jsCodeMatched = normalized.match(/src\/js-code-nodes\/([^/]+)\/index\.tsx?$/);
|
|
7503
|
+
const automationMatched = normalized.match(/src\/automations\/([^/]+)\/index\.tsx?$/);
|
|
7504
|
+
const functionMatched = normalized.match(/src\/functions\/([^/]+)\/index\.tsx?$/);
|
|
7505
|
+
const matched = jsCodeMatched || automationMatched || functionMatched;
|
|
7111
7506
|
if (!matched) return null;
|
|
7507
|
+
const sourceKind = functionMatched
|
|
7508
|
+
? 'functions'
|
|
7509
|
+
: automationMatched
|
|
7510
|
+
? 'automations'
|
|
7511
|
+
: 'js-code-nodes';
|
|
7112
7512
|
const workspaceRoot = findWorkspaceRoot(localPath);
|
|
7113
|
-
return path.join(workspaceRoot, 'dist',
|
|
7513
|
+
return path.join(workspaceRoot, 'dist', sourceKind, matched[1], 'index.cjs');
|
|
7114
7514
|
}
|
|
7115
7515
|
|
|
7116
7516
|
function sortObjectForStableJson(value) {
|
|
@@ -7272,7 +7672,7 @@ async function uploadJsCodeSnapshotFile(
|
|
|
7272
7672
|
const resolvedLocalPath = fs.existsSync(baseResolvedPath)
|
|
7273
7673
|
? baseResolvedPath
|
|
7274
7674
|
: cwdResolvedPath;
|
|
7275
|
-
const bundlePath = resolveJsCodeBundlePath(resolvedLocalPath, scriptCode);
|
|
7675
|
+
const bundlePath = await resolveJsCodeBundlePath(resolvedLocalPath, scriptCode);
|
|
7276
7676
|
if (!fs.existsSync(bundlePath)) {
|
|
7277
7677
|
fail(`JS_CODE bundle不存在: ${bundlePath}`);
|
|
7278
7678
|
}
|
|
@@ -7308,14 +7708,14 @@ async function uploadJsCodeSnapshotFile(
|
|
|
7308
7708
|
return sourceFile;
|
|
7309
7709
|
}
|
|
7310
7710
|
|
|
7311
|
-
function resolveJsCodeBundlePath(localPath, scriptCode) {
|
|
7711
|
+
async function resolveJsCodeBundlePath(localPath, scriptCode) {
|
|
7312
7712
|
const extension = path.extname(localPath).toLowerCase();
|
|
7313
7713
|
if (['.js', '.cjs', '.mjs'].includes(extension)) {
|
|
7314
7714
|
fail(
|
|
7315
7715
|
`JS_CODE V2 要求 AI 源码使用 TypeScript,请把 sourceFile.localPath 指向 src/js-code-nodes/<scriptCode>/index.ts、src/automations/<scriptCode>/index.ts 或 src/functions/<functionCode>/index.ts,而不是已构建的 ${extension} 文件: ${localPath}`
|
|
7316
7716
|
);
|
|
7317
7717
|
}
|
|
7318
|
-
if (
|
|
7718
|
+
if (!['.ts', '.tsx'].includes(extension)) {
|
|
7319
7719
|
fail(
|
|
7320
7720
|
`JS_CODE V2 sourceFile.localPath 必须指向 TypeScript 源文件 src/js-code-nodes/<scriptCode>/index.ts、src/automations/<scriptCode>/index.ts 或 src/functions/<functionCode>/index.ts: ${localPath}`
|
|
7321
7721
|
);
|
|
@@ -7339,16 +7739,88 @@ function resolveJsCodeBundlePath(localPath, scriptCode) {
|
|
|
7339
7739
|
: 'js-code-nodes';
|
|
7340
7740
|
|
|
7341
7741
|
const workspaceRoot = findWorkspaceRoot(localPath);
|
|
7342
|
-
const result =
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7742
|
+
const result = runWorkspaceJsCodeBuild(workspaceRoot, resolvedScriptCode, sourceKind);
|
|
7743
|
+
if (result.status !== 0 && sourceKind === 'functions' && isUnsupportedFunctionsBuildScript(result)) {
|
|
7744
|
+
warn(
|
|
7745
|
+
`当前工作区 scripts/build-js-code.mjs 不支持 App Function source=functions,已使用 openxiangda 内置构建器兼容构建 ${resolvedScriptCode}`
|
|
7746
|
+
);
|
|
7747
|
+
await buildFunctionSourceWithBundledEsbuild(localPath, workspaceRoot, resolvedScriptCode);
|
|
7748
|
+
} else if (result.status !== 0) {
|
|
7749
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
7750
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
7347
7751
|
fail(`JS_CODE bundle构建失败: ${resolvedScriptCode}`);
|
|
7752
|
+
} else {
|
|
7753
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
7754
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
7348
7755
|
}
|
|
7349
7756
|
return path.join(workspaceRoot, 'dist', sourceKind, resolvedScriptCode, 'index.cjs');
|
|
7350
7757
|
}
|
|
7351
7758
|
|
|
7759
|
+
function runWorkspaceJsCodeBuild(workspaceRoot, resolvedScriptCode, sourceKind) {
|
|
7760
|
+
return spawnSync('pnpm', ['build-js-code', '--script', resolvedScriptCode, '--source', sourceKind], {
|
|
7761
|
+
cwd: workspaceRoot,
|
|
7762
|
+
encoding: 'utf8',
|
|
7763
|
+
});
|
|
7764
|
+
}
|
|
7765
|
+
|
|
7766
|
+
function isUnsupportedFunctionsBuildScript(result) {
|
|
7767
|
+
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
7768
|
+
return (
|
|
7769
|
+
output.includes('unsupported source: functions') ||
|
|
7770
|
+
output.includes('Expected js-code-nodes or automations')
|
|
7771
|
+
);
|
|
7772
|
+
}
|
|
7773
|
+
|
|
7774
|
+
async function buildFunctionSourceWithBundledEsbuild(localPath, workspaceRoot, functionCode) {
|
|
7775
|
+
const outfile = path.join(workspaceRoot, 'dist', 'functions', functionCode, 'index.cjs');
|
|
7776
|
+
fs.mkdirSync(path.dirname(outfile), { recursive: true });
|
|
7777
|
+
const external = Array.from(
|
|
7778
|
+
new Set([...builtinModules, ...builtinModules.map(name => `node:${name}`)])
|
|
7779
|
+
);
|
|
7780
|
+
try {
|
|
7781
|
+
await esbuild.build({
|
|
7782
|
+
entryPoints: [localPath],
|
|
7783
|
+
outfile,
|
|
7784
|
+
bundle: true,
|
|
7785
|
+
platform: 'node',
|
|
7786
|
+
format: 'cjs',
|
|
7787
|
+
target: ['node20'],
|
|
7788
|
+
sourcemap: true,
|
|
7789
|
+
logLevel: 'silent',
|
|
7790
|
+
external,
|
|
7791
|
+
plugins: [
|
|
7792
|
+
{
|
|
7793
|
+
name: 'openxiangda-workspace-alias',
|
|
7794
|
+
setup(build) {
|
|
7795
|
+
build.onResolve({ filter: /^@\// }, args => ({
|
|
7796
|
+
path: resolveWorkspaceAliasPath(workspaceRoot, args.path),
|
|
7797
|
+
}));
|
|
7798
|
+
},
|
|
7799
|
+
},
|
|
7800
|
+
],
|
|
7801
|
+
});
|
|
7802
|
+
} catch (error) {
|
|
7803
|
+
fail(`App Function 内置构建失败: ${functionCode}: ${error.message}`);
|
|
7804
|
+
}
|
|
7805
|
+
print(`built functions/${functionCode} -> ${path.relative(workspaceRoot, outfile)}`);
|
|
7806
|
+
}
|
|
7807
|
+
|
|
7808
|
+
function resolveWorkspaceAliasPath(workspaceRoot, importPath) {
|
|
7809
|
+
const raw = path.join(workspaceRoot, 'src', importPath.replace(/^@\//, ''));
|
|
7810
|
+
const candidates = [
|
|
7811
|
+
raw,
|
|
7812
|
+
`${raw}.ts`,
|
|
7813
|
+
`${raw}.tsx`,
|
|
7814
|
+
`${raw}.js`,
|
|
7815
|
+
`${raw}.mjs`,
|
|
7816
|
+
`${raw}.cjs`,
|
|
7817
|
+
path.join(raw, 'index.ts'),
|
|
7818
|
+
path.join(raw, 'index.tsx'),
|
|
7819
|
+
path.join(raw, 'index.js'),
|
|
7820
|
+
];
|
|
7821
|
+
return candidates.find(candidate => fs.existsSync(candidate)) || raw;
|
|
7822
|
+
}
|
|
7823
|
+
|
|
7352
7824
|
function findWorkspaceRoot(startPath) {
|
|
7353
7825
|
if (!fs.existsSync(startPath)) {
|
|
7354
7826
|
return process.cwd();
|