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,19 @@
1
+ 'use strict';
2
+
3
+ const { schemaError } = require('../errors');
4
+
5
+ const KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
6
+
7
+ function validateSemanticKey(key, pointerPath) {
8
+ if (typeof key !== 'string' || !KEY_PATTERN.test(key) || key.includes('.')) {
9
+ throw schemaError('SCHEMA_INVALID_KEY', 'Semantic keys must match /^[A-Za-z][A-Za-z0-9_]*$/ and must not contain dots.', {
10
+ path: pointerPath,
11
+ details: { key },
12
+ });
13
+ }
14
+ }
15
+
16
+ module.exports = {
17
+ KEY_PATTERN,
18
+ validateSemanticKey,
19
+ };
@@ -0,0 +1,566 @@
1
+ 'use strict';
2
+
3
+ const { isDeepStrictEqual } = require('util');
4
+ const { schemaError } = require('../errors');
5
+ const { getResourceState } = require('../state-store');
6
+ const { sortStrings } = require('../sort');
7
+ const { requireSchemaServerRevision } = require('../server-revision');
8
+ const { validateSemanticKey } = require('./keys');
9
+ const { loadPageSource } = require('../page-source-loader');
10
+ const {
11
+ NATIVE_PAGE_PROFILE,
12
+ compileNativePageSource,
13
+ } = require('../../app/services/native-page-compiler');
14
+ const {
15
+ CANVAS_PAGE_PROFILE,
16
+ compileCanvasPageSource,
17
+ } = require('../../app/services/canvas-page-compiler');
18
+ const {
19
+ buildNativePageSchemaPatch,
20
+ computeNativePageShellFingerprint,
21
+ createNativePageShellProfile,
22
+ projectNativePageSchema,
23
+ } = require('../page-foundation');
24
+ const {
25
+ buildCanvasPageSchemaPatch,
26
+ projectCanvasPageSchema,
27
+ } = require('../page-canvas-foundation');
28
+ const {
29
+ buildDataSourceOnlyCanvasPagePatch,
30
+ buildDataSourceOnlyNativePagePatch,
31
+ createDataSourceOnlyShellProfile,
32
+ projectDataSourceOnlyShell,
33
+ } = require('../page-data-source-builder');
34
+ const {
35
+ assertPageWriteReady,
36
+ createPageShellOnce,
37
+ readPageResource,
38
+ savePageSchemaOnce,
39
+ } = require('../../app/services/page-resource-service');
40
+
41
+ const DISPLAY_FORM_TYPE = 'display';
42
+ const PRODUCTION_INITIAL_PROFILE = createDataSourceOnlyShellProfile({
43
+ profileId: 'sac-09b-native-display-shell-v1',
44
+ fingerprint: 'sha256:2e114df34ba29f7a1c7fc7f27a8019db63ba2359c5dd667c64212f902f466f36',
45
+ });
46
+
47
+ const pageAdapter = {
48
+ resourceType: 'page',
49
+ adapterVersion: 1,
50
+ validate(entry) {
51
+ const pointer = `/pages/${escapePointer(entry.key)}`;
52
+ validateSemanticKey(entry.key, pointer);
53
+ const definition = entry.definition || {};
54
+ assertSafeTitle(definition.title, `${pointer}/title`);
55
+ if (typeof definition.source !== 'string' || !definition.source) {
56
+ throw schemaError('SCHEMA_PAGE_SOURCE_INVALID', 'Page source is required.', {
57
+ path: `${pointer}/source`,
58
+ });
59
+ }
60
+ },
61
+
62
+ normalize(entry, context = {}) {
63
+ this.validate(entry, context);
64
+ const loaded = loadPageSource(entry.definition.source, {
65
+ fsImpl: context.fsImpl,
66
+ workspaceRoot: context.workspaceRoot,
67
+ });
68
+ const compiled = compileLoadedPageSource(loaded);
69
+ const dependsOn = new Set([`app:${context.appKey}`]);
70
+ for (const dependency of entry.definition.dependsOn || []) {
71
+ dependsOn.add(normalizeDependency(dependency));
72
+ }
73
+ return {
74
+ resourceType: 'page',
75
+ key: entry.key,
76
+ desired: {
77
+ compiledHash: compiled.compiledHash,
78
+ formType: DISPLAY_FORM_TYPE,
79
+ profile: compiled.profile,
80
+ sourceHash: compiled.sourceHash,
81
+ title: entry.definition.title.trim(),
82
+ },
83
+ source: loaded.relativePath,
84
+ dependsOn: sortStrings(dependsOn),
85
+ dependencySources: {},
86
+ };
87
+ },
88
+
89
+ preflightApply(resource, context = {}) {
90
+ if (hasInjectedPageServices(context.services)) {
91
+ return true;
92
+ }
93
+ return assertPageWriteReady(context);
94
+ },
95
+
96
+ async readObserved(binding, context = {}) {
97
+ const normalized = normalizePageBindings(binding);
98
+ if (!normalized.appType || !normalized.formUuid) {
99
+ throw schemaError('SCHEMA_OBSERVED_BINDING_MISSING', 'Page observed read requires complete bindings.', {
100
+ details: { resourceType: 'page' },
101
+ });
102
+ }
103
+ const reader = context.services && context.services.readPageResource || readPageResource;
104
+ return callWithDispatchBoundary(context, () => reader(context, normalized));
105
+ },
106
+
107
+ projectObserved(observed, binding) {
108
+ const normalized = normalizePageBindings(binding);
109
+ const raw = normalizePageObservation(observed, normalized);
110
+ const projection = projectSavedPage(raw);
111
+ return {
112
+ bindings: normalized,
113
+ managed: projection.managed,
114
+ remoteSchemaHash: projection.remoteSchemaHash,
115
+ };
116
+ },
117
+
118
+ classifyObservedConflict(input = {}) {
119
+ const desired = input.desired || {};
120
+ const lastApplied = input.lastApplied || {};
121
+ const observed = input.observed || {};
122
+ if (desired.title !== lastApplied.title) {
123
+ return 'PAGE_TITLE_UPDATE_UNSUPPORTED';
124
+ }
125
+ if (
126
+ observed.formType !== DISPLAY_FORM_TYPE ||
127
+ observed.profile !== desired.profile ||
128
+ !observed.sourceHash ||
129
+ !observed.compiledHash
130
+ ) {
131
+ return 'PAGE_MANAGED_CONTENT_MISSING';
132
+ }
133
+ return undefined;
134
+ },
135
+
136
+ classifyUpdate() {
137
+ return 'PAGE_SOURCE_CHANGED';
138
+ },
139
+
140
+ async prepareOperation(input, context = {}) {
141
+ const compiled = compileResourceSource(input.resource, context);
142
+ assertCompiledMatchesDesired(compiled, input.resource.desired);
143
+ if (input.operation === 'create') {
144
+ return { compiled };
145
+ }
146
+ const binding = normalizePageBindings(input.stateResource);
147
+ const observed = normalizePageObservation(input.observed, binding);
148
+ const patch = buildUpdatePatch({
149
+ compiled,
150
+ desired: input.resource.desired,
151
+ observed,
152
+ });
153
+ return { compiled, patch };
154
+ },
155
+
156
+ async create(desired, context = {}) {
157
+ const appType = resolveAppType(context.resource, context.state);
158
+ const creator = context.services && context.services.createPageShellOnce || createPageShellOnce;
159
+ const identity = await callWithDispatchBoundary(
160
+ context,
161
+ () => creator(context, { appType, title: desired.title })
162
+ );
163
+ if (typeof context.checkpointCreateIdentity !== 'function') {
164
+ throw schemaError('SCHEMA_APPLY_JOURNAL_INVALID', 'Page create identity checkpoint is unavailable.');
165
+ }
166
+ await context.checkpointCreateIdentity(identity);
167
+ return continuePageCreate(desired, identity, context);
168
+ },
169
+
170
+ async resumeCreate(desired, createIdentity, context = {}) {
171
+ return continuePageCreate(desired, createIdentity && createIdentity.bindings, context);
172
+ },
173
+
174
+ async update(desired, observed, binding, context = {}) {
175
+ const normalized = normalizePageBindings(binding);
176
+ const patch = context.prepared && context.prepared.patch;
177
+ if (!patch || !patch.schema) {
178
+ throw schemaError('SCHEMA_APPLY_JIT_CONFLICT', 'Page update lacks a JIT managed patch.');
179
+ }
180
+ const saver = context.services && context.services.savePageSchemaOnce || savePageSchemaOnce;
181
+ await callWithDispatchBoundary(context, () => saver(context, {
182
+ appType: normalized.appType,
183
+ formUuid: normalized.formUuid,
184
+ schema: patch.schema,
185
+ serverRevision: normalizePageObservation(observed, normalized).serverRevision,
186
+ }));
187
+ return {
188
+ ...normalized,
189
+ preparedRemoteSchemaHash: patch.preparedRemoteSchemaHash,
190
+ };
191
+ },
192
+
193
+ buildBindings(result, context = {}) {
194
+ const existing = normalizePageBindings(context.stateResource);
195
+ const bindings = {
196
+ appType: result && result.appType || existing.appType,
197
+ formUuid: result && result.formUuid || existing.formUuid,
198
+ };
199
+ if (!bindings.appType || !bindings.formUuid) {
200
+ throw schemaError('SCHEMA_APPLY_VERIFY_FAILED', 'Page operation did not produce complete bindings.', {
201
+ details: { resourceType: 'page' },
202
+ });
203
+ }
204
+ return bindings;
205
+ },
206
+
207
+ verify(projection, context = {}) {
208
+ if (!projection || !isDeepStrictEqual(projection.managed, context.resource && context.resource.desired)) {
209
+ throw schemaError('SCHEMA_APPLY_VERIFY_FAILED', 'Page observed projection does not match desired managed state.', {
210
+ details: { resourceType: 'page', key: context.resource && context.resource.key },
211
+ });
212
+ }
213
+ const expected = context.operationResult && context.operationResult.preparedRemoteSchemaHash ||
214
+ context.expectedRemoteSchemaHash;
215
+ if (!expected || projection.remoteSchemaHash !== expected) {
216
+ throw schemaError('SCHEMA_APPLY_VERIFY_FAILED', 'Page post-write Schema contains an unexpected remote delta.', {
217
+ details: { resourceType: 'page', property: 'remoteSchema' },
218
+ });
219
+ }
220
+ return projection;
221
+ },
222
+
223
+ validateStateResource(entry) {
224
+ const state = entry.state || {};
225
+ assertAllowedKeys(state.bindings, ['appType', 'formUuid'], `${entry.path}/bindings`);
226
+ const bindings = normalizePageBindings(state);
227
+ if (!bindings.appType || !bindings.formUuid) {
228
+ throw schemaError('SCHEMA_STATE_INVALID', 'Page state bindings must include appType and formUuid.', {
229
+ path: `${entry.path}/bindings`,
230
+ });
231
+ }
232
+ if (state.lastApplied !== undefined) {
233
+ validateManagedPage(state.lastApplied, `${entry.path}/lastApplied`);
234
+ }
235
+ },
236
+ };
237
+
238
+ async function continuePageCreate(desired, bindingInput, context) {
239
+ const binding = normalizePageBindings(bindingInput);
240
+ const reader = context.services && context.services.readPageResource || readPageResource;
241
+ const observed = normalizePageObservation(
242
+ await callWithDispatchBoundary(context, () => reader(context, binding)),
243
+ binding
244
+ );
245
+ const compiled = context.prepared && context.prepared.compiled;
246
+ assertCompiledMatchesDesired(compiled, desired);
247
+
248
+ const saved = tryProjectSavedPage(observed);
249
+ if (saved) {
250
+ if (!isDeepStrictEqual(saved.managed, desired)) {
251
+ throw schemaError('SCHEMA_APPLY_JIT_CONFLICT', 'Bound page already contains different managed content.', {
252
+ details: { resourceType: 'page' },
253
+ });
254
+ }
255
+ return { ...binding, preparedRemoteSchemaHash: saved.remoteSchemaHash };
256
+ }
257
+
258
+ const patch = buildInitialPatch({
259
+ compiled,
260
+ context,
261
+ binding,
262
+ observed,
263
+ });
264
+ if (!isDeepStrictEqual(desired, {
265
+ compiledHash: compiled.compiledHash,
266
+ formType: DISPLAY_FORM_TYPE,
267
+ profile: compiled.profile,
268
+ sourceHash: compiled.sourceHash,
269
+ title: observed.observedTitle,
270
+ })) {
271
+ throw schemaError('SCHEMA_APPLY_JIT_CONFLICT', 'Created page identity does not match desired managed state.', {
272
+ details: { resourceType: 'page' },
273
+ });
274
+ }
275
+ const saver = context.services && context.services.savePageSchemaOnce || savePageSchemaOnce;
276
+ await callWithDispatchBoundary(context, () => saver(context, {
277
+ appType: binding.appType,
278
+ formUuid: binding.formUuid,
279
+ schema: patch.schema,
280
+ serverRevision: observed.serverRevision,
281
+ }));
282
+ return { ...binding, preparedRemoteSchemaHash: patch.preparedRemoteSchemaHash };
283
+ }
284
+
285
+ async function callWithDispatchBoundary(context, callback) {
286
+ if (typeof context.assertRemoteDispatchBoundary === 'function') {
287
+ context.assertRemoteDispatchBoundary('before');
288
+ }
289
+ const result = await callback();
290
+ if (typeof context.assertRemoteDispatchBoundary === 'function') {
291
+ context.assertRemoteDispatchBoundary('after');
292
+ }
293
+ return result;
294
+ }
295
+
296
+ function projectSavedPage(observed) {
297
+ const canvasProjection = tryProjectCanvasPage(observed);
298
+ if (canvasProjection) {
299
+ return canvasProjection;
300
+ }
301
+ const profile = createSavedProfile(observed.schema);
302
+ const projection = projectNativePageSchema({
303
+ observedFormType: observed.observedFormType,
304
+ observedTitle: observed.observedTitle,
305
+ schema: observed.schema,
306
+ shellProfile: profile,
307
+ });
308
+ if (projection.classification !== 'SCHEMA_SAVED') {
309
+ throw schemaError('SCHEMA_OBSERVED_STRUCTURE_MISMATCH', 'Bound page is not a saved native page.', {
310
+ details: { resourceType: 'page' },
311
+ });
312
+ }
313
+ return projection;
314
+ }
315
+
316
+ function tryProjectSavedPage(observed) {
317
+ try {
318
+ return projectSavedPage(observed);
319
+ } catch (error) {
320
+ if (error && [
321
+ 'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
322
+ 'SCHEMA_OBSERVED_STRUCTURE_MISMATCH',
323
+ ].includes(error.code)) {
324
+ return null;
325
+ }
326
+ throw error;
327
+ }
328
+ }
329
+
330
+ function tryProjectCanvasPage(observed) {
331
+ try {
332
+ return projectCanvasPageSchema({
333
+ observedFormType: observed.observedFormType,
334
+ observedTitle: observed.observedTitle,
335
+ schema: observed.schema,
336
+ });
337
+ } catch (error) {
338
+ if (error && [
339
+ 'SCHEMA_PAGE_FOUNDATION_SCHEMA_INVALID',
340
+ 'SCHEMA_PAGE_FOUNDATION_PROFILE_UNSUPPORTED',
341
+ 'SCHEMA_OBSERVED_STRUCTURE_MISMATCH',
342
+ ].includes(error.code)) {
343
+ return null;
344
+ }
345
+ throw error;
346
+ }
347
+ }
348
+
349
+ function createSavedProfile(schema) {
350
+ return createNativePageShellProfile({
351
+ profileId: 'sac-page-bound-native-v1',
352
+ fingerprint: computeNativePageShellFingerprint(schema),
353
+ });
354
+ }
355
+
356
+ function resolveInitialProfile(context) {
357
+ return context.services && context.services.pageInitialShellProfile || PRODUCTION_INITIAL_PROFILE;
358
+ }
359
+
360
+ function compileResourceSource(resource, context) {
361
+ if (!resource || typeof resource.source !== 'string') {
362
+ throw schemaError('SCHEMA_PAGE_SOURCE_INVALID', 'Normalized page source is missing.');
363
+ }
364
+ return compileLoadedPageSource(loadPageSource(resource.source, {
365
+ fsImpl: context.fsImpl,
366
+ workspaceRoot: context.workspaceRoot || process.cwd(),
367
+ }));
368
+ }
369
+
370
+ function assertCompiledMatchesDesired(compiled, desired) {
371
+ if (
372
+ !compiled ||
373
+ compiled.profile !== desired.profile ||
374
+ compiled.sourceHash !== desired.sourceHash ||
375
+ compiled.compiledHash !== desired.compiledHash
376
+ ) {
377
+ throw schemaError('SCHEMA_APPLY_PLAN_STALE', 'Page source changed after the reviewed manifest.');
378
+ }
379
+ }
380
+
381
+ function compileLoadedPageSource(loaded) {
382
+ if (loaded && loaded.profile === CANVAS_PAGE_PROFILE) {
383
+ return compileCanvasPageSource(loaded);
384
+ }
385
+ if (loaded && loaded.profile === NATIVE_PAGE_PROFILE) {
386
+ return compileNativePageSource(loaded);
387
+ }
388
+ throw schemaError('SCHEMA_PAGE_SOURCE_PROFILE_UNSUPPORTED', 'Page source profile is unsupported.');
389
+ }
390
+
391
+ function buildUpdatePatch(input) {
392
+ if (input.desired.profile === CANVAS_PAGE_PROFILE) {
393
+ const currentProjection = projectCanvasPageSchema({
394
+ observedFormType: input.observed.observedFormType,
395
+ observedTitle: input.observed.observedTitle,
396
+ schema: input.observed.schema,
397
+ });
398
+ return buildCanvasPageSchemaPatch({
399
+ compiledPage: input.compiled,
400
+ desiredFormType: DISPLAY_FORM_TYPE,
401
+ desiredTitle: input.desired.title,
402
+ expectedRemoteSchemaHash: currentProjection.remoteSchemaHash,
403
+ observedFormType: input.observed.observedFormType,
404
+ observedTitle: input.observed.observedTitle,
405
+ remoteSchema: input.observed.schema,
406
+ });
407
+ }
408
+
409
+ const profile = createSavedProfile(input.observed.schema);
410
+ const currentProjection = projectNativePageSchema({
411
+ observedFormType: input.observed.observedFormType,
412
+ observedTitle: input.observed.observedTitle,
413
+ schema: input.observed.schema,
414
+ shellProfile: profile,
415
+ });
416
+ return buildNativePageSchemaPatch({
417
+ compiledPage: input.compiled,
418
+ desiredFormType: DISPLAY_FORM_TYPE,
419
+ desiredTitle: input.desired.title,
420
+ expectedRemoteSchemaHash: currentProjection.remoteSchemaHash,
421
+ observedFormType: input.observed.observedFormType,
422
+ observedTitle: input.observed.observedTitle,
423
+ remoteSchema: input.observed.schema,
424
+ shellProfile: profile,
425
+ });
426
+ }
427
+
428
+ function buildInitialPatch(input) {
429
+ const shellProfile = resolveInitialProfile(input.context);
430
+ const initialProjection = projectDataSourceOnlyShell({
431
+ observedFormType: input.observed.observedFormType,
432
+ observedTitle: input.observed.observedTitle,
433
+ schema: input.observed.schema,
434
+ shellProfile,
435
+ });
436
+ const baseInput = {
437
+ compiledPage: input.compiled,
438
+ expectedInitialRemoteSchemaHash: initialProjection.remoteSchemaHash,
439
+ formUuid: input.binding.formUuid,
440
+ observedFormType: input.observed.observedFormType,
441
+ observedTitle: input.observed.observedTitle,
442
+ operationId: input.context.operationId,
443
+ remoteSchema: input.observed.schema,
444
+ shellProfile,
445
+ };
446
+ if (input.compiled.profile === CANVAS_PAGE_PROFILE) {
447
+ return buildDataSourceOnlyCanvasPagePatch(baseInput);
448
+ }
449
+ return buildDataSourceOnlyNativePagePatch(baseInput);
450
+ }
451
+
452
+ function normalizePageObservation(value, bindings) {
453
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
454
+ throw schemaError('SCHEMA_PAGE_READ_FAILED', 'Bound page observation is invalid.');
455
+ }
456
+ if (value.appType !== bindings.appType || value.formUuid !== bindings.formUuid) {
457
+ throw schemaError('SCHEMA_OBSERVED_STRUCTURE_MISMATCH', 'Bound page observation identity changed.', {
458
+ details: { resourceType: 'page' },
459
+ });
460
+ }
461
+ return {
462
+ appType: bindings.appType,
463
+ formUuid: bindings.formUuid,
464
+ observedFormType: value.observedFormType,
465
+ observedTitle: value.observedTitle,
466
+ schema: value.schema,
467
+ serverRevision: requireSchemaServerRevision(
468
+ value.schema,
469
+ () => schemaError('SCHEMA_REMOTE_READ_FAILED', 'Observed page Schema revision is missing or invalid.', {
470
+ details: { resourceType: 'page' },
471
+ })
472
+ ),
473
+ };
474
+ }
475
+
476
+ function normalizePageBindings(value) {
477
+ const source = value && value.bindings ? value.bindings : value || {};
478
+ const appType = typeof source.appType === 'string' ? source.appType : '';
479
+ const formUuid = typeof source.formUuid === 'string' ? source.formUuid : '';
480
+ if ((appType && !formUuid) || (!appType && formUuid)) {
481
+ throw schemaError('SCHEMA_STATE_INVALID', 'Page bindings must include appType and formUuid together.');
482
+ }
483
+ return { appType, formUuid };
484
+ }
485
+
486
+ function resolveAppType(resource, state) {
487
+ for (const dependency of resource && resource.dependsOn || []) {
488
+ if (!dependency.startsWith('app:')) {
489
+ continue;
490
+ }
491
+ const app = getResourceState(state, 'app', dependency.slice(4));
492
+ if (app && app.bindings && app.bindings.appType) {
493
+ return app.bindings.appType;
494
+ }
495
+ }
496
+ throw schemaError('SCHEMA_STATE_RESOURCE_BINDING_MISSING', 'Page dependency app binding is missing.', {
497
+ details: { resourceType: 'page' },
498
+ });
499
+ }
500
+
501
+ function normalizeDependency(value) {
502
+ const match = typeof value === 'string' && /^(app|form|page|process):([A-Za-z][A-Za-z0-9_]*)$/.exec(value);
503
+ if (!match) {
504
+ throw schemaError('SCHEMA_DEPENDENCY_NOT_FOUND', 'Page dependency must use a supported semantic resource ID.');
505
+ }
506
+ return `${match[1]}:${match[2]}`;
507
+ }
508
+
509
+ function validateManagedPage(value, pointer) {
510
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
511
+ throw schemaError('SCHEMA_STATE_INVALID', 'Page lastApplied must be a compact managed object.', { path: pointer });
512
+ }
513
+ assertAllowedKeys(value, ['compiledHash', 'formType', 'profile', 'sourceHash', 'title'], pointer);
514
+ if (
515
+ value.formType !== DISPLAY_FORM_TYPE ||
516
+ ![NATIVE_PAGE_PROFILE, CANVAS_PAGE_PROFILE].includes(value.profile) ||
517
+ !isHash(value.sourceHash) ||
518
+ !isHash(value.compiledHash)
519
+ ) {
520
+ throw schemaError('SCHEMA_STATE_INVALID', 'Page lastApplied managed content is invalid.', { path: pointer });
521
+ }
522
+ assertSafeTitle(value.title, `${pointer}/title`);
523
+ }
524
+
525
+ function assertSafeTitle(value, pointer) {
526
+ const title = typeof value === 'string' ? value.trim() : '';
527
+ if (!title || title.length > 256 || containsUnsafeText(title)) {
528
+ throw schemaError('SCHEMA_MANIFEST_SCHEMA_INVALID', 'Page title is invalid.', { path: pointer });
529
+ }
530
+ }
531
+
532
+ function containsUnsafeText(value) {
533
+ for (let index = 0; index < value.length; index++) {
534
+ const code = value.charCodeAt(index);
535
+ if ((code < 32 && code !== 9 && code !== 10 && code !== 13) || code === 127) {
536
+ return true;
537
+ }
538
+ }
539
+ return false;
540
+ }
541
+
542
+ function assertAllowedKeys(value, keys, pointer) {
543
+ const allowed = new Set(keys);
544
+ for (const key of Object.keys(value || {})) {
545
+ if (!allowed.has(key)) {
546
+ throw schemaError('SCHEMA_STATE_INVALID', 'State contains an unknown property.', {
547
+ path: `${pointer}/${escapePointer(key)}`,
548
+ });
549
+ }
550
+ }
551
+ }
552
+
553
+ function hasInjectedPageServices(services = {}) {
554
+ return ['createPageShellOnce', 'readPageResource', 'savePageSchemaOnce']
555
+ .every(name => typeof services[name] === 'function');
556
+ }
557
+
558
+ function isHash(value) {
559
+ return typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value);
560
+ }
561
+
562
+ function escapePointer(value) {
563
+ return String(value).replace(/~/g, '~0').replace(/\//g, '~1');
564
+ }
565
+
566
+ module.exports = { pageAdapter };