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,556 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const { isDeepStrictEqual } = require('util');
5
+ const { schemaError } = require('./errors');
6
+ const { hashStable } = require('./state-store');
7
+ const {
8
+ NATIVE_PAGE_PROFILE,
9
+ isCompiledNativePage,
10
+ } = require('../app/services/native-page-compiler');
11
+
12
+ const DISPLAY_FORM_TYPE = 'display';
13
+ const SHELL_PROFILE_CONTRACT_VERSION = 1;
14
+ const HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
15
+ const SHELL_PROFILE_ID_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/;
16
+ const SHELL_PROFILES = new WeakSet();
17
+ const COMPONENT_ORDER_KEYS = new Set(['children', 'componentsMap', 'componentsTree']);
18
+ const SHELL_NEUTRAL_TIME_KEYS = new Set([
19
+ 'createtime',
20
+ 'createdat',
21
+ 'gmtcreate',
22
+ 'gmtmodified',
23
+ 'modifiedtime',
24
+ 'timestamp',
25
+ 'updatedat',
26
+ ]);
27
+
28
+ function createNativePageShellProfile(input = {}) {
29
+ const profileId = typeof input.profileId === 'string' ? input.profileId.trim() : '';
30
+ const fingerprint = typeof input.fingerprint === 'string' ? input.fingerprint.trim() : '';
31
+ if (
32
+ !SHELL_PROFILE_ID_PATTERN.test(profileId) ||
33
+ !HASH_PATTERN.test(fingerprint)
34
+ ) {
35
+ foundationError(
36
+ 'SCHEMA_PAGE_FOUNDATION_SHELL_PROFILE_INVALID',
37
+ 'Native page shell profile is invalid.'
38
+ );
39
+ }
40
+ const profile = Object.freeze({
41
+ contractVersion: SHELL_PROFILE_CONTRACT_VERSION,
42
+ fingerprint,
43
+ profileId,
44
+ });
45
+ SHELL_PROFILES.add(profile);
46
+ return profile;
47
+ }
48
+
49
+ function computeNativePageShellFingerprint(schemaInput) {
50
+ const schema = normalizeSchema(schemaInput);
51
+ inspectNativeStructure(schema);
52
+ inspectActions(schema.actions);
53
+ return computeShellFingerprint(schema);
54
+ }
55
+
56
+ function projectNativePageSchema(input = {}) {
57
+ return projectNativePageSchemaInternal(input, true);
58
+ }
59
+
60
+ function projectNativePageSchemaInternal(input, enforceShellProfile) {
61
+ const observedIdentity = normalizeObservedIdentity(input);
62
+ if (observedIdentity.formType !== DISPLAY_FORM_TYPE) {
63
+ foundationError(
64
+ 'SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED',
65
+ 'Observed page is not a display resource.'
66
+ );
67
+ }
68
+ const shellProfile = requireShellProfile(input.shellProfile);
69
+ const schema = normalizeSchema(input.schema);
70
+ const structure = inspectNativeStructure(schema);
71
+ const actions = inspectActions(schema.actions);
72
+ const shellProfileMatches = computeShellFingerprint(schema) === shellProfile.fingerprint;
73
+ if (enforceShellProfile && !shellProfileMatches) {
74
+ foundationError(
75
+ 'SCHEMA_PAGE_FOUNDATION_CONFLICT',
76
+ 'Remote page Schema does not match the approved initial shell profile.'
77
+ );
78
+ }
79
+ const managed = {
80
+ formType: observedIdentity.formType,
81
+ profile: NATIVE_PAGE_PROFILE,
82
+ title: observedIdentity.title,
83
+ };
84
+ let classification = 'PAGE_CREATED';
85
+ if (actions.saved) {
86
+ classification = 'SCHEMA_SAVED';
87
+ managed.sourceHash = hashStableText(actions.source);
88
+ managed.compiledHash = hashStableText(actions.compiled);
89
+ }
90
+ return {
91
+ classification,
92
+ managed,
93
+ managedHash: hashStable(managed),
94
+ remoteSchemaHash: hashRemoteSchema(schema),
95
+ shellProfileMatches,
96
+ structure: {
97
+ jsxComponentCount: structure.jsxComponents.length,
98
+ pageComponentCount: structure.pageComponents.length,
99
+ },
100
+ };
101
+ }
102
+
103
+ function buildNativePageSchemaPatch(input = {}) {
104
+ const observedIdentity = normalizeObservedIdentity(input);
105
+ const desiredIdentity = normalizeDesiredIdentity(input);
106
+ assertIdentityMatches(observedIdentity, desiredIdentity);
107
+ const compiledPage = requireCompiledPage(input.compiledPage);
108
+ const schema = normalizeSchema(input.remoteSchema);
109
+ const beforeProjection = projectNativePageSchema({
110
+ observedFormType: observedIdentity.formType,
111
+ observedTitle: observedIdentity.title,
112
+ schema,
113
+ shellProfile: input.shellProfile,
114
+ });
115
+ if (
116
+ input.expectedRemoteSchemaHash !== undefined &&
117
+ input.expectedRemoteSchemaHash !== beforeProjection.remoteSchemaHash
118
+ ) {
119
+ foundationError('SCHEMA_PAGE_FOUNDATION_CONFLICT', 'Remote page Schema changed before patching.');
120
+ }
121
+
122
+ const desiredManaged = buildDesiredManaged(desiredIdentity.title, compiledPage);
123
+ if (beforeProjection.classification === 'SCHEMA_SAVED') {
124
+ if (isDeepStrictEqual(beforeProjection.managed, desiredManaged)) {
125
+ return {
126
+ baselineRemoteSchemaHash: beforeProjection.remoteSchemaHash,
127
+ managed: desiredManaged,
128
+ managedHash: hashStable(desiredManaged),
129
+ preparedRemoteSchemaHash: beforeProjection.remoteSchemaHash,
130
+ schema,
131
+ unmanagedHash: hashUnmanagedSchema(schema),
132
+ };
133
+ }
134
+ }
135
+
136
+ const beforeUnmanagedHash = hashUnmanagedSchema(schema);
137
+ const patched = clonePlain(schema);
138
+ if (patched.actions === undefined) {
139
+ patched.actions = {};
140
+ }
141
+ assertPlainObject(patched.actions);
142
+ if (patched.actions.module === undefined) {
143
+ patched.actions.module = {};
144
+ }
145
+ assertPlainObject(patched.actions.module);
146
+ patched.actions.module.source = compiledPage.source;
147
+ patched.actions.module.compiled = compiledPage.compiled;
148
+
149
+ const afterUnmanagedHash = hashUnmanagedSchema(patched);
150
+ if (afterUnmanagedHash !== beforeUnmanagedHash) {
151
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page patch changed unmanaged Schema content.');
152
+ }
153
+ const afterProjection = projectNativePageSchema({
154
+ observedFormType: observedIdentity.formType,
155
+ observedTitle: observedIdentity.title,
156
+ schema: patched,
157
+ shellProfile: input.shellProfile,
158
+ });
159
+ if (!isDeepStrictEqual(afterProjection.managed, desiredManaged)) {
160
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page patch did not produce the desired managed content.');
161
+ }
162
+
163
+ return {
164
+ baselineRemoteSchemaHash: beforeProjection.remoteSchemaHash,
165
+ managed: desiredManaged,
166
+ managedHash: afterProjection.managedHash,
167
+ preparedRemoteSchemaHash: afterProjection.remoteSchemaHash,
168
+ schema: patched,
169
+ unmanagedHash: beforeUnmanagedHash,
170
+ };
171
+ }
172
+
173
+ function compareNativePageWrite(input = {}) {
174
+ const observedIdentity = normalizeObservedIdentity(input);
175
+ const desiredIdentity = normalizeDesiredIdentity(input);
176
+ assertIdentityMatches(observedIdentity, desiredIdentity);
177
+ const compiledPage = requireCompiledPage(input.compiledPage);
178
+ const beforeSchema = normalizeSchema(input.beforeSchema);
179
+ const afterSchema = normalizeSchema(input.afterSchema);
180
+ projectNativePageSchema({
181
+ observedFormType: observedIdentity.formType,
182
+ observedTitle: observedIdentity.title,
183
+ schema: beforeSchema,
184
+ shellProfile: input.shellProfile,
185
+ });
186
+ const afterProjection = projectNativePageSchemaInternal({
187
+ observedFormType: observedIdentity.formType,
188
+ observedTitle: observedIdentity.title,
189
+ schema: afterSchema,
190
+ shellProfile: input.shellProfile,
191
+ }, false);
192
+ const desiredManaged = buildDesiredManaged(desiredIdentity.title, compiledPage);
193
+ const managedMatches = isDeepStrictEqual(afterProjection.managed, desiredManaged);
194
+ const beforeUnmanagedHash = hashUnmanagedSchema(beforeSchema);
195
+ const afterUnmanagedHash = hashUnmanagedSchema(afterSchema);
196
+ const unmanagedPreserved = beforeUnmanagedHash === afterUnmanagedHash;
197
+ return {
198
+ managedMatches,
199
+ projection: afterProjection,
200
+ shellProfileMatches: afterProjection.shellProfileMatches,
201
+ unexpectedDelta: !unmanagedPreserved,
202
+ unmanagedPreserved,
203
+ };
204
+ }
205
+
206
+ function buildDesiredManaged(title, compiledPage) {
207
+ return {
208
+ compiledHash: compiledPage.compiledHash,
209
+ formType: DISPLAY_FORM_TYPE,
210
+ profile: NATIVE_PAGE_PROFILE,
211
+ sourceHash: compiledPage.sourceHash,
212
+ title,
213
+ };
214
+ }
215
+
216
+ function normalizeObservedIdentity(input) {
217
+ const title = normalizeTitle(
218
+ input.observedTitle,
219
+ 'SCHEMA_PAGE_FOUNDATION_OBSERVED_IDENTITY_INVALID',
220
+ 'Observed page title is invalid.'
221
+ );
222
+ const formType = normalizeFormType(
223
+ input.observedFormType,
224
+ 'SCHEMA_PAGE_FOUNDATION_OBSERVED_IDENTITY_INVALID',
225
+ 'Observed page form type is invalid.'
226
+ );
227
+ return { formType, title };
228
+ }
229
+
230
+ function normalizeDesiredIdentity(input) {
231
+ const title = normalizeTitle(
232
+ input.desiredTitle,
233
+ 'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
234
+ 'Desired page title is invalid.'
235
+ );
236
+ const formType = input.desiredFormType === undefined
237
+ ? DISPLAY_FORM_TYPE
238
+ : normalizeFormType(
239
+ input.desiredFormType,
240
+ 'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
241
+ 'Desired page form type is invalid.'
242
+ );
243
+ if (formType !== DISPLAY_FORM_TYPE) {
244
+ foundationError(
245
+ 'SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED',
246
+ 'Desired page must use the display resource type.'
247
+ );
248
+ }
249
+ return { formType, title };
250
+ }
251
+
252
+ function normalizeTitle(value, code, message) {
253
+ const title = typeof value === 'string' ? value.trim() : '';
254
+ if (!title || title.length > 256 || containsUnsafeText(title)) {
255
+ foundationError(code, message);
256
+ }
257
+ return title;
258
+ }
259
+
260
+ function normalizeFormType(value, code, message) {
261
+ const formType = typeof value === 'string' ? value.trim().toLowerCase() : '';
262
+ if (!formType || formType.length > 64 || containsUnsafeText(formType)) {
263
+ foundationError(code, message);
264
+ }
265
+ return formType;
266
+ }
267
+
268
+ function assertIdentityMatches(observedIdentity, desiredIdentity) {
269
+ if (
270
+ observedIdentity.title !== desiredIdentity.title ||
271
+ observedIdentity.formType !== desiredIdentity.formType
272
+ ) {
273
+ foundationError(
274
+ 'SCHEMA_PAGE_FOUNDATION_CONFLICT',
275
+ 'Observed page identity does not match desired identity.'
276
+ );
277
+ }
278
+ }
279
+
280
+ function normalizeSchema(value) {
281
+ let candidate = value;
282
+ if (
283
+ candidate &&
284
+ typeof candidate === 'object' &&
285
+ !Array.isArray(candidate) &&
286
+ candidate.success === true &&
287
+ Object.prototype.hasOwnProperty.call(candidate, 'content')
288
+ ) {
289
+ candidate = candidate.content;
290
+ }
291
+ if (typeof candidate === 'string') {
292
+ if (!candidate.trim()) {
293
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page Schema content is empty.');
294
+ }
295
+ try {
296
+ candidate = JSON.parse(candidate);
297
+ } catch (error) {
298
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page Schema content is invalid.');
299
+ }
300
+ }
301
+ assertPlainObject(candidate);
302
+ return clonePlain(candidate);
303
+ }
304
+
305
+ function inspectNativeStructure(schema) {
306
+ if (
307
+ schema.schemaType !== 'superform' ||
308
+ typeof schema.schemaVersion !== 'string' ||
309
+ !schema.schemaVersion.startsWith('5') ||
310
+ !Array.isArray(schema.pages) ||
311
+ schema.pages.length !== 1
312
+ ) {
313
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page Schema root is invalid.');
314
+ }
315
+ const page = schema.pages[0];
316
+ assertPlainObject(page);
317
+ if (!Array.isArray(page.componentsTree)) {
318
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page component tree is invalid.');
319
+ }
320
+ const components = collectComponents(page.componentsTree);
321
+ if (components.some(component => component.componentName === 'YidaCodeCanvas')) {
322
+ foundationError('SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED', 'Canvas page Schema is unsupported.');
323
+ }
324
+ const pageComponents = components.filter(component => component.componentName === 'Page');
325
+ const jsxComponents = components.filter(component => component.componentName === 'Jsx');
326
+ if (pageComponents.length !== 1 || jsxComponents.length !== 1) {
327
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Native page component structure is ambiguous.');
328
+ }
329
+ return { jsxComponents, pageComponents };
330
+ }
331
+
332
+ function inspectActions(actions) {
333
+ if (actions === undefined) {
334
+ return { saved: false };
335
+ }
336
+ assertPlainObject(actions);
337
+ if (actions.module === undefined) {
338
+ return { saved: false };
339
+ }
340
+ assertPlainObject(actions.module);
341
+ const source = actions.module.source;
342
+ const compiled = actions.module.compiled;
343
+ const hasSource = typeof source === 'string' && source.length > 0;
344
+ const hasCompiled = typeof compiled === 'string' && compiled.length > 0;
345
+ if (!hasSource && !hasCompiled) {
346
+ return { saved: false };
347
+ }
348
+ if (!hasSource || !hasCompiled) {
349
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Native page actions are incomplete.');
350
+ }
351
+ return { compiled, saved: true, source };
352
+ }
353
+
354
+ function collectComponents(value, result = []) {
355
+ if (!value || typeof value !== 'object') {
356
+ return result;
357
+ }
358
+ if (Array.isArray(value)) {
359
+ for (const item of value) {
360
+ collectComponents(item, result);
361
+ }
362
+ return result;
363
+ }
364
+ if (typeof value.componentName === 'string') {
365
+ result.push(value);
366
+ }
367
+ for (const key of Object.keys(value).sort()) {
368
+ collectComponents(value[key], result);
369
+ }
370
+ return result;
371
+ }
372
+
373
+ function requireShellProfile(value) {
374
+ if (
375
+ !value ||
376
+ typeof value !== 'object' ||
377
+ !SHELL_PROFILES.has(value) ||
378
+ value.contractVersion !== SHELL_PROFILE_CONTRACT_VERSION
379
+ ) {
380
+ foundationError(
381
+ 'SCHEMA_PAGE_FOUNDATION_SHELL_PROFILE_REQUIRED',
382
+ 'An approved native page shell profile is required.'
383
+ );
384
+ }
385
+ return value;
386
+ }
387
+
388
+ function computeShellFingerprint(schema) {
389
+ const candidate = clonePlain(schema);
390
+ stripManagedActionText(candidate);
391
+ return hashStable(normalizeShellFingerprintValue(candidate));
392
+ }
393
+
394
+ function normalizeShellFingerprintValue(value, pathSegments = [], inheritedComponentName = '') {
395
+ if (Array.isArray(value)) {
396
+ const normalized = value.map((item, index) => normalizeShellFingerprintValue(
397
+ item,
398
+ pathSegments.concat(String(index)),
399
+ inheritedComponentName
400
+ ));
401
+ const collectionName = pathSegments[pathSegments.length - 1];
402
+ if (
403
+ COMPONENT_ORDER_KEYS.has(collectionName) &&
404
+ normalized.every(item => (
405
+ item &&
406
+ typeof item === 'object' &&
407
+ !Array.isArray(item) &&
408
+ typeof item.componentName === 'string'
409
+ ))
410
+ ) {
411
+ normalized.sort(compareCanonicalValues);
412
+ }
413
+ return normalized;
414
+ }
415
+ if (!value || typeof value !== 'object') {
416
+ return value;
417
+ }
418
+
419
+ const ownComponentName = typeof value.componentName === 'string'
420
+ ? value.componentName
421
+ : '';
422
+ const componentName = ownComponentName || inheritedComponentName;
423
+ const result = {};
424
+ for (const key of Object.keys(value).sort()) {
425
+ if (isShellNeutralKey(value, key, pathSegments, ownComponentName, componentName)) {
426
+ continue;
427
+ }
428
+ result[key] = normalizeShellFingerprintValue(
429
+ value[key],
430
+ pathSegments.concat(key),
431
+ componentName
432
+ );
433
+ }
434
+ return result;
435
+ }
436
+
437
+ function isShellNeutralKey(value, key, pathSegments, ownComponentName, componentName) {
438
+ if (SHELL_NEUTRAL_TIME_KEYS.has(key.toLowerCase())) {
439
+ return true;
440
+ }
441
+ if (key === 'id' && ownComponentName) {
442
+ return true;
443
+ }
444
+ if (
445
+ key === 'id' &&
446
+ pathSegments.length === 2 &&
447
+ pathSegments[0] === 'pages' &&
448
+ /^\d+$/.test(pathSegments[1])
449
+ ) {
450
+ return true;
451
+ }
452
+ if (key === 'fieldId' && componentName === 'Jsx') {
453
+ return true;
454
+ }
455
+ return (
456
+ key === 'className' &&
457
+ componentName === 'Page' &&
458
+ typeof value[key] === 'string' &&
459
+ /^page_[a-z0-9]+$/i.test(value[key])
460
+ );
461
+ }
462
+
463
+ function compareCanonicalValues(left, right) {
464
+ const leftValue = JSON.stringify(left);
465
+ const rightValue = JSON.stringify(right);
466
+ if (leftValue < rightValue) {
467
+ return -1;
468
+ }
469
+ if (leftValue > rightValue) {
470
+ return 1;
471
+ }
472
+ return 0;
473
+ }
474
+
475
+ function stripManagedActionText(schema) {
476
+ if (
477
+ !schema.actions ||
478
+ typeof schema.actions !== 'object' ||
479
+ Array.isArray(schema.actions) ||
480
+ !schema.actions.module ||
481
+ typeof schema.actions.module !== 'object' ||
482
+ Array.isArray(schema.actions.module)
483
+ ) {
484
+ return;
485
+ }
486
+ delete schema.actions.module.source;
487
+ delete schema.actions.module.compiled;
488
+ if (Object.keys(schema.actions.module).length === 0) {
489
+ delete schema.actions.module;
490
+ }
491
+ if (Object.keys(schema.actions).length === 0) {
492
+ delete schema.actions;
493
+ }
494
+ }
495
+
496
+ function hashUnmanagedSchema(schema) {
497
+ const unmanaged = clonePlain(schema);
498
+ stripManagedActionText(unmanaged);
499
+ return hashStable(unmanaged);
500
+ }
501
+
502
+ function hashRemoteSchema(schema) {
503
+ const normalized = clonePlain(schema);
504
+ delete normalized.gmtModified;
505
+ return hashStable(normalized);
506
+ }
507
+
508
+ function requireCompiledPage(value) {
509
+ if (!isCompiledNativePage(value)) {
510
+ foundationError('SCHEMA_PAGE_SOURCE_INVALID', 'Page patch requires trusted compiled native source.');
511
+ }
512
+ return value;
513
+ }
514
+
515
+ function hashStableText(value) {
516
+ return 'sha256:' + crypto.createHash('sha256').update(value).digest('hex');
517
+ }
518
+
519
+ function assertPlainObject(value) {
520
+ const prototype = value && typeof value === 'object' ? Object.getPrototypeOf(value) : undefined;
521
+ if (
522
+ !value ||
523
+ typeof value !== 'object' ||
524
+ Array.isArray(value) ||
525
+ (prototype !== Object.prototype && prototype !== null)
526
+ ) {
527
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Page Schema value must be a plain object.');
528
+ }
529
+ }
530
+
531
+ function containsUnsafeText(value) {
532
+ for (let index = 0; index < value.length; index++) {
533
+ const code = value.charCodeAt(index);
534
+ if ((code < 32 && code !== 9 && code !== 10 && code !== 13) || code === 127) {
535
+ return true;
536
+ }
537
+ }
538
+ return false;
539
+ }
540
+
541
+ function clonePlain(value) {
542
+ return JSON.parse(JSON.stringify(value));
543
+ }
544
+
545
+ function foundationError(code, message) {
546
+ throw schemaError(code, message);
547
+ }
548
+
549
+ module.exports = {
550
+ DISPLAY_FORM_TYPE,
551
+ buildNativePageSchemaPatch,
552
+ compareNativePageWrite,
553
+ computeNativePageShellFingerprint,
554
+ createNativePageShellProfile,
555
+ projectNativePageSchema,
556
+ };