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,447 @@
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
+ CANVAS_PAGE_PROFILE,
9
+ createCanvasCompiledHash,
10
+ isCompiledCanvasPage,
11
+ } = require('../app/services/canvas-page-compiler');
12
+
13
+ const DISPLAY_FORM_TYPE = 'display';
14
+ const COMPONENT_ORDER_KEYS = new Set(['children', 'componentsMap', 'componentsTree']);
15
+ const SHELL_NEUTRAL_TIME_KEYS = new Set([
16
+ 'createtime',
17
+ 'createdat',
18
+ 'gmtcreate',
19
+ 'gmtmodified',
20
+ 'modifiedtime',
21
+ 'timestamp',
22
+ 'updatedat',
23
+ ]);
24
+
25
+ function projectCanvasPageSchema(input = {}) {
26
+ const observedIdentity = normalizeObservedIdentity(input);
27
+ if (observedIdentity.formType !== DISPLAY_FORM_TYPE) {
28
+ foundationError(
29
+ 'SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED',
30
+ 'Observed page is not a display resource.'
31
+ );
32
+ }
33
+ const schema = normalizeSchema(input.schema);
34
+ const structure = inspectCanvasStructure(schema);
35
+ const canvasProps = inspectCanvasProps(structure.canvasComponent);
36
+ const managed = {
37
+ compiledHash: createCanvasCompiledHash(canvasProps.runtimeCode, canvasProps.importedModules),
38
+ formType: observedIdentity.formType,
39
+ profile: CANVAS_PAGE_PROFILE,
40
+ sourceHash: hashStableText(canvasProps.code),
41
+ title: observedIdentity.title,
42
+ };
43
+ return {
44
+ classification: 'SCHEMA_SAVED',
45
+ managed,
46
+ managedHash: hashStable(managed),
47
+ remoteSchemaHash: hashRemoteSchema(schema),
48
+ structure: {
49
+ canvasComponentCount: structure.canvasComponents.length,
50
+ pageComponentCount: structure.pageComponents.length,
51
+ },
52
+ };
53
+ }
54
+
55
+ function buildCanvasPageSchemaPatch(input = {}) {
56
+ const observedIdentity = normalizeObservedIdentity(input);
57
+ const desiredIdentity = normalizeDesiredIdentity(input);
58
+ assertIdentityMatches(observedIdentity, desiredIdentity);
59
+ const compiledPage = requireCompiledPage(input.compiledPage);
60
+ const schema = normalizeSchema(input.remoteSchema);
61
+ const beforeProjection = projectCanvasPageSchema({
62
+ observedFormType: observedIdentity.formType,
63
+ observedTitle: observedIdentity.title,
64
+ schema,
65
+ });
66
+ if (
67
+ input.expectedRemoteSchemaHash !== undefined &&
68
+ input.expectedRemoteSchemaHash !== beforeProjection.remoteSchemaHash
69
+ ) {
70
+ foundationError('SCHEMA_PAGE_FOUNDATION_CONFLICT', 'Remote page Schema changed before patching.');
71
+ }
72
+
73
+ const desiredManaged = buildDesiredManaged(desiredIdentity.title, compiledPage);
74
+ if (isDeepStrictEqual(beforeProjection.managed, desiredManaged)) {
75
+ return {
76
+ baselineRemoteSchemaHash: beforeProjection.remoteSchemaHash,
77
+ managed: desiredManaged,
78
+ managedHash: hashStable(desiredManaged),
79
+ preparedRemoteSchemaHash: beforeProjection.remoteSchemaHash,
80
+ schema,
81
+ unmanagedHash: hashCanvasUnmanagedSchema(schema),
82
+ };
83
+ }
84
+
85
+ const beforeUnmanagedHash = hashCanvasUnmanagedSchema(schema);
86
+ const patched = clonePlain(schema);
87
+ const structure = inspectCanvasStructure(patched);
88
+ const canvasProps = requireCanvasProps(structure.canvasComponent);
89
+ canvasProps.code = compiledPage.source;
90
+ canvasProps.runtimeCode = compiledPage.runtimeCode;
91
+ canvasProps.importedModules = compiledPage.importedModules;
92
+
93
+ const afterUnmanagedHash = hashCanvasUnmanagedSchema(patched);
94
+ if (afterUnmanagedHash !== beforeUnmanagedHash) {
95
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page patch changed unmanaged Schema content.');
96
+ }
97
+ const afterProjection = projectCanvasPageSchema({
98
+ observedFormType: observedIdentity.formType,
99
+ observedTitle: observedIdentity.title,
100
+ schema: patched,
101
+ });
102
+ if (!isDeepStrictEqual(afterProjection.managed, desiredManaged)) {
103
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page patch did not produce the desired managed content.');
104
+ }
105
+
106
+ return {
107
+ baselineRemoteSchemaHash: beforeProjection.remoteSchemaHash,
108
+ managed: desiredManaged,
109
+ managedHash: afterProjection.managedHash,
110
+ preparedRemoteSchemaHash: afterProjection.remoteSchemaHash,
111
+ schema: patched,
112
+ unmanagedHash: beforeUnmanagedHash,
113
+ };
114
+ }
115
+
116
+ function buildDesiredManaged(title, compiledPage) {
117
+ return {
118
+ compiledHash: compiledPage.compiledHash,
119
+ formType: DISPLAY_FORM_TYPE,
120
+ profile: CANVAS_PAGE_PROFILE,
121
+ sourceHash: compiledPage.sourceHash,
122
+ title,
123
+ };
124
+ }
125
+
126
+ function normalizeObservedIdentity(input) {
127
+ const title = normalizeTitle(
128
+ input.observedTitle,
129
+ 'SCHEMA_PAGE_FOUNDATION_OBSERVED_IDENTITY_INVALID',
130
+ 'Observed page title is invalid.'
131
+ );
132
+ const formType = normalizeFormType(
133
+ input.observedFormType,
134
+ 'SCHEMA_PAGE_FOUNDATION_OBSERVED_IDENTITY_INVALID',
135
+ 'Observed page form type is invalid.'
136
+ );
137
+ return { formType, title };
138
+ }
139
+
140
+ function normalizeDesiredIdentity(input) {
141
+ const title = normalizeTitle(
142
+ input.desiredTitle,
143
+ 'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
144
+ 'Desired page title is invalid.'
145
+ );
146
+ const formType = input.desiredFormType === undefined
147
+ ? DISPLAY_FORM_TYPE
148
+ : normalizeFormType(
149
+ input.desiredFormType,
150
+ 'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
151
+ 'Desired page form type is invalid.'
152
+ );
153
+ if (formType !== DISPLAY_FORM_TYPE) {
154
+ foundationError(
155
+ 'SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED',
156
+ 'Desired page must use the display resource type.'
157
+ );
158
+ }
159
+ return { formType, title };
160
+ }
161
+
162
+ function normalizeTitle(value, code, message) {
163
+ const title = typeof value === 'string' ? value.trim() : '';
164
+ if (!title || title.length > 256 || containsUnsafeText(title)) {
165
+ foundationError(code, message);
166
+ }
167
+ return title;
168
+ }
169
+
170
+ function normalizeFormType(value, code, message) {
171
+ const formType = typeof value === 'string' ? value.trim().toLowerCase() : '';
172
+ if (!formType || formType.length > 64 || containsUnsafeText(formType)) {
173
+ foundationError(code, message);
174
+ }
175
+ return formType;
176
+ }
177
+
178
+ function assertIdentityMatches(observedIdentity, desiredIdentity) {
179
+ if (
180
+ observedIdentity.title !== desiredIdentity.title ||
181
+ observedIdentity.formType !== desiredIdentity.formType
182
+ ) {
183
+ foundationError(
184
+ 'SCHEMA_PAGE_FOUNDATION_CONFLICT',
185
+ 'Observed page identity does not match desired identity.'
186
+ );
187
+ }
188
+ }
189
+
190
+ function normalizeSchema(value) {
191
+ let candidate = value;
192
+ if (
193
+ candidate &&
194
+ typeof candidate === 'object' &&
195
+ !Array.isArray(candidate) &&
196
+ candidate.success === true &&
197
+ Object.prototype.hasOwnProperty.call(candidate, 'content')
198
+ ) {
199
+ candidate = candidate.content;
200
+ }
201
+ if (typeof candidate === 'string') {
202
+ if (!candidate.trim()) {
203
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page Schema content is empty.');
204
+ }
205
+ try {
206
+ candidate = JSON.parse(candidate);
207
+ } catch (error) {
208
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page Schema content is invalid.');
209
+ }
210
+ }
211
+ assertPlainObject(candidate);
212
+ return clonePlain(candidate);
213
+ }
214
+
215
+ function inspectCanvasStructure(schema) {
216
+ if (
217
+ schema.schemaType !== 'superform' ||
218
+ typeof schema.schemaVersion !== 'string' ||
219
+ !schema.schemaVersion.startsWith('5') ||
220
+ !Array.isArray(schema.pages) ||
221
+ schema.pages.length !== 1
222
+ ) {
223
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page Schema root is invalid.');
224
+ }
225
+ const page = schema.pages[0];
226
+ assertPlainObject(page);
227
+ if (!Array.isArray(page.componentsTree)) {
228
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page component tree is invalid.');
229
+ }
230
+ const components = collectComponents(page.componentsTree);
231
+ if (components.some(component => component.componentName === 'Jsx')) {
232
+ foundationError('SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED', 'Native page Schema is unsupported for canvas/default.');
233
+ }
234
+ const pageComponents = components.filter(component => component.componentName === 'Page');
235
+ const canvasComponents = components.filter(component => component.componentName === 'YidaCodeCanvas');
236
+ if (pageComponents.length !== 1 || canvasComponents.length !== 1) {
237
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page component structure is ambiguous.');
238
+ }
239
+ return {
240
+ canvasComponent: canvasComponents[0],
241
+ canvasComponents,
242
+ pageComponents,
243
+ };
244
+ }
245
+
246
+ function inspectCanvasProps(canvasComponent) {
247
+ const props = requireCanvasProps(canvasComponent);
248
+ const code = props.code;
249
+ const runtimeCode = props.runtimeCode;
250
+ const importedModules = props.importedModules;
251
+ if (
252
+ typeof code !== 'string' ||
253
+ !code ||
254
+ typeof runtimeCode !== 'string' ||
255
+ !runtimeCode ||
256
+ typeof importedModules !== 'string' ||
257
+ !isJsonArrayString(importedModules)
258
+ ) {
259
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page managed props are incomplete.');
260
+ }
261
+ return { code, importedModules, runtimeCode };
262
+ }
263
+
264
+ function requireCanvasProps(canvasComponent) {
265
+ assertPlainObject(canvasComponent);
266
+ if (canvasComponent.props === undefined) {
267
+ canvasComponent.props = {};
268
+ }
269
+ assertPlainObject(canvasComponent.props);
270
+ return canvasComponent.props;
271
+ }
272
+
273
+ function collectComponents(value, result = []) {
274
+ if (!value || typeof value !== 'object') {
275
+ return result;
276
+ }
277
+ if (Array.isArray(value)) {
278
+ for (const item of value) {
279
+ collectComponents(item, result);
280
+ }
281
+ return result;
282
+ }
283
+ if (typeof value.componentName === 'string') {
284
+ result.push(value);
285
+ }
286
+ for (const key of Object.keys(value).sort()) {
287
+ collectComponents(value[key], result);
288
+ }
289
+ return result;
290
+ }
291
+
292
+ function hashCanvasUnmanagedSchema(schema) {
293
+ const unmanaged = clonePlain(schema);
294
+ stripManagedCanvasProps(unmanaged);
295
+ return hashStable(normalizeShellFingerprintValue(unmanaged));
296
+ }
297
+
298
+ function stripManagedCanvasProps(schema) {
299
+ let structure;
300
+ try {
301
+ structure = inspectCanvasStructure(schema);
302
+ } catch (error) {
303
+ return;
304
+ }
305
+ const props = requireCanvasProps(structure.canvasComponent);
306
+ delete props.code;
307
+ delete props.runtimeCode;
308
+ delete props.importedModules;
309
+ }
310
+
311
+ function normalizeShellFingerprintValue(value, pathSegments = [], inheritedComponentName = '') {
312
+ if (Array.isArray(value)) {
313
+ const normalized = value.map((item, index) => normalizeShellFingerprintValue(
314
+ item,
315
+ pathSegments.concat(String(index)),
316
+ inheritedComponentName
317
+ ));
318
+ const collectionName = pathSegments[pathSegments.length - 1];
319
+ if (
320
+ COMPONENT_ORDER_KEYS.has(collectionName) &&
321
+ normalized.every(item => (
322
+ item &&
323
+ typeof item === 'object' &&
324
+ !Array.isArray(item) &&
325
+ typeof item.componentName === 'string'
326
+ ))
327
+ ) {
328
+ normalized.sort(compareCanonicalValues);
329
+ }
330
+ return normalized;
331
+ }
332
+ if (!value || typeof value !== 'object') {
333
+ return value;
334
+ }
335
+
336
+ const ownComponentName = typeof value.componentName === 'string'
337
+ ? value.componentName
338
+ : '';
339
+ const componentName = ownComponentName || inheritedComponentName;
340
+ const result = {};
341
+ for (const key of Object.keys(value).sort()) {
342
+ if (isShellNeutralKey(value, key, pathSegments, ownComponentName, componentName)) {
343
+ continue;
344
+ }
345
+ result[key] = normalizeShellFingerprintValue(
346
+ value[key],
347
+ pathSegments.concat(key),
348
+ componentName
349
+ );
350
+ }
351
+ return result;
352
+ }
353
+
354
+ function isShellNeutralKey(value, key, pathSegments, ownComponentName, componentName) {
355
+ if (SHELL_NEUTRAL_TIME_KEYS.has(key.toLowerCase())) {
356
+ return true;
357
+ }
358
+ if (key === 'id' && ownComponentName) {
359
+ return true;
360
+ }
361
+ if (
362
+ key === 'id' &&
363
+ pathSegments.length === 2 &&
364
+ pathSegments[0] === 'pages' &&
365
+ /^\d+$/.test(pathSegments[1])
366
+ ) {
367
+ return true;
368
+ }
369
+ return (
370
+ key === 'className' &&
371
+ componentName === 'Page' &&
372
+ typeof value[key] === 'string' &&
373
+ /^page_[a-z0-9]+$/i.test(value[key])
374
+ );
375
+ }
376
+
377
+ function compareCanonicalValues(left, right) {
378
+ const leftValue = JSON.stringify(left);
379
+ const rightValue = JSON.stringify(right);
380
+ if (leftValue < rightValue) {
381
+ return -1;
382
+ }
383
+ if (leftValue > rightValue) {
384
+ return 1;
385
+ }
386
+ return 0;
387
+ }
388
+
389
+ function hashRemoteSchema(schema) {
390
+ const normalized = clonePlain(schema);
391
+ delete normalized.gmtModified;
392
+ return hashStable(normalized);
393
+ }
394
+
395
+ function requireCompiledPage(value) {
396
+ if (!isCompiledCanvasPage(value)) {
397
+ foundationError('SCHEMA_PAGE_SOURCE_INVALID', 'Page patch requires trusted compiled Canvas source.');
398
+ }
399
+ return value;
400
+ }
401
+
402
+ function hashStableText(value) {
403
+ return 'sha256:' + crypto.createHash('sha256').update(value).digest('hex');
404
+ }
405
+
406
+ function isJsonArrayString(value) {
407
+ try {
408
+ return Array.isArray(JSON.parse(value));
409
+ } catch {
410
+ return false;
411
+ }
412
+ }
413
+
414
+ function assertPlainObject(value) {
415
+ const prototype = value && typeof value === 'object' ? Object.getPrototypeOf(value) : undefined;
416
+ if (
417
+ !value ||
418
+ typeof value !== 'object' ||
419
+ Array.isArray(value) ||
420
+ (prototype !== Object.prototype && prototype !== null)
421
+ ) {
422
+ foundationError('SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID', 'Canvas page Schema value must be a plain object.');
423
+ }
424
+ }
425
+
426
+ function containsUnsafeText(value) {
427
+ for (let index = 0; index < value.length; index++) {
428
+ const code = value.charCodeAt(index);
429
+ if ((code < 32 && code !== 9 && code !== 10 && code !== 13) || code === 127) {
430
+ return true;
431
+ }
432
+ }
433
+ return false;
434
+ }
435
+
436
+ function clonePlain(value) {
437
+ return JSON.parse(JSON.stringify(value));
438
+ }
439
+
440
+ function foundationError(code, message) {
441
+ throw schemaError(code, message);
442
+ }
443
+
444
+ module.exports = {
445
+ buildCanvasPageSchemaPatch,
446
+ projectCanvasPageSchema,
447
+ };