openyida 2026.7.19 → 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.
- package/README.md +14 -0
- package/bin/yida.js +107 -18
- package/lib/app/create-app.js +12 -1
- package/lib/app/create-form/args.js +13 -0
- package/lib/app/create-form.js +154 -67
- package/lib/app/create-page.js +45 -9
- package/lib/app/get-schema.js +65 -14
- package/lib/app/import-app.js +92 -41
- package/lib/app/publish.js +289 -556
- package/lib/app/schema-field-resolution.js +370 -0
- package/lib/app/services/app-reader.js +85 -0
- package/lib/app/services/app-service.js +235 -0
- package/lib/app/services/canvas-page-compiler.js +91 -0
- package/lib/app/services/canvas-page-schema-builder.js +149 -0
- package/lib/app/services/field-bindings.js +68 -0
- package/lib/app/services/form-compiler.js +1922 -0
- package/lib/app/services/form-mode-service.js +182 -0
- package/lib/app/services/form-schema-patcher.js +333 -0
- package/lib/app/services/form-schema-reader.js +59 -0
- package/lib/app/services/form-service.js +379 -0
- package/lib/app/services/form-validation.js +562 -0
- package/lib/app/services/native-page-compiler.js +92 -0
- package/lib/app/services/native-page-schema-builder.js +428 -0
- package/lib/app/services/page-resource-service.js +185 -0
- package/lib/app/update-app.js +18 -6
- package/lib/auth/token-auth.js +34 -3
- package/lib/auth/token-store.js +22 -4
- package/lib/bridge/bridge.js +37 -5
- package/lib/core/agent-capabilities.js +5 -3
- package/lib/core/command-manifest.js +40 -0
- package/lib/core/doctor.js +15 -4
- package/lib/core/env-cmd.js +1 -1
- package/lib/core/env.js +7 -6
- package/lib/core/legacy-schema-guard.js +408 -0
- package/lib/core/locales/en.js +8 -46
- package/lib/core/locales/zh.js +8 -46
- package/lib/core/sample.js +1 -1
- package/lib/core/utils.js +334 -66
- package/lib/core/yida-client.js +104 -3
- package/lib/formula/evaluate.js +8 -66
- package/lib/formula/field-refs.js +83 -0
- package/lib/process/configure-process.js +44 -1992
- package/lib/process/create-process.js +54 -87
- package/lib/process/services/process-compiler.js +2056 -0
- package/lib/process/services/process-reader.js +232 -0
- package/lib/process/services/process-resource-service.js +571 -0
- package/lib/process/services/process-service.js +122 -0
- package/lib/process/services/process-stage-checkpoint.js +246 -0
- package/lib/report/append.js +7 -26
- package/lib/report/http.js +31 -5
- package/lib/report/index.js +10 -2
- package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
- package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
- package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
- package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +204 -8
- package/lib/schema/adapters/app-adapter.js +210 -0
- package/lib/schema/adapters/form-adapter.js +1362 -0
- package/lib/schema/adapters/keys.js +19 -0
- package/lib/schema/adapters/page-adapter.js +566 -0
- package/lib/schema/adapters/process-adapter.js +519 -0
- package/lib/schema/applier.js +1878 -0
- package/lib/schema/apply-store.js +1083 -0
- package/lib/schema/command.js +645 -0
- package/lib/schema/dependency-graph.js +83 -0
- package/lib/schema/errors.js +481 -0
- package/lib/schema/hash.js +9 -0
- package/lib/schema/manifest-limits.js +177 -0
- package/lib/schema/manifest-loader.js +309 -0
- package/lib/schema/manifest-schema-v1.json +193 -0
- package/lib/schema/normalize-manifest.js +281 -0
- package/lib/schema/page-canvas-foundation.js +447 -0
- package/lib/schema/page-data-source-builder.js +561 -0
- package/lib/schema/page-foundation.js +556 -0
- package/lib/schema/page-source-loader.js +314 -0
- package/lib/schema/planner.js +691 -0
- package/lib/schema/remote-dispatch-boundary.js +39 -0
- package/lib/schema/remote-missing.js +21 -0
- package/lib/schema/remote-reader.js +189 -0
- package/lib/schema/resource-registry.js +157 -0
- package/lib/schema/server-revision.js +71 -0
- package/lib/schema/sort.js +28 -0
- package/lib/schema/state-store.js +589 -0
- package/package.json +2 -2
- package/scripts/postinstall.js +8 -12
- package/scripts/validate-package-size.js +1 -1
- package/yida-skills/SKILL.md +114 -29
- package/yida-skills/references/schema-as-code-phase1.md +99 -0
- package/yida-skills/references/setup-and-env.md +59 -59
- package/yida-skills/references/task-retrospective.md +6 -6
- package/yida-skills/skills/yida-app/SKILL.md +60 -14
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +7 -1
- package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
- package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
- package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
- package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
- package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
- package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
- package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
- package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
- package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
- package/yida-skills/skills/yida-integration/SKILL.md +7 -1
- package/yida-skills/skills/yida-login/SKILL.md +50 -99
- package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
- package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
- package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
- package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
- package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
- package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
- package/yida-skills/skills/yida-report/SKILL.md +7 -1
- package/yida-skills/skills-index.json +14 -46
- package/yida-skills/skills/large-file-write/SKILL.md +0 -93
- package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
- package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
- package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
- package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
- 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
|
+
};
|