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.
- package/README.md +37 -2
- package/bin/yida.js +113 -18
- package/docs/capabilities.md +202 -0
- package/lib/agent-center/agent-center.js +2 -3
- package/lib/app/app-list.js +3 -4
- package/lib/app/build-page.js +13 -6
- package/lib/app/check-page.js +16 -6
- package/lib/app/compile.js +17 -7
- package/lib/app/create-app.js +14 -2
- package/lib/app/create-form/api-path.js +25 -0
- package/lib/app/create-form/args.js +13 -0
- package/lib/app/create-form/commands.js +4 -0
- package/lib/app/create-form/definition-reader.js +23 -23
- package/lib/app/create-form/field-normalizers.js +26 -7
- package/lib/app/create-form/schema-patch.js +4 -1
- package/lib/app/create-form.js +155 -68
- package/lib/app/create-page.js +47 -10
- package/lib/app/generate-page.js +20 -1
- package/lib/app/get-form-config.js +2 -1
- package/lib/app/get-schema.js +67 -15
- package/lib/app/import-app.js +94 -42
- package/lib/app/list-forms.js +4 -8
- package/lib/app/page-ir.js +140 -0
- 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 +22 -9
- package/lib/app/update-form-config.js +4 -2
- package/lib/asset/asset-cmd.js +19 -6
- package/lib/auth/oauth-loopback.js +376 -15
- package/lib/auth/token-auth.js +34 -3
- package/lib/auth/token-store.js +22 -4
- package/lib/basic-info/basic-info.js +2 -1
- package/lib/bridge/bridge.js +43 -18
- package/lib/core/agent-capabilities.js +5 -3
- package/lib/core/command-errors.js +38 -0
- package/lib/core/command-manifest.js +59 -1
- package/lib/core/copy.js +12 -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/i18n.js +66 -8
- package/lib/core/legacy-schema-guard.js +408 -0
- package/lib/core/locales/en.js +38 -46
- package/lib/core/locales/zh.js +30 -46
- package/lib/core/sample.js +12 -2
- package/lib/core/utils.js +334 -66
- package/lib/core/yida-client.js +108 -5
- package/lib/corp-efficiency/corp-efficiency.js +2 -2
- package/lib/corp-manager/corp-manager.js +3 -3
- package/lib/formula/evaluate.js +11 -68
- package/lib/formula/field-refs.js +83 -0
- package/lib/i18n-management/i18n-management.js +2 -3
- package/lib/process/configure-process.js +44 -1992
- package/lib/process/create-process.js +54 -87
- package/lib/process/preview-process.js +20 -8
- 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/dashboard-starter.canvas.jsx +5 -2
- package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
- package/lib/samples/yida-canvas-custom-page/portal-shell-home.canvas.jsx +2 -2
- package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +206 -10
- package/lib/samples/yida-custom-page/data-management.oyd.jsx +14 -3
- package/lib/samples/yida-custom-page/design-tokens.js +2 -2
- package/lib/samples/yida-custom-page/detail-profile.oyd.jsx +1 -1
- package/lib/samples/yida-custom-page/portal-shell-home.oyd.jsx +7 -2
- package/lib/samples/yida-custom-page/split-pane-detail.oyd.jsx +2 -1
- 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 +10 -8
- package/scripts/check-release-risks.js +254 -0
- package/scripts/generate-command-docs.js +6 -1
- package/scripts/i18n-baseline.json +11 -11
- package/scripts/postinstall.js +8 -12
- package/scripts/validate-ci.sh +15 -11
- package/scripts/validate-i18n.js +30 -9
- package/scripts/validate-package-size.js +5 -2
- package/scripts/validate-skills.js +4 -0
- package/scripts/validate-structure.js +15 -0
- package/yida-skills/SKILL.md +117 -30
- 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 +78 -17
- 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 +15 -0
- 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 +9 -235
- package/yida-skills/skills/yida-report/references/schema-builder-details.md +239 -0
- package/yida-skills/skills/yida-skill-evaluator/SKILL.md +170 -0
- package/yida-skills/skills-index.json +238 -54
- package/lib/core/locales/ar.js +0 -834
- package/lib/core/locales/de.js +0 -834
- package/lib/core/locales/es.js +0 -834
- package/lib/core/locales/fr.js +0 -834
- package/lib/core/locales/hi.js +0 -834
- package/lib/core/locales/ja.js +0 -1227
- package/lib/core/locales/ko.js +0 -836
- package/lib/core/locales/pt.js +0 -834
- package/lib/core/locales/vi.js +0 -834
- package/lib/core/locales/zh-HK.js +0 -1263
- package/project/pages/src/demo-birthday-game.oyd.jsx +0 -832
- package/project/pages/src/demo-chip-insight.oyd.jsx +0 -983
- package/project/pages/src/demo-compat-smoke.oyd.jsx +0 -58
- package/project/pages/src/demo-crm-batch-entry.oyd.jsx +0 -805
- package/project/pages/src/demo-crm-dashboard.oyd.jsx +0 -677
- package/project/pages/src/demo-future-vision-2026.oyd.jsx +0 -1102
- package/project/pages/src/demo-ppt.oyd.jsx +0 -1192
- package/project/pages/src/demo-salary-calculator.oyd.jsx +0 -904
- package/project/pages/src/openyida-knowledge-doc.oyd.jsx +0 -1714
- 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,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
|
+
};
|