openyida 2026.7.18 → 2026.7.21

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.
Files changed (200) hide show
  1. package/README.md +37 -2
  2. package/bin/yida.js +113 -18
  3. package/docs/capabilities.md +202 -0
  4. package/lib/agent-center/agent-center.js +2 -3
  5. package/lib/app/app-list.js +3 -4
  6. package/lib/app/build-page.js +13 -6
  7. package/lib/app/check-page.js +16 -6
  8. package/lib/app/compile.js +17 -7
  9. package/lib/app/create-app.js +14 -2
  10. package/lib/app/create-form/api-path.js +25 -0
  11. package/lib/app/create-form/args.js +13 -0
  12. package/lib/app/create-form/commands.js +4 -0
  13. package/lib/app/create-form/definition-reader.js +23 -23
  14. package/lib/app/create-form/field-normalizers.js +26 -7
  15. package/lib/app/create-form/schema-patch.js +4 -1
  16. package/lib/app/create-form.js +155 -68
  17. package/lib/app/create-page.js +47 -10
  18. package/lib/app/generate-page.js +20 -1
  19. package/lib/app/get-form-config.js +2 -1
  20. package/lib/app/get-schema.js +67 -15
  21. package/lib/app/import-app.js +94 -42
  22. package/lib/app/list-forms.js +4 -8
  23. package/lib/app/page-ir.js +140 -0
  24. package/lib/app/publish.js +289 -556
  25. package/lib/app/schema-field-resolution.js +370 -0
  26. package/lib/app/services/app-reader.js +85 -0
  27. package/lib/app/services/app-service.js +235 -0
  28. package/lib/app/services/canvas-page-compiler.js +91 -0
  29. package/lib/app/services/canvas-page-schema-builder.js +149 -0
  30. package/lib/app/services/field-bindings.js +68 -0
  31. package/lib/app/services/form-compiler.js +1922 -0
  32. package/lib/app/services/form-mode-service.js +182 -0
  33. package/lib/app/services/form-schema-patcher.js +333 -0
  34. package/lib/app/services/form-schema-reader.js +59 -0
  35. package/lib/app/services/form-service.js +379 -0
  36. package/lib/app/services/form-validation.js +562 -0
  37. package/lib/app/services/native-page-compiler.js +92 -0
  38. package/lib/app/services/native-page-schema-builder.js +428 -0
  39. package/lib/app/services/page-resource-service.js +185 -0
  40. package/lib/app/update-app.js +22 -9
  41. package/lib/app/update-form-config.js +4 -2
  42. package/lib/asset/asset-cmd.js +19 -6
  43. package/lib/auth/oauth-loopback.js +376 -15
  44. package/lib/auth/token-auth.js +34 -3
  45. package/lib/auth/token-store.js +22 -4
  46. package/lib/basic-info/basic-info.js +2 -1
  47. package/lib/bridge/bridge.js +43 -18
  48. package/lib/core/agent-capabilities.js +5 -3
  49. package/lib/core/command-errors.js +38 -0
  50. package/lib/core/command-manifest.js +59 -1
  51. package/lib/core/copy.js +12 -0
  52. package/lib/core/doctor.js +15 -4
  53. package/lib/core/env-cmd.js +1 -1
  54. package/lib/core/env.js +7 -6
  55. package/lib/core/i18n.js +66 -8
  56. package/lib/core/legacy-schema-guard.js +408 -0
  57. package/lib/core/locales/en.js +38 -46
  58. package/lib/core/locales/zh.js +30 -46
  59. package/lib/core/sample.js +12 -2
  60. package/lib/core/utils.js +334 -66
  61. package/lib/core/yida-client.js +108 -5
  62. package/lib/corp-efficiency/corp-efficiency.js +2 -2
  63. package/lib/corp-manager/corp-manager.js +3 -3
  64. package/lib/formula/evaluate.js +11 -68
  65. package/lib/formula/field-refs.js +83 -0
  66. package/lib/i18n-management/i18n-management.js +2 -3
  67. package/lib/process/configure-process.js +44 -1992
  68. package/lib/process/create-process.js +54 -87
  69. package/lib/process/preview-process.js +20 -8
  70. package/lib/process/services/process-compiler.js +2056 -0
  71. package/lib/process/services/process-reader.js +232 -0
  72. package/lib/process/services/process-resource-service.js +571 -0
  73. package/lib/process/services/process-service.js +122 -0
  74. package/lib/process/services/process-stage-checkpoint.js +246 -0
  75. package/lib/report/append.js +7 -26
  76. package/lib/report/http.js +31 -5
  77. package/lib/report/index.js +10 -2
  78. package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
  79. package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
  80. package/lib/samples/yida-canvas-custom-page/dashboard-starter.canvas.jsx +5 -2
  81. package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
  82. package/lib/samples/yida-canvas-custom-page/portal-shell-home.canvas.jsx +2 -2
  83. package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +206 -10
  84. package/lib/samples/yida-custom-page/data-management.oyd.jsx +14 -3
  85. package/lib/samples/yida-custom-page/design-tokens.js +2 -2
  86. package/lib/samples/yida-custom-page/detail-profile.oyd.jsx +1 -1
  87. package/lib/samples/yida-custom-page/portal-shell-home.oyd.jsx +7 -2
  88. package/lib/samples/yida-custom-page/split-pane-detail.oyd.jsx +2 -1
  89. package/lib/schema/adapters/app-adapter.js +210 -0
  90. package/lib/schema/adapters/form-adapter.js +1362 -0
  91. package/lib/schema/adapters/keys.js +19 -0
  92. package/lib/schema/adapters/page-adapter.js +566 -0
  93. package/lib/schema/adapters/process-adapter.js +519 -0
  94. package/lib/schema/applier.js +1878 -0
  95. package/lib/schema/apply-store.js +1083 -0
  96. package/lib/schema/command.js +645 -0
  97. package/lib/schema/dependency-graph.js +83 -0
  98. package/lib/schema/errors.js +481 -0
  99. package/lib/schema/hash.js +9 -0
  100. package/lib/schema/manifest-limits.js +177 -0
  101. package/lib/schema/manifest-loader.js +309 -0
  102. package/lib/schema/manifest-schema-v1.json +193 -0
  103. package/lib/schema/normalize-manifest.js +281 -0
  104. package/lib/schema/page-canvas-foundation.js +447 -0
  105. package/lib/schema/page-data-source-builder.js +561 -0
  106. package/lib/schema/page-foundation.js +556 -0
  107. package/lib/schema/page-source-loader.js +314 -0
  108. package/lib/schema/planner.js +691 -0
  109. package/lib/schema/remote-dispatch-boundary.js +39 -0
  110. package/lib/schema/remote-missing.js +21 -0
  111. package/lib/schema/remote-reader.js +189 -0
  112. package/lib/schema/resource-registry.js +157 -0
  113. package/lib/schema/server-revision.js +71 -0
  114. package/lib/schema/sort.js +28 -0
  115. package/lib/schema/state-store.js +589 -0
  116. package/package.json +10 -8
  117. package/scripts/check-release-risks.js +254 -0
  118. package/scripts/generate-command-docs.js +6 -1
  119. package/scripts/i18n-baseline.json +11 -11
  120. package/scripts/postinstall.js +8 -12
  121. package/scripts/validate-ci.sh +15 -11
  122. package/scripts/validate-i18n.js +30 -9
  123. package/scripts/validate-package-size.js +5 -2
  124. package/scripts/validate-skills.js +4 -0
  125. package/scripts/validate-structure.js +15 -0
  126. package/yida-skills/SKILL.md +117 -30
  127. package/yida-skills/references/schema-as-code-phase1.md +99 -0
  128. package/yida-skills/references/setup-and-env.md +59 -59
  129. package/yida-skills/references/task-retrospective.md +6 -6
  130. package/yida-skills/skills/yida-app/SKILL.md +78 -17
  131. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
  132. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
  133. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +15 -0
  134. package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
  135. package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
  136. package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
  137. package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
  138. package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
  139. package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
  140. package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
  141. package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
  142. package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
  143. package/yida-skills/skills/yida-integration/SKILL.md +7 -1
  144. package/yida-skills/skills/yida-login/SKILL.md +50 -99
  145. package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
  146. package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
  147. package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
  148. package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
  149. package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
  150. package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
  151. package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
  152. package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
  153. package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
  154. package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
  155. package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
  156. package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
  157. package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
  158. package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
  159. package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
  160. package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
  161. package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
  162. package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
  163. package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
  164. package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
  165. package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
  166. package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
  167. package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
  168. package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
  169. package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
  170. package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
  171. package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
  172. package/yida-skills/skills/yida-report/SKILL.md +9 -235
  173. package/yida-skills/skills/yida-report/references/schema-builder-details.md +239 -0
  174. package/yida-skills/skills/yida-skill-evaluator/SKILL.md +170 -0
  175. package/yida-skills/skills-index.json +238 -54
  176. package/lib/core/locales/ar.js +0 -834
  177. package/lib/core/locales/de.js +0 -834
  178. package/lib/core/locales/es.js +0 -834
  179. package/lib/core/locales/fr.js +0 -834
  180. package/lib/core/locales/hi.js +0 -834
  181. package/lib/core/locales/ja.js +0 -1227
  182. package/lib/core/locales/ko.js +0 -836
  183. package/lib/core/locales/pt.js +0 -834
  184. package/lib/core/locales/vi.js +0 -834
  185. package/lib/core/locales/zh-HK.js +0 -1263
  186. package/project/pages/src/demo-birthday-game.oyd.jsx +0 -832
  187. package/project/pages/src/demo-chip-insight.oyd.jsx +0 -983
  188. package/project/pages/src/demo-compat-smoke.oyd.jsx +0 -58
  189. package/project/pages/src/demo-crm-batch-entry.oyd.jsx +0 -805
  190. package/project/pages/src/demo-crm-dashboard.oyd.jsx +0 -677
  191. package/project/pages/src/demo-future-vision-2026.oyd.jsx +0 -1102
  192. package/project/pages/src/demo-ppt.oyd.jsx +0 -1192
  193. package/project/pages/src/demo-salary-calculator.oyd.jsx +0 -904
  194. package/project/pages/src/openyida-knowledge-doc.oyd.jsx +0 -1714
  195. package/yida-skills/skills/large-file-write/SKILL.md +0 -93
  196. package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
  197. package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
  198. package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
  199. package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
  200. package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
@@ -0,0 +1,571 @@
1
+ 'use strict';
2
+
3
+ const { isDeepStrictEqual } = require('util');
4
+ const {
5
+ compileProcessDefinition,
6
+ validateManagedNodeBindings,
7
+ } = require('./process-compiler');
8
+ const { projectProcessManaged, readProcessDefinition } = require('./process-reader');
9
+ const { readFormMode } = require('../../app/services/form-mode-service');
10
+ const {
11
+ newDraftProcess,
12
+ publishProcessById,
13
+ queryProcessVersions,
14
+ saveProcessById,
15
+ } = require('./process-service');
16
+ const { hasRemoteDispatchBoundary } = require('../../schema/remote-dispatch-boundary');
17
+ const { isTokenAuthRef } = require('../../core/yida-client');
18
+
19
+ const VERSION_PAGE_SIZE = 10;
20
+ const MAX_VERSION_ROWS = 1000;
21
+ const VERSION_STATUSES = new Set(['PUBLISHED', 'SAVED', 'INVALID']);
22
+
23
+ class ProcessResourceServiceError extends Error {
24
+ constructor(code, message, details) {
25
+ super(message);
26
+ this.name = 'ProcessResourceServiceError';
27
+ this.code = code;
28
+ this.details = details;
29
+ }
30
+ }
31
+
32
+ async function readProcessVersionSnapshot(context, input) {
33
+ if (!isNonEmptyString(input && input.appType) || !isNonEmptyString(input && input.processCode)) {
34
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
35
+ }
36
+ const query = resolveService(context, 'queryProcessVersions', queryProcessVersions);
37
+ const rows = [];
38
+ let totalCount;
39
+ let totalPages = 1;
40
+
41
+ // The API is ordered by modification time; only status identifies the active version.
42
+ for (let pageIndex = 1; pageIndex <= totalPages; pageIndex += 1) {
43
+ let result;
44
+ try {
45
+ assertRemoteDispatchBoundary(context, 'before');
46
+ const queryOptions = { pageIndex, pageSize: VERSION_PAGE_SIZE };
47
+ if (query === queryProcessVersions) {
48
+ queryOptions.oneShot = true;
49
+ }
50
+ result = await query(
51
+ resolveAuthRef(context),
52
+ input.appType,
53
+ input.processCode,
54
+ '',
55
+ queryOptions
56
+ );
57
+ assertRemoteDispatchBoundary(context, 'after');
58
+ } catch (error) {
59
+ rethrowApplyBoundaryFailure(error);
60
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
61
+ }
62
+ const content = result && result.success === true && result.content;
63
+ const currentPage = normalizeCount(content && content.currentPage);
64
+ const responseTotalCount = normalizeCount(content && content.totalCount);
65
+ const pageRows = content && content.data;
66
+ if (
67
+ !Array.isArray(pageRows) ||
68
+ currentPage !== pageIndex ||
69
+ responseTotalCount === null ||
70
+ responseTotalCount < 1 ||
71
+ responseTotalCount > MAX_VERSION_ROWS ||
72
+ pageRows.length === 0 ||
73
+ pageRows.length > VERSION_PAGE_SIZE
74
+ ) {
75
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
76
+ }
77
+ if (pageIndex === 1) {
78
+ totalCount = responseTotalCount;
79
+ totalPages = Math.ceil(totalCount / VERSION_PAGE_SIZE);
80
+ } else if (responseTotalCount !== totalCount) {
81
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
82
+ }
83
+ rows.push(...pageRows);
84
+ }
85
+
86
+ if (rows.length !== totalCount) {
87
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
88
+ }
89
+
90
+ const normalizedRows = rows.map(row => normalizeVersionRow(row, input.processCode));
91
+ if (normalizedRows.some(row => row === null)) {
92
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
93
+ }
94
+ const processIds = new Set();
95
+ const versions = new Set();
96
+ for (const row of normalizedRows) {
97
+ if (processIds.has(row.processId) || versions.has(row.processVersion)) {
98
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
99
+ }
100
+ processIds.add(row.processId);
101
+ versions.add(row.processVersion);
102
+ }
103
+ normalizedRows.sort(compareVersionRows);
104
+ const published = normalizedRows.filter(row => row.status === 'PUBLISHED');
105
+ if (published.length !== 1) {
106
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
107
+ }
108
+ return {
109
+ active: published[0],
110
+ drafts: normalizedRows.filter(row => row.status === 'SAVED'),
111
+ rows: normalizedRows,
112
+ };
113
+ }
114
+
115
+ async function prepareProcessResource(context, input) {
116
+ assertProcessWriteReady(context);
117
+ const recovery = input && input.recovery && input.recovery.stageCheckpoint;
118
+ if (recovery && !hasCompleteCheckpointIdentity(recovery, input)) {
119
+ throw resourceError('PROCESS_RESOURCE_RECONCILIATION_REQUIRED', 'checkpoint_validate');
120
+ }
121
+ const identity = recovery
122
+ ? identityFromCheckpoint(recovery, input)
123
+ : await resolveNextProcessIdentity(context, input);
124
+ const existingBindings = recovery && recovery.nodeBindings || input.existingBindings || {};
125
+ let compiled;
126
+ try {
127
+ compiled = compileProcessDefinition(input.desired, {
128
+ appType: identity.appType,
129
+ baseUrl: input.baseUrl,
130
+ existingBindings,
131
+ formUuid: identity.formUuid,
132
+ processCode: identity.processCode,
133
+ });
134
+ } catch {
135
+ throw resourceError('PROCESS_RESOURCE_COMPILE_FAILED', 'compile');
136
+ }
137
+ return {
138
+ ...identity,
139
+ compiled,
140
+ };
141
+ }
142
+
143
+ function assertProcessWriteReady(context) {
144
+ if (!hasRemoteDispatchBoundary(context) || hasInjectedProcessWriter(context)) {
145
+ return;
146
+ }
147
+ const authRef = resolveAuthRef(context);
148
+ if (!authRef || typeof authRef.baseUrl !== 'string' || !authRef.baseUrl) {
149
+ throw resourceError('PROCESS_RESOURCE_WRITE_PRECHECK_FAILED', 'write_precheck');
150
+ }
151
+ if (isTokenAuthRef(authRef)) {
152
+ return;
153
+ }
154
+ if (
155
+ typeof authRef.csrfToken !== 'string' || !authRef.csrfToken ||
156
+ !Array.isArray(authRef.cookies)
157
+ ) {
158
+ throw resourceError('PROCESS_RESOURCE_WRITE_PRECHECK_FAILED', 'write_precheck');
159
+ }
160
+ }
161
+
162
+ function hasInjectedProcessWriter(context) {
163
+ const services = context && context.services || {};
164
+ return ['newDraftProcess', 'saveProcessById', 'publishProcessById'].every(name => (
165
+ typeof services[name] === 'function'
166
+ ));
167
+ }
168
+
169
+ async function applyProcessResource(context, input) {
170
+ const prepared = input && input.prepared;
171
+ const checkpoint = input && input.recovery && input.recovery.stageCheckpoint;
172
+ if (!prepared || !prepared.compiled || typeof context.checkpointStage !== 'function') {
173
+ throw resourceError('PROCESS_RESOURCE_WRITE_FAILED', 'prepare');
174
+ }
175
+
176
+ let processId = checkpoint && checkpoint.processId;
177
+ const processVersion = checkpoint && checkpoint.processVersion !== undefined
178
+ ? checkpoint.processVersion
179
+ : prepared.processVersion;
180
+ let stage = checkpoint && checkpoint.stage;
181
+
182
+ if (!stage) {
183
+ const createDraft = resolveService(context, 'newDraftProcess', newDraftProcess);
184
+ let result;
185
+ try {
186
+ assertRemoteDispatchBoundary(context, 'before');
187
+ const oneShotOptions = createDraft === newDraftProcess ? [{ oneShot: true }] : [];
188
+ result = await createDraft(
189
+ resolveAuthRef(context),
190
+ prepared.appType,
191
+ prepared.processCode,
192
+ prepared.formUuid,
193
+ prepared.baseProcessId,
194
+ processVersion,
195
+ ...oneShotOptions
196
+ );
197
+ assertRemoteDispatchBoundary(context, 'after');
198
+ } catch (error) {
199
+ rethrowApplyBoundaryFailure(error);
200
+ throw resourceError('PROCESS_RESOURCE_DRAFT_FAILED', 'draft_created');
201
+ }
202
+ processId = extractDraftProcessId(result);
203
+ if (!processId) {
204
+ throw resourceError('PROCESS_RESOURCE_DRAFT_FAILED', 'draft_created');
205
+ }
206
+ context.checkpointStage('draft_created', {
207
+ nodeBindings: prepared.compiled.nodeBindings,
208
+ processCode: prepared.processCode,
209
+ processId,
210
+ processVersion,
211
+ });
212
+ stage = 'draft_created';
213
+ }
214
+
215
+ if (stage === 'draft_created') {
216
+ const save = resolveService(context, 'saveProcessById', saveProcessById);
217
+ let result;
218
+ try {
219
+ assertRemoteDispatchBoundary(context, 'before');
220
+ const oneShotOptions = save === saveProcessById ? [{ oneShot: true }] : [];
221
+ result = await save(
222
+ resolveAuthRef(context),
223
+ prepared.appType,
224
+ prepared.formUuid,
225
+ prepared.processCode,
226
+ processId,
227
+ processVersion,
228
+ JSON.stringify(prepared.compiled.processJson),
229
+ JSON.stringify(prepared.compiled.viewJson),
230
+ ...oneShotOptions
231
+ );
232
+ assertRemoteDispatchBoundary(context, 'after');
233
+ } catch (error) {
234
+ rethrowApplyBoundaryFailure(error);
235
+ throw resourceError('PROCESS_RESOURCE_SAVE_FAILED', 'saved');
236
+ }
237
+ assertWriteSuccess(result, 'PROCESS_RESOURCE_SAVE_FAILED', 'saved');
238
+ context.checkpointStage('saved', {});
239
+ stage = 'saved';
240
+ }
241
+
242
+ if (stage === 'saved') {
243
+ const publish = resolveService(context, 'publishProcessById', publishProcessById);
244
+ let result;
245
+ try {
246
+ assertRemoteDispatchBoundary(context, 'before');
247
+ const oneShotOptions = publish === publishProcessById ? [{ oneShot: true }] : [];
248
+ result = await publish(
249
+ resolveAuthRef(context),
250
+ prepared.appType,
251
+ prepared.formUuid,
252
+ prepared.processCode,
253
+ processId,
254
+ processVersion,
255
+ ...oneShotOptions
256
+ );
257
+ assertRemoteDispatchBoundary(context, 'after');
258
+ } catch (error) {
259
+ rethrowApplyBoundaryFailure(error);
260
+ throw resourceError('PROCESS_RESOURCE_PUBLISH_FAILED', 'published');
261
+ }
262
+ assertWriteSuccess(result, 'PROCESS_RESOURCE_PUBLISH_FAILED', 'published');
263
+ context.checkpointStage('published', {});
264
+ }
265
+
266
+ return {
267
+ appType: prepared.appType,
268
+ formUuid: prepared.formUuid,
269
+ nodeBindings: prepared.compiled.nodeBindings,
270
+ processCode: prepared.processCode,
271
+ processId,
272
+ processVersion,
273
+ };
274
+ }
275
+
276
+ async function reconcileProcessResource(context, input) {
277
+ const checkpoint = input && input.stageCheckpoint;
278
+ if (!hasCompleteCheckpointIdentity(checkpoint, input)) {
279
+ return null;
280
+ }
281
+ const resumesRemoteWrite = checkpoint.stage === 'draft_created' || checkpoint.stage === 'saved';
282
+ if (resumesRemoteWrite && !hasWriteRecoveryProvenance(input, checkpoint)) {
283
+ return null;
284
+ }
285
+ const bindings = identityFromCheckpoint(checkpoint, input);
286
+ if (resumesRemoteWrite && !await hasCurrentFormProcessBinding(context, bindings)) {
287
+ return null;
288
+ }
289
+ const reader = resolveService(context, 'readProcessDefinition', readProcessDefinition);
290
+ let readResult;
291
+ try {
292
+ assertRemoteDispatchBoundary(context, 'before');
293
+ readResult = await reader(context, bindings);
294
+ assertRemoteDispatchBoundary(context, 'after');
295
+ } catch (error) {
296
+ rethrowApplyBoundaryFailure(error);
297
+ return null;
298
+ }
299
+
300
+ if (checkpoint.stage === 'draft_created') {
301
+ if (
302
+ !readResult.definition ||
303
+ readResult.definition.bindingForm !== bindings.formUuid ||
304
+ await readExactVersionStatus(context, bindings) !== 'SAVED'
305
+ ) {
306
+ return null;
307
+ }
308
+ const schema = readResult && readResult.definition && readResult.definition.schema;
309
+ if (!schema || !Array.isArray(schema.children)) {
310
+ return null;
311
+ }
312
+ return containsBoundNode(schema.children, checkpoint.nodeBindings)
313
+ ? null
314
+ : { action: 'resume' };
315
+ }
316
+
317
+ let managed;
318
+ try {
319
+ managed = projectProcessManaged(readResult, {
320
+ desired: input.resource.desired,
321
+ formUuid: bindings.formUuid,
322
+ nodeBindings: checkpoint.nodeBindings,
323
+ });
324
+ } catch {
325
+ return null;
326
+ }
327
+ if (!isDeepStrictEqual(managed, input.resource.desired)) {
328
+ return null;
329
+ }
330
+ if (checkpoint.stage === 'saved') {
331
+ const status = await readExactVersionStatus(context, bindings);
332
+ if (status === 'SAVED') {
333
+ return { action: 'resume' };
334
+ }
335
+ if (status === 'PUBLISHED') {
336
+ return completeDecision(bindings, checkpoint.nodeBindings);
337
+ }
338
+ return null;
339
+ }
340
+ if (['published', 'readback_verified', 'completed'].includes(checkpoint.stage)) {
341
+ const status = await readExactVersionStatus(context, bindings);
342
+ return status === 'PUBLISHED'
343
+ ? completeDecision(bindings, checkpoint.nodeBindings)
344
+ : null;
345
+ }
346
+ return null;
347
+ }
348
+
349
+ function hasWriteRecoveryProvenance(input, checkpoint) {
350
+ const operation = input && input.operation;
351
+ const resource = input && input.resource;
352
+ return !!(
353
+ operation &&
354
+ resource &&
355
+ operation.resourceType === 'process' &&
356
+ operation.key === resource.key &&
357
+ (operation.operation === 'create' || operation.operation === 'update') &&
358
+ (operation.status === 'pending' || operation.status === 'uncertain') &&
359
+ isNonEmptyString(operation.operationId) &&
360
+ operation.desiredHash === checkpoint.desiredManagedHash &&
361
+ isDeepStrictEqual(operation.stageCheckpoint, checkpoint)
362
+ );
363
+ }
364
+
365
+ function assertRemoteDispatchBoundary(context, phase) {
366
+ if (typeof context.assertRemoteDispatchBoundary === 'function') {
367
+ context.assertRemoteDispatchBoundary(phase);
368
+ }
369
+ }
370
+
371
+ function rethrowApplyBoundaryFailure(error) {
372
+ if (error && error.code === 'SCHEMA_APPLY_LOCK_LOST') {
373
+ throw error;
374
+ }
375
+ }
376
+
377
+ async function readExactVersionStatus(context, bindings) {
378
+ let snapshot;
379
+ try {
380
+ snapshot = await readProcessVersionSnapshot(context, bindings);
381
+ } catch (error) {
382
+ rethrowApplyBoundaryFailure(error);
383
+ return null;
384
+ }
385
+ const match = snapshot.rows.find(row => (
386
+ row.processId === bindings.processId &&
387
+ row.processVersion === bindings.processVersion
388
+ ));
389
+ return match && (match.status === 'SAVED' || match.status === 'PUBLISHED')
390
+ ? match.status
391
+ : null;
392
+ }
393
+
394
+ async function hasCurrentFormProcessBinding(context, bindings) {
395
+ const reader = resolveService(context, 'readFormMode', readFormMode);
396
+ let observed;
397
+ try {
398
+ assertRemoteDispatchBoundary(context, 'before');
399
+ observed = await reader(context, {
400
+ appType: bindings.appType,
401
+ formUuid: bindings.formUuid,
402
+ });
403
+ assertRemoteDispatchBoundary(context, 'after');
404
+ } catch (error) {
405
+ rethrowApplyBoundaryFailure(error);
406
+ return false;
407
+ }
408
+ return !!(
409
+ observed &&
410
+ observed.mode === 'process' &&
411
+ observed.processCode === bindings.processCode &&
412
+ (!observed.processId || observed.processId === bindings.processId)
413
+ );
414
+ }
415
+
416
+ function containsBoundNode(children, nodeBindings) {
417
+ const boundIds = new Set(Object.values(nodeBindings || {}).map(binding => binding && binding.nodeId).filter(Boolean));
418
+ return children.some(node => node && boundIds.has(node.id));
419
+ }
420
+
421
+ function completeDecision(bindings, nodeBindings) {
422
+ return {
423
+ action: 'complete',
424
+ result: {
425
+ ...bindings,
426
+ nodeBindings: clonePlain(nodeBindings),
427
+ },
428
+ };
429
+ }
430
+
431
+ async function resolveNextProcessIdentity(context, input) {
432
+ let snapshot;
433
+ try {
434
+ snapshot = await readProcessVersionSnapshot(context, input);
435
+ } catch (error) {
436
+ rethrowApplyBoundaryFailure(error);
437
+ throw resourceError('PROCESS_RESOURCE_VERSION_READ_FAILED', 'version_read');
438
+ }
439
+ if (snapshot.drafts.length > 0) {
440
+ throw resourceError('PROCESS_RESOURCE_DRAFT_CONFLICT', 'version_compare');
441
+ }
442
+ if (input.stateBindings && (
443
+ normalizeIdentifier(input.stateBindings.processId) !== snapshot.active.processId ||
444
+ normalizeVersion(input.stateBindings.processVersion) !== snapshot.active.processVersion
445
+ )) {
446
+ throw resourceError('PROCESS_RESOURCE_VERSION_CONFLICT', 'version_compare');
447
+ }
448
+ const nextVersion = snapshot.rows.reduce(
449
+ (maximum, row) => Math.max(maximum, row.processVersion),
450
+ -1
451
+ ) + 1;
452
+ return {
453
+ appType: input.appType,
454
+ baseProcessId: snapshot.active.processId,
455
+ formUuid: input.formUuid,
456
+ processCode: input.processCode,
457
+ processVersion: nextVersion,
458
+ };
459
+ }
460
+
461
+ function identityFromCheckpoint(checkpoint, input) {
462
+ return {
463
+ appType: input && input.appType,
464
+ formUuid: input && input.formUuid,
465
+ processCode: checkpoint.processCode,
466
+ processId: checkpoint.processId,
467
+ processVersion: checkpoint.processVersion,
468
+ };
469
+ }
470
+
471
+ function hasCompleteCheckpointIdentity(checkpoint, input) {
472
+ const desired = input && input.desired || input && input.resource && input.resource.desired;
473
+ return !!(
474
+ checkpoint &&
475
+ isNonEmptyString(input && input.appType) &&
476
+ isNonEmptyString(input && input.formUuid) &&
477
+ isNonEmptyString(checkpoint.processCode) &&
478
+ checkpoint.processCode === input.processCode &&
479
+ isNonEmptyString(checkpoint.processId) &&
480
+ Number.isInteger(checkpoint.processVersion) &&
481
+ checkpoint.processVersion >= 0 &&
482
+ validateManagedNodeBindings(desired && desired.nodes, checkpoint.nodeBindings)
483
+ );
484
+ }
485
+
486
+ function extractDraftProcessId(result) {
487
+ if (!result || result.success !== true) {
488
+ return null;
489
+ }
490
+ const content = result.content;
491
+ const value = content && typeof content === 'object' ? content.processId : content;
492
+ return normalizeIdentifier(value);
493
+ }
494
+
495
+ function assertWriteSuccess(result, code, operation) {
496
+ if (!result || result.success !== true) {
497
+ throw resourceError(code, operation);
498
+ }
499
+ }
500
+
501
+ function normalizeIdentifier(value) {
502
+ if (typeof value === 'number' && Number.isFinite(value)) {
503
+ return String(value);
504
+ }
505
+ return isNonEmptyString(value) ? value : null;
506
+ }
507
+
508
+ function normalizeVersion(value) {
509
+ if (typeof value === 'number') {
510
+ return Number.isInteger(value) && value >= 0 ? value : null;
511
+ }
512
+ if (typeof value === 'string' && /^\d+$/.test(value.trim())) {
513
+ return Number(value.trim());
514
+ }
515
+ return null;
516
+ }
517
+
518
+ function normalizeCount(value) {
519
+ const normalized = normalizeVersion(value);
520
+ return normalized === null ? null : normalized;
521
+ }
522
+
523
+ function normalizeVersionRow(row, processCode) {
524
+ const processId = normalizeIdentifier(row && row.id);
525
+ const processVersion = normalizeVersion(row && row.version);
526
+ const status = row && row.status;
527
+ if (
528
+ !processId ||
529
+ processVersion === null ||
530
+ !VERSION_STATUSES.has(status) ||
531
+ row.code !== processCode
532
+ ) {
533
+ return null;
534
+ }
535
+ return { processId, processVersion, status };
536
+ }
537
+
538
+ function compareVersionRows(left, right) {
539
+ if (left.processVersion !== right.processVersion) {
540
+ return left.processVersion - right.processVersion;
541
+ }
542
+ return left.processId < right.processId ? -1 : left.processId > right.processId ? 1 : 0;
543
+ }
544
+
545
+ function resolveService(context, name, fallback) {
546
+ return context && context.services && context.services[name] || fallback;
547
+ }
548
+
549
+ function resolveAuthRef(context) {
550
+ return context && context.authRef ? context.authRef : context;
551
+ }
552
+
553
+ function resourceError(code, operation) {
554
+ return new ProcessResourceServiceError(code, 'Process resource operation failed.', { operation });
555
+ }
556
+
557
+ function isNonEmptyString(value) {
558
+ return typeof value === 'string' && value.trim() !== '';
559
+ }
560
+
561
+ function clonePlain(value) {
562
+ return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
563
+ }
564
+
565
+ module.exports = {
566
+ ProcessResourceServiceError,
567
+ applyProcessResource,
568
+ prepareProcessResource,
569
+ readProcessVersionSnapshot,
570
+ reconcileProcessResource,
571
+ };
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ const { createYidaClient } = require('../../core/yida-client');
4
+ const {
5
+ getProcessCodeFromAppParam,
6
+ switchFormType,
7
+ } = require('../../app/services/form-mode-service');
8
+
9
+ function queryProcessVersions(authRef, appType, processCode, status, options = {}) {
10
+ const paging = options || {};
11
+ const requestPath = '/alibaba/web/' + appType + '/query/process/pageProcessVersion.json';
12
+ const client = createYidaClient({ authRef });
13
+ const method = options.oneShot === true ? 'getOnce' : 'get';
14
+ return client[method](requestPath, auth => ({
15
+ _api: 'Process.getProcessVersionInfo',
16
+ _mock: 'false',
17
+ _csrf_token: auth.csrfToken,
18
+ _locale_time_zone_offset: '28800000',
19
+ processCode,
20
+ appType,
21
+ status: status || '',
22
+ pageIndex: paging.pageIndex || 1,
23
+ pageSize: paging.pageSize || 10,
24
+ orderByModifyTime: 'desc',
25
+ _stamp: Date.now(),
26
+ }));
27
+ }
28
+
29
+ function getProcessCodeFromSchema(authRef, appType, formUuid) {
30
+ const requestPath = '/dingtalk/web/' + appType + '/query/formdesign/getFormSchema.json';
31
+ return createYidaClient({ authRef }).get(requestPath, {
32
+ formUuid,
33
+ schemaVersion: 'V5',
34
+ }).then(function (result) {
35
+ if (result && result.success && result.content) {
36
+ const schemaStr = typeof result.content === 'string' ? result.content : JSON.stringify(result.content);
37
+ const matches = schemaStr.match(/TPROC[A-Za-z0-9_-]+/g);
38
+ if (matches && matches.length > 0) {
39
+ return Array.from(new Set(matches))[0];
40
+ }
41
+ }
42
+ return null;
43
+ });
44
+ }
45
+
46
+ function newDraftProcess(authRef, appType, processCode, formUuid, processId, processVersion, options = {}) {
47
+ const requestPath = '/' + appType + '/query/simpleProcess/newDraftProcess.json';
48
+ const client = createYidaClient({ authRef });
49
+ const method = options.oneShot === true ? 'postFormOnce' : 'postForm';
50
+ return client[method](requestPath, auth => {
51
+ const postObj = {
52
+ _csrf_token: auth.csrfToken,
53
+ _locale_time_zone_offset: '28800000',
54
+ formUuid,
55
+ processCode,
56
+ };
57
+ if (processId) {postObj.processId = String(processId);}
58
+ if (processVersion !== undefined && processVersion !== null) {
59
+ postObj.processVersion = String(processVersion);
60
+ }
61
+ return postObj;
62
+ });
63
+ }
64
+
65
+ function saveProcessById(authRef, appType, formUuid, processCode, processId, processVersion, processJsonStr, viewJsonStr, options = {}) {
66
+ const requestPath = '/alibaba/web/' + appType + '/query/simpleProcess/saveProcessById.json';
67
+ const client = createYidaClient({ authRef });
68
+ const method = options.oneShot === true ? 'postFormOnce' : 'postForm';
69
+ return client[method](requestPath, auth => ({
70
+ _csrf_token: auth.csrfToken,
71
+ formUuid,
72
+ isOnline: 'true',
73
+ json: processJsonStr,
74
+ needReportLine: 'y',
75
+ processCode,
76
+ processId: String(processId),
77
+ processVersion: String(processVersion),
78
+ viewJson: viewJsonStr,
79
+ }));
80
+ }
81
+
82
+ function publishProcessById(authRef, appType, formUuid, processCode, processId, processVersion, options = {}) {
83
+ const requestPath = '/alibaba/web/' + appType + '/query/simpleProcess/publishProcessById.json';
84
+ const client = createYidaClient({ authRef });
85
+ const method = options.oneShot === true ? 'postFormOnce' : 'postForm';
86
+ return client[method](requestPath, auth => ({
87
+ _csrf_token: auth.csrfToken,
88
+ formUuid,
89
+ processCode,
90
+ processId: String(processId),
91
+ processVersion: String(processVersion),
92
+ }));
93
+ }
94
+
95
+ function getProcessById(authRef, input, options = {}) {
96
+ const appType = input && input.appType;
97
+ const requestPath = '/alibaba/web/' + appType + '/query/simpleProcess/getProcessById.json';
98
+ const client = createYidaClient({ authRef });
99
+ const method = options.oneShot === true ? 'getOnce' : 'get';
100
+ return client[method](requestPath, auth => ({
101
+ _api: 'SimpleProcess.getProcessById',
102
+ _mock: 'false',
103
+ _csrf_token: auth.csrfToken,
104
+ _locale_time_zone_offset: '28800000',
105
+ formUuid: input && input.formUuid,
106
+ processCode: input && input.processCode,
107
+ processId: input && input.processId,
108
+ processVersion: input && input.processVersion,
109
+ _stamp: Date.now(),
110
+ }));
111
+ }
112
+
113
+ module.exports = {
114
+ getProcessById,
115
+ getProcessCodeFromAppParam,
116
+ getProcessCodeFromSchema,
117
+ newDraftProcess,
118
+ publishProcessById,
119
+ queryProcessVersions,
120
+ saveProcessById,
121
+ switchFormType,
122
+ };