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,177 @@
1
+ 'use strict';
2
+
3
+ const { schemaError } = require('./errors');
4
+
5
+ const MANIFEST_INPUT_LIMITS = Object.freeze({
6
+ maxBytes: 1024 * 1024,
7
+ maxDepth: 80,
8
+ maxNodes: 20000,
9
+ maxCollectionItems: 1000,
10
+ maxStringLength: 8192,
11
+ });
12
+
13
+ const MANIFEST_LIMIT_KEYS = Object.freeze(Object.keys(MANIFEST_INPUT_LIMITS));
14
+
15
+ function createManifestLimitState(options = {}) {
16
+ return {
17
+ limits: resolveManifestInputLimits(options),
18
+ nodes: 0,
19
+ seen: typeof WeakSet === 'function' ? new WeakSet() : null,
20
+ };
21
+ }
22
+
23
+ function resolveManifestInputLimits(options = {}) {
24
+ const overrides = options.limits;
25
+ if (overrides === undefined) {
26
+ return Object.assign({}, MANIFEST_INPUT_LIMITS);
27
+ }
28
+ if (!overrides || typeof overrides !== 'object' || Array.isArray(overrides)) {
29
+ throwInvalidLimit();
30
+ }
31
+
32
+ const result = {};
33
+ const knownKeys = new Set(MANIFEST_LIMIT_KEYS);
34
+ for (const key of Object.keys(overrides)) {
35
+ if (!knownKeys.has(key)) {
36
+ throwInvalidLimit();
37
+ }
38
+ }
39
+ for (const key of MANIFEST_LIMIT_KEYS) {
40
+ const defaultValue = MANIFEST_INPUT_LIMITS[key];
41
+ if (!Object.prototype.hasOwnProperty.call(overrides, key)) {
42
+ result[key] = defaultValue;
43
+ continue;
44
+ }
45
+ const override = overrides[key];
46
+ if (!Number.isSafeInteger(override) || override <= 0) {
47
+ throwInvalidLimit();
48
+ }
49
+ result[key] = Math.min(defaultValue, override);
50
+ }
51
+ return result;
52
+ }
53
+
54
+ function throwInvalidLimit() {
55
+ throw schemaError('SCHEMA_MANIFEST_LIMIT_INVALID', 'Manifest input limit override is invalid.');
56
+ }
57
+
58
+ function assertManifestFileSize(byteLength, options = {}) {
59
+ const limits = resolveManifestInputLimits(options);
60
+ if (!Number.isFinite(byteLength) || byteLength < 0) {
61
+ throw schemaError('SCHEMA_MANIFEST_LOAD_FAILED', 'Unable to read manifest file.');
62
+ }
63
+ if (byteLength > limits.maxBytes) {
64
+ throw schemaError('SCHEMA_MANIFEST_TOO_LARGE', 'Manifest file exceeds the maximum supported size.', {
65
+ path: '/',
66
+ details: {
67
+ limit: limits.maxBytes,
68
+ },
69
+ });
70
+ }
71
+ }
72
+
73
+ function assertManifestNode(state, pathSegments, depth) {
74
+ if (depth > state.limits.maxDepth) {
75
+ throw schemaError('SCHEMA_MANIFEST_DEPTH_LIMIT_EXCEEDED', 'Manifest JSON exceeds the maximum supported depth.', {
76
+ path: formatJsonPointer(pathSegments),
77
+ details: {
78
+ limit: state.limits.maxDepth,
79
+ },
80
+ });
81
+ }
82
+ state.nodes += 1;
83
+ if (state.nodes > state.limits.maxNodes) {
84
+ throw schemaError('SCHEMA_MANIFEST_NODE_LIMIT_EXCEEDED', 'Manifest JSON exceeds the maximum supported node count.', {
85
+ path: formatJsonPointer(pathSegments),
86
+ details: {
87
+ limit: state.limits.maxNodes,
88
+ },
89
+ });
90
+ }
91
+ }
92
+
93
+ function assertManifestCollectionSize(state, size, pathSegments) {
94
+ if (size > state.limits.maxCollectionItems) {
95
+ throw schemaError('SCHEMA_MANIFEST_COLLECTION_LIMIT_EXCEEDED', 'Manifest JSON collection exceeds the maximum supported item count.', {
96
+ path: formatJsonPointer(pathSegments),
97
+ details: {
98
+ limit: state.limits.maxCollectionItems,
99
+ },
100
+ });
101
+ }
102
+ }
103
+
104
+ function assertManifestStringLength(state, value, pathSegments) {
105
+ if (String(value).length > state.limits.maxStringLength) {
106
+ throw schemaError('SCHEMA_MANIFEST_STRING_LIMIT_EXCEEDED', 'Manifest JSON string exceeds the maximum supported length.', {
107
+ path: formatJsonPointer(pathSegments),
108
+ details: {
109
+ limit: state.limits.maxStringLength,
110
+ },
111
+ });
112
+ }
113
+ }
114
+
115
+ function assertManifestObjectLimits(value, options = {}) {
116
+ const state = createManifestLimitState(options);
117
+ visit(value, state, [], 0);
118
+ }
119
+
120
+ function visit(value, state, pathSegments, depth) {
121
+ assertManifestNode(state, pathSegments, depth);
122
+ if (typeof value === 'string') {
123
+ assertManifestStringLength(state, value, pathSegments);
124
+ return;
125
+ }
126
+ if (!value || typeof value !== 'object') {
127
+ return;
128
+ }
129
+ if (state.seen) {
130
+ if (state.seen.has(value)) {
131
+ throw schemaError('SCHEMA_MANIFEST_STRUCTURE_UNSUPPORTED', 'Manifest JSON object graph must be acyclic.', {
132
+ path: formatJsonPointer(pathSegments),
133
+ });
134
+ }
135
+ state.seen.add(value);
136
+ }
137
+ if (Array.isArray(value)) {
138
+ assertManifestCollectionSize(state, value.length, pathSegments);
139
+ value.forEach((item, index) => visit(item, state, pathSegments.concat(String(index)), depth + 1));
140
+ return;
141
+ }
142
+ if (!isPlainJsonObject(value)) {
143
+ throw schemaError('SCHEMA_MANIFEST_STRUCTURE_UNSUPPORTED', 'Manifest JSON object graph must contain only plain objects and arrays.', {
144
+ path: formatJsonPointer(pathSegments),
145
+ });
146
+ }
147
+ const keys = Object.keys(value);
148
+ assertManifestCollectionSize(state, keys.length, pathSegments);
149
+ keys.forEach((key) => {
150
+ assertManifestStringLength(state, key, pathSegments.concat(key));
151
+ visit(value[key], state, pathSegments.concat(key), depth + 1);
152
+ });
153
+ }
154
+
155
+ function isPlainJsonObject(value) {
156
+ const prototype = Object.getPrototypeOf(value);
157
+ return prototype === Object.prototype || prototype === null;
158
+ }
159
+
160
+ function formatJsonPointer(segments) {
161
+ if (!segments || segments.length === 0) {
162
+ return '/';
163
+ }
164
+ return '/' + segments.map(segment => String(segment).replace(/~/g, '~0').replace(/\//g, '~1')).join('/');
165
+ }
166
+
167
+ module.exports = {
168
+ MANIFEST_INPUT_LIMITS,
169
+ assertManifestCollectionSize,
170
+ assertManifestFileSize,
171
+ assertManifestNode,
172
+ assertManifestObjectLimits,
173
+ assertManifestStringLength,
174
+ createManifestLimitState,
175
+ formatJsonPointer,
176
+ resolveManifestInputLimits,
177
+ };
@@ -0,0 +1,309 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { schemaError } = require('./errors');
6
+ const {
7
+ assertManifestCollectionSize,
8
+ assertManifestFileSize,
9
+ assertManifestNode,
10
+ assertManifestStringLength,
11
+ createManifestLimitState,
12
+ formatJsonPointer,
13
+ resolveManifestInputLimits,
14
+ } = require('./manifest-limits');
15
+
16
+ function loadManifest(manifestPath, options = {}) {
17
+ if (!manifestPath || typeof manifestPath !== 'string') {
18
+ throw schemaError('SCHEMA_INVALID_ARGUMENTS', 'Manifest path is required.');
19
+ }
20
+
21
+ const resolved = resolveManifestPath(manifestPath, options);
22
+ const raw = readManifestFileBounded(resolved.realPath, options);
23
+
24
+ detectDuplicateJsonKeys(raw, options);
25
+
26
+ try {
27
+ return JSON.parse(raw);
28
+ } catch (error) {
29
+ throw schemaError('SCHEMA_MANIFEST_PARSE_FAILED', 'Manifest file must be valid JSON.', {
30
+ details: {
31
+ reason: error && error.message ? String(error.message).split('\n')[0] : 'Invalid JSON',
32
+ },
33
+ });
34
+ }
35
+ }
36
+
37
+ function readManifestFileBounded(filePath, options = {}) {
38
+ const fsImpl = options.fsImpl || fs;
39
+ const limits = resolveManifestInputLimits(options);
40
+ const readLimit = limits.maxBytes + 1;
41
+ const flags = resolveOpenFlags(fsImpl);
42
+ let fd;
43
+ try {
44
+ fd = fsImpl.openSync(filePath, flags);
45
+ const stat = fsImpl.fstatSync(fd);
46
+ if (!stat || typeof stat.isFile !== 'function' || !stat.isFile()) {
47
+ throw schemaError('SCHEMA_MANIFEST_LOAD_FAILED', 'Unable to read manifest file.');
48
+ }
49
+ assertManifestFileSize(stat.size, { limits });
50
+
51
+ const buffer = Buffer.alloc(readLimit);
52
+ let totalRead = 0;
53
+ while (totalRead < readLimit) {
54
+ const bytesRead = fsImpl.readSync(fd, buffer, totalRead, readLimit - totalRead, null);
55
+ if (bytesRead === 0) {
56
+ break;
57
+ }
58
+ totalRead += bytesRead;
59
+ if (totalRead > limits.maxBytes) {
60
+ throw schemaError('SCHEMA_MANIFEST_TOO_LARGE', 'Manifest file exceeds the maximum supported size.', {
61
+ path: '/',
62
+ details: { limit: limits.maxBytes },
63
+ });
64
+ }
65
+ }
66
+ return buffer.toString('utf8', 0, totalRead);
67
+ } catch (error) {
68
+ if (error && error.code && String(error.code).startsWith('SCHEMA_')) {
69
+ throw error;
70
+ }
71
+ throw schemaError('SCHEMA_MANIFEST_LOAD_FAILED', 'Unable to read manifest file.');
72
+ } finally {
73
+ if (fd !== undefined) {
74
+ try {
75
+ fsImpl.closeSync(fd);
76
+ } catch {
77
+ // Best effort close: keep the original read/validation error stable.
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ function resolveOpenFlags(fsImpl) {
84
+ const constants = fsImpl.constants || fs.constants || {};
85
+ const readOnly = constants.O_RDONLY !== undefined ? constants.O_RDONLY : 'r';
86
+ if (typeof readOnly === 'number' && typeof constants.O_NOFOLLOW === 'number') {
87
+ return readOnly | constants.O_NOFOLLOW;
88
+ }
89
+ return readOnly;
90
+ }
91
+
92
+ function resolveManifestPath(manifestPath, options = {}) {
93
+ const workspaceRoot = path.resolve(options.workspaceRoot || options.projectRoot || process.cwd());
94
+ let rootRealPath;
95
+ try {
96
+ rootRealPath = fs.realpathSync(workspaceRoot);
97
+ } catch {
98
+ throw schemaError('SCHEMA_MANIFEST_PATH_UNSAFE', 'Manifest path must stay inside the workspace trust root.', {
99
+ path: '/',
100
+ });
101
+ }
102
+
103
+ const absolutePath = path.isAbsolute(manifestPath)
104
+ ? path.resolve(manifestPath)
105
+ : path.resolve(rootRealPath, manifestPath);
106
+ const lexicallyInside = isPathInside(rootRealPath, absolutePath) ||
107
+ (path.isAbsolute(manifestPath) && isPathInside(workspaceRoot, absolutePath));
108
+
109
+ let realPath;
110
+ try {
111
+ realPath = fs.realpathSync(absolutePath);
112
+ } catch {
113
+ if (!lexicallyInside) {
114
+ throw schemaError('SCHEMA_MANIFEST_PATH_UNSAFE', 'Manifest path must stay inside the workspace trust root.', {
115
+ path: '/',
116
+ });
117
+ }
118
+ throw schemaError('SCHEMA_MANIFEST_LOAD_FAILED', 'Unable to read manifest file.');
119
+ }
120
+ if (!isPathInside(rootRealPath, realPath)) {
121
+ throw schemaError('SCHEMA_MANIFEST_PATH_UNSAFE', 'Manifest path must stay inside the workspace trust root.', {
122
+ path: '/',
123
+ });
124
+ }
125
+
126
+ return {
127
+ realPath,
128
+ rootRealPath,
129
+ };
130
+ }
131
+
132
+ function isPathInside(rootPath, targetPath) {
133
+ const relative = path.relative(rootPath, targetPath);
134
+ return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative));
135
+ }
136
+
137
+ function detectDuplicateJsonKeys(source, options = {}) {
138
+ let index = 0;
139
+ const limitState = createManifestLimitState(options);
140
+
141
+ function skipWhitespace() {
142
+ while (index < source.length && /\s/.test(source[index])) {
143
+ index += 1;
144
+ }
145
+ }
146
+
147
+ function parseString(pathSegments) {
148
+ let result = '';
149
+ index += 1;
150
+ while (index < source.length) {
151
+ const char = source[index];
152
+ if (char === '"') {
153
+ index += 1;
154
+ return result;
155
+ }
156
+ if (char === '\\') {
157
+ index += 1;
158
+ if (index >= source.length) {
159
+ return result;
160
+ }
161
+ const escaped = source[index];
162
+ if (escaped === 'u') {
163
+ const hex = source.slice(index + 1, index + 5);
164
+ if (/^[0-9a-fA-F]{4}$/.test(hex)) {
165
+ result += String.fromCharCode(parseInt(hex, 16));
166
+ index += 5;
167
+ } else {
168
+ index += 1;
169
+ }
170
+ continue;
171
+ }
172
+ const escapeMap = {
173
+ '"': '"',
174
+ '\\': '\\',
175
+ '/': '/',
176
+ b: '\b',
177
+ f: '\f',
178
+ n: '\n',
179
+ r: '\r',
180
+ t: '\t',
181
+ };
182
+ result += Object.prototype.hasOwnProperty.call(escapeMap, escaped) ? escapeMap[escaped] : escaped;
183
+ assertManifestStringLength(limitState, result, pathSegments);
184
+ index += 1;
185
+ continue;
186
+ }
187
+ result += char;
188
+ assertManifestStringLength(limitState, result, pathSegments);
189
+ index += 1;
190
+ }
191
+ return result;
192
+ }
193
+
194
+ function parseNumber() {
195
+ const start = index;
196
+ while (index < source.length && /[-+0-9.eE]/.test(source[index])) {
197
+ index += 1;
198
+ }
199
+ if (index === start) {
200
+ index += 1;
201
+ }
202
+ }
203
+
204
+ function parseLiteral(value) {
205
+ if (source.slice(index, index + value.length) === value) {
206
+ index += value.length;
207
+ } else {
208
+ index += 1;
209
+ }
210
+ }
211
+
212
+ function parseArray(pathSegments, depth) {
213
+ index += 1;
214
+ let itemIndex = 0;
215
+ skipWhitespace();
216
+ if (source[index] === ']') {
217
+ index += 1;
218
+ return;
219
+ }
220
+ while (index < source.length) {
221
+ if (itemIndex + 1 > limitState.limits.maxCollectionItems) {
222
+ assertManifestCollectionSize(limitState, itemIndex + 1, pathSegments);
223
+ }
224
+ parseValue(pathSegments.concat(String(itemIndex)), depth + 1);
225
+ itemIndex += 1;
226
+ skipWhitespace();
227
+ if (source[index] === ',') {
228
+ index += 1;
229
+ continue;
230
+ }
231
+ if (source[index] === ']') {
232
+ index += 1;
233
+ }
234
+ return;
235
+ }
236
+ }
237
+
238
+ function parseObject(pathSegments, depth) {
239
+ index += 1;
240
+ const keys = new Set();
241
+ skipWhitespace();
242
+ if (source[index] === '}') {
243
+ index += 1;
244
+ return;
245
+ }
246
+ while (index < source.length) {
247
+ skipWhitespace();
248
+ if (source[index] !== '"') {
249
+ return;
250
+ }
251
+ const key = parseString(pathSegments);
252
+ assertManifestStringLength(limitState, key, pathSegments.concat(key));
253
+ if (keys.has(key)) {
254
+ throw schemaError('SCHEMA_DUPLICATE_KEY', 'Manifest contains duplicate JSON object keys.', {
255
+ path: formatJsonPointer(pathSegments.concat(key)),
256
+ details: { key },
257
+ });
258
+ }
259
+ keys.add(key);
260
+ assertManifestCollectionSize(limitState, keys.size, pathSegments);
261
+ skipWhitespace();
262
+ if (source[index] !== ':') {
263
+ return;
264
+ }
265
+ index += 1;
266
+ parseValue(pathSegments.concat(key), depth + 1);
267
+ skipWhitespace();
268
+ if (source[index] === ',') {
269
+ index += 1;
270
+ continue;
271
+ }
272
+ if (source[index] === '}') {
273
+ index += 1;
274
+ }
275
+ return;
276
+ }
277
+ }
278
+
279
+ function parseValue(pathSegments, depth) {
280
+ skipWhitespace();
281
+ assertManifestNode(limitState, pathSegments, depth);
282
+ const char = source[index];
283
+ if (char === '{') {
284
+ parseObject(pathSegments, depth);
285
+ } else if (char === '[') {
286
+ parseArray(pathSegments, depth);
287
+ } else if (char === '"') {
288
+ parseString(pathSegments);
289
+ } else if (char === 't') {
290
+ parseLiteral('true');
291
+ } else if (char === 'f') {
292
+ parseLiteral('false');
293
+ } else if (char === 'n') {
294
+ parseLiteral('null');
295
+ } else {
296
+ parseNumber();
297
+ }
298
+ }
299
+
300
+ parseValue([], 0);
301
+ }
302
+
303
+ module.exports = {
304
+ detectDuplicateJsonKeys,
305
+ isPathInside,
306
+ loadManifest,
307
+ readManifestFileBounded,
308
+ resolveManifestPath,
309
+ };
@@ -0,0 +1,193 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://openyida.ai/schemas/openyida-app-manifest-v1.json",
4
+ "type": "object",
5
+ "required": ["kind", "schemaVersion", "app"],
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "kind": {
9
+ "const": "openyida_app_manifest"
10
+ },
11
+ "schemaVersion": {
12
+ "const": 1
13
+ },
14
+ "app": {
15
+ "$ref": "#/definitions/app"
16
+ },
17
+ "forms": {
18
+ "type": "object",
19
+ "additionalProperties": {
20
+ "$ref": "#/definitions/form"
21
+ }
22
+ },
23
+ "processes": {
24
+ "type": "object",
25
+ "additionalProperties": {
26
+ "$ref": "#/definitions/process"
27
+ }
28
+ },
29
+ "pages": {
30
+ "type": "object",
31
+ "additionalProperties": {
32
+ "$ref": "#/definitions/page"
33
+ }
34
+ }
35
+ },
36
+ "definitions": {
37
+ "app": {
38
+ "type": "object",
39
+ "required": ["key", "name"],
40
+ "additionalProperties": false,
41
+ "properties": {
42
+ "key": {
43
+ "type": "string",
44
+ "minLength": 1
45
+ },
46
+ "name": {
47
+ "type": "string",
48
+ "minLength": 1
49
+ }
50
+ }
51
+ },
52
+ "form": {
53
+ "type": "object",
54
+ "required": ["title", "fields"],
55
+ "additionalProperties": false,
56
+ "properties": {
57
+ "title": {
58
+ "type": "string",
59
+ "minLength": 1
60
+ },
61
+ "mode": {
62
+ "enum": ["receipt", "process"]
63
+ },
64
+ "dependsOn": {
65
+ "$ref": "#/definitions/dependencyList"
66
+ },
67
+ "fields": {
68
+ "type": "object",
69
+ "minProperties": 1,
70
+ "additionalProperties": {
71
+ "$ref": "#/definitions/field"
72
+ }
73
+ }
74
+ }
75
+ },
76
+ "field": {
77
+ "type": "object",
78
+ "required": ["type", "label"],
79
+ "additionalProperties": false,
80
+ "properties": {
81
+ "type": {
82
+ "type": "string",
83
+ "minLength": 1
84
+ },
85
+ "label": {
86
+ "type": "string",
87
+ "minLength": 1
88
+ },
89
+ "required": {
90
+ "type": "boolean"
91
+ },
92
+ "options": {
93
+ "type": "array",
94
+ "items": {
95
+ "oneOf": [
96
+ { "type": "string", "minLength": 1 },
97
+ {
98
+ "type": "object",
99
+ "required": ["label", "value"],
100
+ "additionalProperties": false,
101
+ "properties": {
102
+ "label": { "type": "string", "minLength": 1 },
103
+ "value": { "type": "string", "minLength": 1 }
104
+ }
105
+ }
106
+ ]
107
+ }
108
+ },
109
+ "form": {
110
+ "type": "string",
111
+ "minLength": 1
112
+ },
113
+ "dependsOn": {
114
+ "$ref": "#/definitions/dependencyList"
115
+ },
116
+ "children": {
117
+ "type": "object",
118
+ "minProperties": 1,
119
+ "additionalProperties": {
120
+ "$ref": "#/definitions/field"
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "process": {
126
+ "type": "object",
127
+ "required": ["form", "nodes"],
128
+ "additionalProperties": false,
129
+ "properties": {
130
+ "form": {
131
+ "type": "string",
132
+ "minLength": 1
133
+ },
134
+ "nodes": {
135
+ "type": "array",
136
+ "minItems": 1,
137
+ "items": {
138
+ "$ref": "#/definitions/processNode"
139
+ }
140
+ }
141
+ }
142
+ },
143
+ "page": {
144
+ "type": "object",
145
+ "required": ["title", "source"],
146
+ "additionalProperties": false,
147
+ "properties": {
148
+ "title": {
149
+ "type": "string",
150
+ "minLength": 1
151
+ },
152
+ "source": {
153
+ "type": "string",
154
+ "minLength": 1
155
+ },
156
+ "dependsOn": {
157
+ "$ref": "#/definitions/dependencyList"
158
+ }
159
+ }
160
+ },
161
+ "processNode": {
162
+ "type": "object",
163
+ "required": ["key", "type", "name", "approver"],
164
+ "additionalProperties": false,
165
+ "properties": {
166
+ "key": {
167
+ "type": "string",
168
+ "minLength": 1
169
+ },
170
+ "type": {
171
+ "type": "string",
172
+ "minLength": 1
173
+ },
174
+ "name": {
175
+ "type": "string",
176
+ "minLength": 1
177
+ },
178
+ "approver": {
179
+ "type": "string",
180
+ "minLength": 1
181
+ }
182
+ }
183
+ },
184
+ "dependencyList": {
185
+ "type": "array",
186
+ "items": {
187
+ "type": "string",
188
+ "minLength": 1
189
+ },
190
+ "uniqueItems": true
191
+ }
192
+ }
193
+ }