openxiangda 1.0.239 → 1.0.241

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
@@ -7292,6 +7292,11 @@ async function runAppReleaseCommand(
7292
7292
  environmentControl: appReleaseEnvironmentControl(flags),
7293
7293
  }
7294
7294
  );
7295
+ const finalizedDataViews = await finalizeDeferredDataViewResources(
7296
+ config,
7297
+ target,
7298
+ readCommaSeparatedFlag(flags, 'finalize-data-views')
7299
+ );
7295
7300
  const bindingVerification =
7296
7301
  await verifyFinalizedBackendBindingContracts(
7297
7302
  config,
@@ -7336,6 +7341,9 @@ async function runAppReleaseCommand(
7336
7341
  prepared,
7337
7342
  verified,
7338
7343
  activated,
7344
+ ...(finalizedDataViews
7345
+ ? { finalizedDataViews }
7346
+ : {}),
7339
7347
  ...(bindingVerification ? { bindingVerification } : {}),
7340
7348
  ...(healthWait ? { healthWait } : {}),
7341
7349
  ...(stateReconcileWarning ? { stateReconcileWarning } : {}),
@@ -14380,9 +14388,16 @@ async function resource(args) {
14380
14388
  manifest.scopeGrantSources || []
14381
14389
  )
14382
14390
  : [];
14391
+ const dataViewFormCodes = stagedChangeId
14392
+ ? resolveDataViewSourceFormCodes(
14393
+ manifest.dataViews || [],
14394
+ target.bound
14395
+ )
14396
+ : [];
14383
14397
  const stagedContractFormCodes = unique([
14384
14398
  ...workflowFormCodes,
14385
14399
  ...scopeGrantSourceFormCodes,
14400
+ ...dataViewFormCodes,
14386
14401
  ]).sort();
14387
14402
  if (
14388
14403
  stagedContractFormCodes.length > 0 &&
@@ -14509,6 +14524,20 @@ async function resource(args) {
14509
14524
  const publishLease = await telemetry.runPhase('lease', async () =>
14510
14525
  resolvePublishLeaseForWrite(config, target, flags)
14511
14526
  );
14527
+ const deliveryV2BackendManifestIntent =
14528
+ resolveDeliveryV2BackendManifestReplacementIntent({
14529
+ flags,
14530
+ typeFilters,
14531
+ codeFilters,
14532
+ manifest,
14533
+ });
14534
+ const publishLeaseFlags = deliveryV2BackendManifestIntent
14535
+ ? {
14536
+ ...flags,
14537
+ reason: deliveryV2BackendManifestIntent.reason,
14538
+ 'replace-reason': deliveryV2BackendManifestIntent.reason,
14539
+ }
14540
+ : flags;
14512
14541
  const changeBaselinePreflight = await telemetry.runPhase(
14513
14542
  'preflight',
14514
14543
  {
@@ -14538,10 +14567,12 @@ async function resource(args) {
14538
14567
  pruneCandidates,
14539
14568
  publishLeaseId: publishLease?.leaseId,
14540
14569
  publishLease,
14541
- publishLeaseFlags: flags,
14570
+ publishLeaseFlags,
14542
14571
  scopedFiles,
14543
14572
  baseRef: flags.since || 'HEAD',
14544
- replaceManifest: Boolean(flags['replace-manifest']),
14573
+ replaceManifest:
14574
+ Boolean(flags['replace-manifest']) ||
14575
+ Boolean(deliveryV2BackendManifestIntent),
14545
14576
  changeId:
14546
14577
  flags.change || getStoredChangeBaseline(target)?.changeId || null,
14547
14578
  stagedFormBindingOverlays,
@@ -14557,6 +14588,10 @@ async function resource(args) {
14557
14588
  Boolean(typeFilters?.has('workflows')),
14558
14589
  })
14559
14590
  );
14591
+ if (deliveryV2BackendManifestIntent) {
14592
+ result.deliveryV2BackendManifestReplacement =
14593
+ deliveryV2BackendManifestIntent;
14594
+ }
14560
14595
  if (changeBaselinePreflight) {
14561
14596
  result.changeBaselinePreflight = changeBaselinePreflight;
14562
14597
  }
@@ -23789,6 +23824,10 @@ async function findExistingScopeResource(config, target, segment, code) {
23789
23824
  }
23790
23825
 
23791
23826
  async function publishDataViewResources(config, target, dataViews, result, options = {}) {
23827
+ const stagedFormBindingOverlays =
23828
+ options.stagedFormBindingOverlays instanceof Map
23829
+ ? options.stagedFormBindingOverlays
23830
+ : new Map();
23792
23831
  for (const dataViewItem of dataViews) {
23793
23832
  if (shouldSkipNoopResource(options, 'dataView', dataViewItem.code)) {
23794
23833
  recordNoopResource(result, 'dataView', dataViewItem.code, options);
@@ -23796,6 +23835,18 @@ async function publishDataViewResources(config, target, dataViews, result, optio
23796
23835
  }
23797
23836
  const existing = await findExistingDataView(config, target, dataViewItem.code);
23798
23837
  const body = normalizeDataViewManifest(target.bound, dataViewItem);
23838
+ const stagedFormReleases = options.finalizeDeferredDataViews
23839
+ ? []
23840
+ : stagedDataViewFormReleaseDependencies(
23841
+ body,
23842
+ stagedFormBindingOverlays
23843
+ );
23844
+ if (stagedFormReleases.length > 0) {
23845
+ body.stagedFormReleases = stagedFormReleases;
23846
+ }
23847
+ if (options.finalizeDeferredDataViews) {
23848
+ body.finalizeDeferred = true;
23849
+ }
23799
23850
  const data = existing
23800
23851
  ? await requestWithAuth(
23801
23852
  config,
@@ -23819,10 +23870,26 @@ async function publishDataViewResources(config, target, dataViews, result, optio
23819
23870
  result.published.push({
23820
23871
  kind: 'dataView',
23821
23872
  code: dataViewItem.code,
23822
- action: existing ? 'update' : 'create',
23873
+ action: options.finalizeDeferredDataViews
23874
+ ? data?.status === 'active'
23875
+ ? 'finalize'
23876
+ : 'noop'
23877
+ : existing
23878
+ ? 'update'
23879
+ : 'create',
23823
23880
  id: data?.id,
23881
+ status: data?.status || null,
23882
+ ...(stagedFormReleases.length > 0
23883
+ ? {
23884
+ activation: 'deferred-until-form-activation',
23885
+ stagedFormReleaseCount: stagedFormReleases.length,
23886
+ }
23887
+ : {}),
23824
23888
  });
23825
- if (Array.isArray(body.permissionGroups)) {
23889
+ if (
23890
+ !options.finalizeDeferredDataViews &&
23891
+ Array.isArray(body.permissionGroups)
23892
+ ) {
23826
23893
  await publishDataViewPermissionGroups(config, target, dataViewItem.code, body.permissionGroups);
23827
23894
  result.published.push({
23828
23895
  kind: 'dataViewPermissionGroup',
@@ -23833,6 +23900,128 @@ async function publishDataViewResources(config, target, dataViews, result, optio
23833
23900
  }
23834
23901
  }
23835
23902
 
23903
+ function stagedDataViewFormReleaseDependencies(
23904
+ body,
23905
+ stagedFormBindingOverlays = new Map()
23906
+ ) {
23907
+ const overlaysByFormUuid = new Map(
23908
+ Array.from(stagedFormBindingOverlays.values())
23909
+ .map(overlay => [String(overlay?.formUuid || '').trim(), overlay])
23910
+ .filter(([formUuid]) => Boolean(formUuid))
23911
+ );
23912
+ const sources = [
23913
+ body?.definition?.base,
23914
+ ...(body?.definition?.joins || []),
23915
+ ].filter(Boolean);
23916
+ const dependencies = new Map();
23917
+ for (const source of sources) {
23918
+ const formUuid = String(source?.formUuid || '').trim();
23919
+ const staged = overlaysByFormUuid.get(formUuid);
23920
+ if (!staged) continue;
23921
+ dependencies.set(formUuid, {
23922
+ formUuid,
23923
+ releaseId: staged.releaseId,
23924
+ contentHash: staged.contentHash,
23925
+ baseRevision: staged.baseRevision,
23926
+ parentReleaseId: staged.parentReleaseId,
23927
+ });
23928
+ }
23929
+ return Array.from(dependencies.values()).sort((left, right) =>
23930
+ left.formUuid.localeCompare(right.formUuid)
23931
+ );
23932
+ }
23933
+
23934
+ function resolveDataViewSourceFormCodes(dataViews, bound) {
23935
+ const codeByFormUuid = new Map(
23936
+ Object.entries(bound.resources?.forms || {})
23937
+ .map(([formCode, resource]) => [
23938
+ String(resource?.formUuid || '').trim(),
23939
+ formCode,
23940
+ ])
23941
+ .filter(([formUuid]) => Boolean(formUuid))
23942
+ );
23943
+ const formCodes = [];
23944
+ for (const dataView of dataViews || []) {
23945
+ const definition =
23946
+ dataView?.definition !== undefined
23947
+ ? dataView.definition
23948
+ : dataView;
23949
+ const sources = [
23950
+ definition?.base,
23951
+ ...(definition?.joins || []),
23952
+ ].filter(Boolean);
23953
+ for (const source of sources) {
23954
+ const formCode = String(
23955
+ source?.formCode ||
23956
+ source?.form ||
23957
+ (source?.kind === 'form' ? source?.code : '') ||
23958
+ codeByFormUuid.get(String(source?.formUuid || '').trim()) ||
23959
+ ''
23960
+ ).trim();
23961
+ if (formCode) formCodes.push(formCode);
23962
+ }
23963
+ }
23964
+ return unique(formCodes).sort();
23965
+ }
23966
+
23967
+ async function finalizeDeferredDataViewResources(
23968
+ config,
23969
+ target,
23970
+ codes
23971
+ ) {
23972
+ if (!Array.isArray(codes) || codes.length === 0) return null;
23973
+ const selectors = codes.map(code => ({
23974
+ key: 'dataViews',
23975
+ code,
23976
+ raw: code,
23977
+ }));
23978
+ const manifest = loadWorkspaceResources({
23979
+ typeFilters: new Set(['dataViews']),
23980
+ codeFilters: selectors,
23981
+ });
23982
+ if ((manifest.unmatchedResourceCodeFilters || []).length > 0) {
23983
+ fail(
23984
+ `DATA_VIEW_FINALIZE_SOURCE_MISSING: App finalize 找不到数据视图源码: ${manifest.unmatchedResourceCodeFilters.join(', ')}`
23985
+ );
23986
+ }
23987
+ const result = {
23988
+ appType: target.appType,
23989
+ profile: target.profileName,
23990
+ published: [],
23991
+ warnings: [],
23992
+ };
23993
+ await publishDataViewResources(
23994
+ config,
23995
+ target,
23996
+ manifest.dataViews || [],
23997
+ result,
23998
+ {
23999
+ skipNoop: false,
24000
+ finalizeDeferredDataViews: true,
24001
+ }
24002
+ );
24003
+ const notActive = result.published.filter(
24004
+ item => item.kind === 'dataView' && item.status !== 'active'
24005
+ );
24006
+ if (notActive.length > 0) {
24007
+ fail(
24008
+ `DATA_VIEW_FINALIZE_INCOMPLETE: ${notActive
24009
+ .map(item => `${item.code}:${item.status || 'unknown'}`)
24010
+ .join(', ')}`
24011
+ );
24012
+ }
24013
+ return {
24014
+ requested: codes,
24015
+ finalized: result.published
24016
+ .filter(item => item.kind === 'dataView')
24017
+ .map(item => ({
24018
+ code: item.code,
24019
+ id: item.id || null,
24020
+ status: item.status,
24021
+ })),
24022
+ };
24023
+ }
24024
+
23836
24025
  async function publishStorageConfigResources(config, target, storageConfigs, result, options = {}) {
23837
24026
  for (const storageItem of storageConfigs) {
23838
24027
  const code = storageItem.code || storageItem.resourceCode;
@@ -28096,6 +28285,86 @@ function shouldReplaceResourceManifestItem(item, resourceKey, code, options = {}
28096
28285
  return options.replaceManifest === true;
28097
28286
  }
28098
28287
 
28288
+ function resolveDeliveryV2BackendManifestReplacementIntent({
28289
+ flags = {},
28290
+ typeFilters = null,
28291
+ codeFilters = null,
28292
+ manifest = {},
28293
+ deliveryContext = activeDeliveryV2Context,
28294
+ } = {}) {
28295
+ if (!deliveryContext || !flags['stage-only']) return null;
28296
+ const selectedTypes = Array.from(typeFilters || []);
28297
+ if (
28298
+ selectedTypes.length === 0 ||
28299
+ selectedTypes.some(
28300
+ type => !['functions', 'automations'].includes(type)
28301
+ )
28302
+ ) {
28303
+ return null;
28304
+ }
28305
+ if (!Array.isArray(codeFilters) || codeFilters.length === 0) {
28306
+ const error = new Error(
28307
+ 'DELIVERY_BACKEND_SCOPE_MISMATCH: Delivery V2 Backend stage 缺少冻结的精确 selector'
28308
+ );
28309
+ error.code = 'DELIVERY_BACKEND_SCOPE_MISMATCH';
28310
+ error.retryable = false;
28311
+ throw error;
28312
+ }
28313
+ const expectedSelectors = unique([
28314
+ ...(deliveryContext.targets?.functions || []).map(
28315
+ code => `Function:${String(code)}`
28316
+ ),
28317
+ ...(deliveryContext.targets?.automations || []).map(
28318
+ code => `Automation:${String(code)}`
28319
+ ),
28320
+ ]).sort();
28321
+ const actualSelectors = unique([
28322
+ ...(manifest.functions || []).map(
28323
+ item => `Function:${getResourceItemCode(item)}`
28324
+ ),
28325
+ ...(manifest.automations || []).map(
28326
+ item => `Automation:${getResourceItemCode(item)}`
28327
+ ),
28328
+ ])
28329
+ .filter(selector => !selector.endsWith(':'))
28330
+ .sort();
28331
+ if (
28332
+ expectedSelectors.length === 0 ||
28333
+ JSON.stringify(actualSelectors) !== JSON.stringify(expectedSelectors)
28334
+ ) {
28335
+ const error = new Error(
28336
+ `DELIVERY_BACKEND_SCOPE_MISMATCH: Delivery V2 Backend stage 与 ReleaseRun 冻结范围不一致;expected=[${expectedSelectors.join(
28337
+ ','
28338
+ )}] actual=[${actualSelectors.join(',')}]`
28339
+ );
28340
+ error.code = 'DELIVERY_BACKEND_SCOPE_MISMATCH';
28341
+ error.retryable = false;
28342
+ error.expectedSelectors = expectedSelectors;
28343
+ error.actualSelectors = actualSelectors;
28344
+ throw error;
28345
+ }
28346
+ const runId = String(deliveryContext.runId || '').trim();
28347
+ const packageDigest = String(deliveryContext.packageDigest || '').trim();
28348
+ if (!runId || !/^[a-f0-9]{64}$/.test(packageDigest)) {
28349
+ const error = new Error(
28350
+ 'DELIVERY_CONTROL_INVALID: Delivery V2 Backend stage 缺少 ReleaseRun/packageDigest 权威上下文'
28351
+ );
28352
+ error.code = 'DELIVERY_CONTROL_INVALID';
28353
+ error.retryable = false;
28354
+ throw error;
28355
+ }
28356
+ return {
28357
+ mode: 'manifest_replacement',
28358
+ authority: 'delivery_v2_sealed_package',
28359
+ runId,
28360
+ packageDigest,
28361
+ selectors: actualSelectors,
28362
+ reason:
28363
+ `Delivery V2 ReleaseRun ${runId} sealed package ${packageDigest} ` +
28364
+ 'authoritative backend manifest and binding contract replacement',
28365
+ };
28366
+ }
28367
+
28099
28368
  function buildAutomationSourcePatchSources(existingDefinition, desiredDefinition) {
28100
28369
  const existing = existingDefinition || {};
28101
28370
  const desired = desiredDefinition || {};
@@ -29431,6 +29700,16 @@ function readStringFlag(flags, key) {
29431
29700
  return String(value).trim();
29432
29701
  }
29433
29702
 
29703
+ function readCommaSeparatedFlag(flags, key) {
29704
+ const value = readStringFlag(flags, key);
29705
+ return unique(
29706
+ value
29707
+ .split(',')
29708
+ .map(item => item.trim())
29709
+ .filter(Boolean)
29710
+ ).sort();
29711
+ }
29712
+
29434
29713
  function normalizeWorkspacePublishOptions(flags) {
29435
29714
  const targetForm = readStringFlag(flags, 'form');
29436
29715
  const targetPage = readStringFlag(flags, 'page');
@@ -29812,5 +30091,8 @@ module.exports = {
29812
30091
  buildScopeGrantSourceSyncDecision,
29813
30092
  main,
29814
30093
  resolveReleaseCommandScopedFiles,
30094
+ resolveDeliveryV2BackendManifestReplacementIntent,
30095
+ resolveDataViewSourceFormCodes,
29815
30096
  satisfiedFormCodesFromContext,
30097
+ stagedDataViewFormReleaseDependencies,
29816
30098
  };
@@ -882,6 +882,10 @@ function compactStepResult(stepId, result, context = {}) {
882
882
  if (bindingContracts) {
883
883
  compact.backendBindingContracts = bindingContracts;
884
884
  }
885
+ if (result.deliveryV2BackendManifestReplacement) {
886
+ compact.backendManifestReplacement =
887
+ result.deliveryV2BackendManifestReplacement;
888
+ }
885
889
  }
886
890
  return compact;
887
891
  }
@@ -327,6 +327,9 @@ function buildWorkspaceReleaseSteps(
327
327
  const stagesFormRelease =
328
328
  runtimeMode === 'react-spa' &&
329
329
  (targets.forms.length > 0 || formPermissionGroups.length > 0);
330
+ const deferredDataViewCodes = stagesFormRelease
331
+ ? uniqueSorted(targets.resourceSelectors?.dataViews || [])
332
+ : [];
330
333
  const directConfigurationSteps =
331
334
  buildDirectConfigurationReleaseSteps(
332
335
  targets,
@@ -437,6 +440,12 @@ function buildWorkspaceReleaseSteps(
437
440
  `.openxiangda/releases/${changeId || 'change'}/staged-resources.json`,
438
441
  ]
439
442
  : []),
443
+ ...(deferredDataViewCodes.length > 0
444
+ ? [
445
+ '--finalize-data-views',
446
+ deferredDataViewCodes.join(','),
447
+ ]
448
+ : []),
440
449
  '--wait',
441
450
  ],
442
451
  profileArg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.239",
3
+ "version": "1.0.241",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {