openxiangda 1.0.138 → 1.0.139

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/lib/cli.js CHANGED
@@ -7079,6 +7079,12 @@ function validateResourceItem(kind, item, errors, warnings) {
7079
7079
  if (kind === 'scopeGrantSources') {
7080
7080
  if (!item.name) warnings.push(`${label}: 未声明 name,将使用 code`);
7081
7081
  if (!item.sourceFormUuid && !item.formUuid) errors.push(`${label}: 缺少 sourceFormUuid`);
7082
+ if (
7083
+ item.syncMode !== undefined &&
7084
+ !['on_write', 'manual'].includes(String(item.syncMode))
7085
+ ) {
7086
+ errors.push(`${label}: syncMode 只能是 on_write 或 manual`);
7087
+ }
7082
7088
  const subjectMappings = item.subjectMappings || item.subjects;
7083
7089
  const dimensionMappings = item.dimensionMappings || item.dimensions;
7084
7090
  if (!Array.isArray(subjectMappings) || subjectMappings.length === 0) {
@@ -7159,6 +7165,11 @@ function validateScopePermissionResources(manifest, errors, warnings) {
7159
7165
  .map(String)
7160
7166
  );
7161
7167
  const scopePolicyRefs = [];
7168
+ for (const source of manifest.scopeGrantSources || []) {
7169
+ if (String(source.syncMode || '').trim() === 'manual') {
7170
+ warnings.push(`${resourceLabel('scopeGrantSources', source)}: syncMode=manual,运行时来源表单变更不会自动物化;请确认有显式 scope sync 作业`);
7171
+ }
7172
+ }
7162
7173
  const inspectPermissionGroup = (kind, item) => {
7163
7174
  const groups = kind === 'dataViews'
7164
7175
  ? (Array.isArray(item.permissionGroups) ? item.permissionGroups : [])
@@ -10610,10 +10621,20 @@ function normalizeScopeGrantSourceManifest(source, bound) {
10610
10621
  effectiveFromField: source.effectiveFromField,
10611
10622
  effectiveToField: source.effectiveToField,
10612
10623
  filterJson: source.filterJson || source.filter,
10624
+ syncMode: normalizeScopeGrantSourceSyncMode(source.syncMode, source.sync),
10613
10625
  enabled: source.enabled !== false,
10614
10626
  });
10615
10627
  }
10616
10628
 
10629
+ function normalizeScopeGrantSourceSyncMode(value, legacySync) {
10630
+ const raw = String(value || '').trim().toLowerCase();
10631
+ if (['manual', 'off', 'none', 'disabled', 'false'].includes(raw)) {
10632
+ return 'manual';
10633
+ }
10634
+ if (legacySync === false) return 'manual';
10635
+ return 'on_write';
10636
+ }
10637
+
10617
10638
  function normalizeDataScopePolicyManifest(policy) {
10618
10639
  const code = policy.code || policy.resourceCode;
10619
10640
  return stripUndefinedValues({
@@ -135,7 +135,7 @@ const DESIGN_GATE_TOPICS = [
135
135
  '正式后台优先选择 managed-platform-account;如果平台账号已存在,选择 existing-platform-user-assignment;固定内部门户选择 static-role-permission',
136
136
  'query-param-context 仅用于低风险页面上下文、筛选条件或公开 ticket 输入,不作为敏感数据授权依据',
137
137
  '角色写 src/resources/roles,页面组写 permissions/page-groups,表单组写 permissions/form-groups,显式声明 actions/dataPermission/fieldAccessPolicy;平台内部仍兼容存储为 operations',
138
- '复杂业务范围优先写 permissions/scope-dimensions、permissions/scope-grant-sources、permissions/data-scope-policies,并在表单权限组或 data-view 权限组使用 dataPermission.type=scope_policy',
138
+ '复杂业务范围优先写 permissions/scope-dimensions、permissions/scope-grant-sources、permissions/data-scope-policies,并在表单权限组或 data-view 权限组使用 dataPermission.type=scope_policy;授权来源默认 syncMode=on_write,由平台在来源表单写入后自动物化',
139
139
  'scope_policy 只表达数据范围,actions 表达操作能力;按钮隐藏只是 UX,后端 action check 才是权威',
140
140
  'scope_policy 默认语义是个人授权 + 当前应用角色授权;多维 rules 显式 AND;空授权集合拒绝;管理员绕过但可审计',
141
141
  '能管理角色或给别人分配角色的应用角色必须在 roles manifest 声明 apiPermissionCodes;至少包含 app:role:manage,按需加入 app:page-permission-group:manage、app:form-permission-group:manage、app:organization:manage',
@@ -162,9 +162,11 @@ Default semantics:
162
162
  - Authorization source rows should include an explicit status/enabled field and
163
163
  grant sources should use `filterJson` or `enabledField` so drafts, disabled
164
164
  rows, or rejected assignments are not materialized.
165
- - Source forms must have a sync path. `sync: true` on the manifest runs during
166
- publish; runtime changes need an automation/App Function or platform API call
167
- that resyncs the affected grant source after create/update/delete.
165
+ - Source authorization forms are synced by the platform after form
166
+ create/update/delete/import when the grant source uses the default
167
+ `syncMode: "on_write"`. Use `syncMode: "manual"` only for externally
168
+ maintained sources or unusual bulk jobs; `sync: true` still performs an
169
+ immediate full sync during resource publish.
168
170
  - Policy target fields should normally be hidden scalar keys. If targeting a
169
171
  JSONB option/person/department field directly, declare `valuePath: "value"` or
170
172
  `componentType`; multi-value fields should be normalized into a scalar key or
@@ -260,6 +260,7 @@ Declare scope resources:
260
260
  { "field": "status", "op": "eq", "value": "enabled" }
261
261
  ]
262
262
  },
263
+ "syncMode": "on_write",
263
264
  "sync": true
264
265
  }
265
266
  ```
@@ -354,10 +355,11 @@ Default runtime semantics:
354
355
  - `filterJson` filters authorization source rows before materializing grants.
355
356
  Use it for enabled/approved/effective authorization rows; unsupported filter
356
357
  operations do not match.
357
- - Source authorization forms must be synced after changes. For formal apps,
358
- create an automation/App Function that runs `openxiangda scope sync` or the
359
- equivalent platform API after authorization rows are created, updated, or
360
- deleted. Publishing with `"sync": true` only syncs during resource publish.
358
+ - Source authorization forms are synced by the platform after create/update/
359
+ delete/import when the grant source uses the default `syncMode: "on_write"`.
360
+ Use `syncMode: "manual"` only for externally maintained sources or unusual
361
+ bulk jobs that explicitly call `openxiangda scope sync`. Publishing with
362
+ `"sync": true` still performs an immediate full sync during resource publish.
361
363
  - App Function `ctx.form.queryOne/queryMany/getById` enforces the caller's view
362
364
  permission when there is a real current user. No matching view permission
363
365
  group means deny; backend system automations can still use trusted internal
@@ -334,6 +334,7 @@ Grant source:
334
334
  { "field": "status", "op": "eq", "value": "enabled" }
335
335
  ]
336
336
  },
337
+ "syncMode": "on_write",
337
338
  "sync": true
338
339
  }
339
340
  ```
@@ -373,9 +374,12 @@ Data View permission groups use the same `dataPermission` object inside
373
374
  default; large grant sets use DB `EXISTS` instead of huge `IN` SQL. Prefer
374
375
  hidden scalar target fields such as `collegeCode`; if the target is a JSONB
375
376
  option/person/department field, set `valuePath: "value"` or `componentType`.
376
- Runtime changes to the authorization source form need an automation/App
377
- Function or explicit platform API call to resync the grant source; manifest
378
- `sync: true` only runs during resource publish.
377
+ Authorization source form changes are synced by the platform after
378
+ create/update/delete/import when the grant source uses the default
379
+ `syncMode: "on_write"`. Use `syncMode: "manual"` only for externally maintained
380
+ sources or special bulk jobs that explicitly call `openxiangda scope sync`.
381
+ Manifest `sync: true` still performs an immediate full sync during resource
382
+ publish.
379
383
 
380
384
  ## 3. Connector — `src/resources/connectors/<code>.json`
381
385
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.138",
3
+ "version": "1.0.139",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {