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,519 @@
1
+ 'use strict';
2
+
3
+ const { isDeepStrictEqual } = require('util');
4
+ const { schemaError } = require('../errors');
5
+ const { validateSemanticKey } = require('./keys');
6
+ const { normalizeFormReference } = require('./form-adapter');
7
+ const { projectProcessManaged, readProcessDefinition } = require('../../process/services/process-reader');
8
+ const { validateManagedNodeBindings } = require('../../process/services/process-compiler');
9
+ const {
10
+ applyProcessResource,
11
+ prepareProcessResource,
12
+ readProcessVersionSnapshot,
13
+ reconcileProcessResource,
14
+ } = require('../../process/services/process-resource-service');
15
+ const {
16
+ advanceProcessStagedCheckpoint,
17
+ isProcessStageCheckpointRemoteComplete,
18
+ validateProcessStageCheckpoint,
19
+ } = require('../../process/services/process-stage-checkpoint');
20
+ const { hashStable } = require('../state-store');
21
+ const { sortStrings } = require('../sort');
22
+
23
+ const PROCESS_NODE_TYPE = 'approval';
24
+ const PROCESS_APPROVER = 'originator';
25
+ const PROCESS_ADAPTER_VERSION = 1;
26
+
27
+ const processAdapter = {
28
+ resourceType: 'process',
29
+ adapterVersion: PROCESS_ADAPTER_VERSION,
30
+ validate(entry, context = {}) {
31
+ const processKey = entry.key;
32
+ const definition = entry.definition;
33
+ const pointer = `/processes/${escapePointer(processKey)}`;
34
+ validateSemanticKey(processKey, pointer);
35
+
36
+ const formReference = normalizeFormReference(definition.form, context);
37
+ const formKey = formReference.slice('form:'.length);
38
+ const form = context.manifest && context.manifest.forms && context.manifest.forms[formKey];
39
+ if (form && form.mode !== 'process') {
40
+ throw schemaError('SCHEMA_PROCESS_FORM_MODE_REQUIRED', 'Process resources require their form to declare mode "process".', {
41
+ path: `${pointer}/form`,
42
+ details: {
43
+ key: processKey,
44
+ resourceType: 'process',
45
+ },
46
+ });
47
+ }
48
+
49
+ const nodeKeys = new Set();
50
+ definition.nodes.forEach((node, index) => {
51
+ const nodePointer = `${pointer}/nodes/${index}`;
52
+ validateSemanticKey(node.key, `${nodePointer}/key`);
53
+ if (nodeKeys.has(node.key)) {
54
+ throw schemaError('SCHEMA_DUPLICATE_KEY', 'Process node semantic keys must be unique within a process.', {
55
+ path: `${nodePointer}/key`,
56
+ details: { key: node.key },
57
+ });
58
+ }
59
+ nodeKeys.add(node.key);
60
+ if (node.type !== PROCESS_NODE_TYPE) {
61
+ throw schemaError('SCHEMA_PROCESS_NODE_TYPE_UNSUPPORTED', 'Process node type is not supported by Manifest v1.', {
62
+ path: `${nodePointer}/type`,
63
+ details: { nodeType: node.type },
64
+ });
65
+ }
66
+ if (node.approver !== PROCESS_APPROVER) {
67
+ throw schemaError('SCHEMA_PROCESS_NODE_PROPERTY_INVALID', 'Process approval nodes currently require approver "originator".', {
68
+ path: `${nodePointer}/approver`,
69
+ details: { property: 'approver' },
70
+ });
71
+ }
72
+ });
73
+ },
74
+
75
+ normalize(entry, context = {}) {
76
+ this.validate(entry, context);
77
+ const processKey = entry.key;
78
+ const definition = entry.definition;
79
+ const pointer = `/processes/${escapePointer(processKey)}`;
80
+ const formReference = normalizeFormReference(definition.form, context);
81
+
82
+ return {
83
+ resourceType: 'process',
84
+ key: processKey,
85
+ desired: {
86
+ form: formReference,
87
+ nodes: definition.nodes.map(node => ({
88
+ key: node.key,
89
+ type: PROCESS_NODE_TYPE,
90
+ name: node.name,
91
+ approver: PROCESS_APPROVER,
92
+ })),
93
+ },
94
+ dependsOn: [formReference],
95
+ dependencySources: {
96
+ [formReference]: [{
97
+ kind: 'reference',
98
+ path: `${pointer}/form`,
99
+ }],
100
+ },
101
+ };
102
+ },
103
+
104
+ async readObserved(binding, context = {}) {
105
+ const bindings = normalizeProcessBindings(binding);
106
+ const versionSnapshot = await readProcessVersionSnapshot(context, bindings);
107
+ if (!matchesProcessIdentity(versionSnapshot.active, bindings)) {
108
+ return { versionSnapshot };
109
+ }
110
+ const reader = context.services && context.services.readProcessDefinition || readProcessDefinition;
111
+ assertAdapterDispatchBoundary(context, 'before');
112
+ const definitionResult = await reader(context, bindings);
113
+ assertAdapterDispatchBoundary(context, 'after');
114
+ return {
115
+ definitionResult,
116
+ versionSnapshot,
117
+ };
118
+ },
119
+
120
+ projectObserved(observed, binding, context = {}) {
121
+ const bindings = normalizeProcessBindings(binding);
122
+ const desired = context.resource && context.resource.desired || context.desired;
123
+ const versionObservation = projectVersionObservation(observed && observed.versionSnapshot, bindings);
124
+ const managed = versionObservation.observedIdentityMatchesBindings
125
+ ? projectProcessManaged(observed.definitionResult, {
126
+ desired,
127
+ formUuid: bindings.formUuid,
128
+ nodeBindings: bindings.nodeBindings,
129
+ })
130
+ : clonePlain(binding && binding.lastApplied || {});
131
+ return {
132
+ bindings,
133
+ managed,
134
+ ...versionObservation,
135
+ };
136
+ },
137
+
138
+ classifyObservedConflict(input) {
139
+ const observed = input.observedResource || {};
140
+ if (observed.observedIdentityMatchesBindings === false) {
141
+ return 'REMOTE_IDENTITY_DRIFT';
142
+ }
143
+ if (
144
+ observed.observedDraftCount > 0 &&
145
+ input.desiredChanged === true &&
146
+ input.observedMatchesDesired !== true
147
+ ) {
148
+ return 'REMOTE_DRAFT_EXISTS';
149
+ }
150
+ return undefined;
151
+ },
152
+
153
+ async prepareOperation(input, context = {}) {
154
+ const operationInput = buildProcessOperationInput(
155
+ input.resource,
156
+ input.stateResource,
157
+ context,
158
+ input.recovery
159
+ );
160
+ const prepare = context.services && context.services.prepareProcessResource || prepareProcessResource;
161
+ try {
162
+ return await prepare(context, operationInput);
163
+ } catch (error) {
164
+ if (error && error.code === 'PROCESS_RESOURCE_RECONCILIATION_REQUIRED') {
165
+ throw schemaError('SCHEMA_RECONCILIATION_REQUIRED', 'Process checkpoint evidence is incomplete.', {
166
+ details: {
167
+ operation: input.operation,
168
+ resourceType: 'process',
169
+ key: input.resource && input.resource.key,
170
+ },
171
+ });
172
+ }
173
+ if (error && (
174
+ error.code === 'PROCESS_RESOURCE_VERSION_CONFLICT' ||
175
+ error.code === 'PROCESS_RESOURCE_DRAFT_CONFLICT'
176
+ )) {
177
+ throw schemaError('SCHEMA_APPLY_JIT_CONFLICT', 'Current process version state no longer permits the reviewed write.', {
178
+ details: {
179
+ operation: input.operation,
180
+ resourceType: 'process',
181
+ key: input.resource && input.resource.key,
182
+ },
183
+ });
184
+ }
185
+ if (error && error.code === 'PROCESS_RESOURCE_COMPILE_FAILED') {
186
+ throw schemaError('SCHEMA_PROCESS_COMPILE_FAILED', 'Process desired state could not be compiled.', {
187
+ details: {
188
+ resourceType: 'process',
189
+ key: input.resource && input.resource.key,
190
+ },
191
+ });
192
+ }
193
+ throw schemaError('SCHEMA_REMOTE_READ_FAILED', 'Process version state could not be read.', {
194
+ details: {
195
+ resourceType: 'process',
196
+ key: input.resource && input.resource.key,
197
+ },
198
+ });
199
+ }
200
+ },
201
+
202
+ async create(desired, context = {}) {
203
+ return runProcessWrite(context);
204
+ },
205
+
206
+ async update(desired, observed, binding, context = {}) {
207
+ return runProcessWrite(context);
208
+ },
209
+
210
+ buildBindings(result, context = {}) {
211
+ const checkpoint = context.stageCheckpoint || {};
212
+ return requireCompleteProcessBindings({
213
+ appType: result && result.appType,
214
+ formUuid: result && result.formUuid,
215
+ nodeBindings: result && result.nodeBindings || checkpoint.nodeBindings,
216
+ processCode: result && result.processCode || checkpoint.processCode,
217
+ processId: result && result.processId || checkpoint.processId,
218
+ processVersion: result && result.processVersion !== undefined
219
+ ? result.processVersion
220
+ : checkpoint.processVersion,
221
+ });
222
+ },
223
+
224
+ verify(projection, context = {}) {
225
+ if (
226
+ !projection ||
227
+ projection.observedIdentityMatchesBindings !== true ||
228
+ !isDeepStrictEqual(projection.managed, context.resource && context.resource.desired)
229
+ ) {
230
+ throw schemaError('SCHEMA_APPLY_VERIFY_FAILED', 'Process observed projection does not match desired managed state.', {
231
+ details: {
232
+ resourceType: 'process',
233
+ key: context.resource && context.resource.key,
234
+ },
235
+ });
236
+ }
237
+ return projection;
238
+ },
239
+
240
+ async reconcileStaged(input, context = {}) {
241
+ const operationInput = buildProcessOperationInput(
242
+ input.resource,
243
+ input.stateResource,
244
+ context,
245
+ { stageCheckpoint: input.stageCheckpoint }
246
+ );
247
+ const reconcile = context.services && context.services.reconcileProcessResource || reconcileProcessResource;
248
+ return reconcile(context, {
249
+ ...operationInput,
250
+ operation: input.operation,
251
+ resource: input.resource,
252
+ stageCheckpoint: input.stageCheckpoint,
253
+ });
254
+ },
255
+
256
+ advanceStagedCheckpoint(previous, event, input) {
257
+ return advanceProcessStagedCheckpoint(previous, event, input);
258
+ },
259
+
260
+ isStagedCheckpointRemoteComplete(checkpoint, context = {}) {
261
+ return isProcessStageCheckpointRemoteComplete(checkpoint, {
262
+ desiredManagedHash: context.desiredManagedHash,
263
+ });
264
+ },
265
+
266
+ validateStageCheckpoint(checkpoint, context = {}) {
267
+ return validateProcessStageCheckpoint(checkpoint, context);
268
+ },
269
+
270
+ validateStateResource(entry) {
271
+ validateProcessStateResource(entry);
272
+ },
273
+ };
274
+
275
+ function assertAdapterDispatchBoundary(context, phase) {
276
+ if (typeof context.assertRemoteDispatchBoundary === 'function') {
277
+ context.assertRemoteDispatchBoundary(phase);
278
+ }
279
+ }
280
+
281
+ function runProcessWrite(context) {
282
+ const writer = context.services && context.services.applyProcessResource || applyProcessResource;
283
+ return writer({
284
+ ...context,
285
+ checkpointStage: context.checkpointStage,
286
+ }, {
287
+ prepared: context.prepared,
288
+ recovery: context.recovery,
289
+ });
290
+ }
291
+
292
+ function projectVersionObservation(snapshot, bindings) {
293
+ if (!snapshot || !snapshot.active || !Array.isArray(snapshot.drafts)) {
294
+ throw schemaError('SCHEMA_REMOTE_PROJECT_FAILED', 'Process version observation is invalid.', {
295
+ details: { resourceType: 'process' },
296
+ });
297
+ }
298
+ const activeIdentity = {
299
+ processId: snapshot.active.processId,
300
+ processVersion: snapshot.active.processVersion,
301
+ };
302
+ const draftIdentities = snapshot.drafts.map(row => ({
303
+ processId: row.processId,
304
+ processVersion: row.processVersion,
305
+ }));
306
+ return {
307
+ observedDraftCount: draftIdentities.length,
308
+ observedDraftHash: hashStable({
309
+ adapterVersion: PROCESS_ADAPTER_VERSION,
310
+ identities: draftIdentities,
311
+ resourceType: 'process',
312
+ }),
313
+ observedIdentityHash: hashStable({
314
+ adapterVersion: PROCESS_ADAPTER_VERSION,
315
+ identity: activeIdentity,
316
+ resourceType: 'process',
317
+ }),
318
+ observedIdentityMatchesBindings: matchesProcessIdentity(snapshot.active, bindings),
319
+ };
320
+ }
321
+
322
+ function matchesProcessIdentity(identity, bindings) {
323
+ return !!(
324
+ identity &&
325
+ identity.processId === bindings.processId &&
326
+ identity.processVersion === bindings.processVersion
327
+ );
328
+ }
329
+
330
+ function buildProcessOperationInput(resource, stateResource, context, recovery) {
331
+ const formKey = resource && resource.desired && String(resource.desired.form).slice('form:'.length);
332
+ const formState = context.state && context.state.resources && context.state.resources.form && context.state.resources.form[formKey];
333
+ const formBindings = formState && formState.bindings || {};
334
+ if (!isNonEmptyString(formBindings.appType) || !isNonEmptyString(formBindings.formUuid) || !isNonEmptyString(formBindings.processCode)) {
335
+ throw schemaError('SCHEMA_STATE_RESOURCE_BINDING_MISSING', 'Process form dependency bindings are incomplete.', {
336
+ details: { resourceType: 'form', key: formKey },
337
+ });
338
+ }
339
+ const stageCheckpoint = recovery && recovery.stageCheckpoint;
340
+ if (stageCheckpoint && stageCheckpoint.processCode !== formBindings.processCode) {
341
+ throw schemaError('SCHEMA_RECONCILIATION_REQUIRED', 'Process checkpoint no longer matches its form dependency.', {
342
+ details: {
343
+ operation: 'reconcile',
344
+ resourceType: 'process',
345
+ key: resource && resource.key,
346
+ },
347
+ });
348
+ }
349
+ const stateBindings = stateResource ? normalizeProcessBindings(stateResource) : null;
350
+ if (stateBindings && (
351
+ stateBindings.appType !== formBindings.appType ||
352
+ stateBindings.formUuid !== formBindings.formUuid ||
353
+ stateBindings.processCode !== formBindings.processCode
354
+ )) {
355
+ throw schemaError('SCHEMA_APPLY_JIT_CONFLICT', 'Process bindings no longer match the form dependency.', {
356
+ details: {
357
+ operation: 'update',
358
+ resourceType: 'process',
359
+ key: resource && resource.key,
360
+ },
361
+ });
362
+ }
363
+ return {
364
+ appType: formBindings.appType,
365
+ baseUrl: context.authRef && context.authRef.baseUrl || context.environment && context.environment.endpoint,
366
+ desired: resource.desired,
367
+ existingBindings: stateBindings && stateBindings.nodeBindings || {},
368
+ formUuid: formBindings.formUuid,
369
+ processCode: formBindings.processCode,
370
+ recovery,
371
+ stateBindings,
372
+ };
373
+ }
374
+
375
+ function normalizeProcessBindings(binding) {
376
+ const source = binding && binding.bindings ? binding.bindings : binding || {};
377
+ return {
378
+ appType: source.appType,
379
+ formUuid: source.formUuid,
380
+ nodeBindings: cloneNodeBindings(source.nodeBindings || {}),
381
+ processCode: source.processCode,
382
+ processId: source.processId,
383
+ processVersion: source.processVersion,
384
+ };
385
+ }
386
+
387
+ function requireCompleteProcessBindings(value) {
388
+ const normalized = normalizeProcessBindings(value);
389
+ if (
390
+ !isNonEmptyString(normalized.appType) ||
391
+ !isNonEmptyString(normalized.formUuid) ||
392
+ !isNonEmptyString(normalized.processCode) ||
393
+ !isNonEmptyString(normalized.processId) ||
394
+ !Number.isInteger(normalized.processVersion) ||
395
+ normalized.processVersion < 0 ||
396
+ Object.keys(normalized.nodeBindings).length === 0
397
+ ) {
398
+ throw schemaError('SCHEMA_APPLY_VERIFY_FAILED', 'Process operation did not produce complete bindings.', {
399
+ details: { resourceType: 'process' },
400
+ });
401
+ }
402
+ return normalized;
403
+ }
404
+
405
+ function validateProcessStateResource(entry) {
406
+ const state = entry.state || {};
407
+ validateProcessBindings(state.bindings, `${entry.path}/bindings`);
408
+ if (state.lastApplied !== undefined) {
409
+ validateManagedProcess(state.lastApplied, `${entry.path}/lastApplied`);
410
+ if (!validateManagedNodeBindings(state.lastApplied.nodes, state.bindings.nodeBindings)) {
411
+ stateInvalid(`${entry.path}/bindings/nodeBindings`);
412
+ }
413
+ }
414
+ }
415
+
416
+ function validateProcessBindings(bindings, pointer) {
417
+ assertObject(bindings, pointer);
418
+ assertAllowedKeys(bindings, [
419
+ 'appType',
420
+ 'formUuid',
421
+ 'nodeBindings',
422
+ 'processCode',
423
+ 'processId',
424
+ 'processVersion',
425
+ ], pointer);
426
+ for (const property of ['appType', 'formUuid', 'processCode', 'processId']) {
427
+ if (!isNonEmptyString(bindings[property])) {
428
+ stateInvalid(`${pointer}/${property}`);
429
+ }
430
+ }
431
+ if (!Number.isInteger(bindings.processVersion) || bindings.processVersion < 0) {
432
+ stateInvalid(`${pointer}/processVersion`);
433
+ }
434
+ assertObject(bindings.nodeBindings, `${pointer}/nodeBindings`);
435
+ if (Object.keys(bindings.nodeBindings).length === 0) {
436
+ stateInvalid(`${pointer}/nodeBindings`);
437
+ }
438
+ const bindingKeys = sortStrings(Object.keys(bindings.nodeBindings));
439
+ for (const key of bindingKeys) {
440
+ validateSemanticKey(key, `${pointer}/nodeBindings/${escapePointer(key)}`);
441
+ }
442
+ const stateNodes = bindingKeys.map(key => ({ key, type: PROCESS_NODE_TYPE }));
443
+ if (!validateManagedNodeBindings(stateNodes, bindings.nodeBindings)) {
444
+ stateInvalid(`${pointer}/nodeBindings`);
445
+ }
446
+ }
447
+
448
+ function validateManagedProcess(value, pointer) {
449
+ assertObject(value, pointer);
450
+ assertAllowedKeys(value, ['form', 'nodes'], pointer);
451
+ if (!isNonEmptyString(value.form) || !value.form.startsWith('form:')) {
452
+ stateInvalid(`${pointer}/form`);
453
+ }
454
+ if (!Array.isArray(value.nodes) || value.nodes.length === 0) {
455
+ stateInvalid(`${pointer}/nodes`);
456
+ }
457
+ value.nodes.forEach((node, index) => {
458
+ const nodePointer = `${pointer}/nodes/${index}`;
459
+ assertObject(node, nodePointer);
460
+ assertAllowedKeys(node, ['key', 'type', 'name', 'approver'], nodePointer);
461
+ validateSemanticKey(node.key, `${nodePointer}/key`);
462
+ if (
463
+ node.type !== PROCESS_NODE_TYPE ||
464
+ node.approver !== PROCESS_APPROVER ||
465
+ !isNonEmptyString(node.name)
466
+ ) {
467
+ stateInvalid(nodePointer);
468
+ }
469
+ });
470
+ }
471
+
472
+ function cloneNodeBindings(value) {
473
+ const result = Object.create(null);
474
+ for (const key of sortStrings(Object.keys(value || {}))) {
475
+ const node = value[key] || {};
476
+ result[key] = {
477
+ nodeId: node.nodeId,
478
+ componentName: node.componentName,
479
+ };
480
+ }
481
+ return result;
482
+ }
483
+
484
+ function assertObject(value, pointer) {
485
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
486
+ stateInvalid(pointer);
487
+ }
488
+ }
489
+
490
+ function assertAllowedKeys(value, allowedKeys, pointer) {
491
+ const allowed = new Set(allowedKeys);
492
+ for (const key of Object.keys(value || {})) {
493
+ if (!allowed.has(key)) {
494
+ stateInvalid(`${pointer}/${escapePointer(key)}`);
495
+ }
496
+ }
497
+ }
498
+
499
+ function stateInvalid(path) {
500
+ throw schemaError('SCHEMA_STATE_INVALID', 'Process state resource is invalid.', { path });
501
+ }
502
+
503
+ function isNonEmptyString(value) {
504
+ return typeof value === 'string' && value.trim() !== '';
505
+ }
506
+
507
+ function clonePlain(value) {
508
+ return JSON.parse(JSON.stringify(value));
509
+ }
510
+
511
+ function escapePointer(value) {
512
+ return String(value).replace(/~/g, '~0').replace(/\//g, '~1');
513
+ }
514
+
515
+ module.exports = {
516
+ PROCESS_APPROVER,
517
+ PROCESS_NODE_TYPE,
518
+ processAdapter,
519
+ };