openyida 2026.7.19 → 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 +14 -0
- package/bin/yida.js +107 -18
- package/lib/app/create-app.js +12 -1
- package/lib/app/create-form/args.js +13 -0
- package/lib/app/create-form.js +154 -67
- package/lib/app/create-page.js +45 -9
- package/lib/app/get-schema.js +65 -14
- package/lib/app/import-app.js +92 -41
- 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 +18 -6
- package/lib/auth/token-auth.js +34 -3
- package/lib/auth/token-store.js +22 -4
- package/lib/bridge/bridge.js +37 -5
- package/lib/core/agent-capabilities.js +5 -3
- package/lib/core/command-manifest.js +40 -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/legacy-schema-guard.js +408 -0
- package/lib/core/locales/en.js +8 -46
- package/lib/core/locales/zh.js +8 -46
- package/lib/core/sample.js +1 -1
- package/lib/core/utils.js +334 -66
- package/lib/core/yida-client.js +104 -3
- package/lib/formula/evaluate.js +8 -66
- package/lib/formula/field-refs.js +83 -0
- package/lib/process/configure-process.js +44 -1992
- package/lib/process/create-process.js +54 -87
- 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/data-management.canvas.jsx +228 -14
- package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +204 -8
- 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 +2 -2
- package/scripts/postinstall.js +8 -12
- package/scripts/validate-package-size.js +1 -1
- package/yida-skills/SKILL.md +114 -29
- 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 +60 -14
- 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 +7 -1
- 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 +7 -1
- package/yida-skills/skills-index.json +14 -46
- 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,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function hasRemoteDispatchBoundary(context) {
|
|
4
|
+
return !!(context && typeof context.assertRemoteDispatchBoundary === 'function');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function assertRemoteDispatchBoundary(context, phase) {
|
|
8
|
+
if (hasRemoteDispatchBoundary(context)) {
|
|
9
|
+
context.assertRemoteDispatchBoundary(phase);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function dispatchRemotePrimitive(context, callback) {
|
|
14
|
+
assertRemoteDispatchBoundary(context, 'before');
|
|
15
|
+
let result;
|
|
16
|
+
let requestError;
|
|
17
|
+
try {
|
|
18
|
+
result = await callback();
|
|
19
|
+
} catch (error) {
|
|
20
|
+
requestError = error;
|
|
21
|
+
}
|
|
22
|
+
assertRemoteDispatchBoundary(context, 'after');
|
|
23
|
+
if (requestError) {
|
|
24
|
+
throw requestError;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function rethrowRemoteDispatchBoundaryFailure(error) {
|
|
30
|
+
if (error && error.code === 'SCHEMA_APPLY_LOCK_LOST') {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = Object.freeze({
|
|
36
|
+
dispatchRemotePrimitive,
|
|
37
|
+
hasRemoteDispatchBoundary,
|
|
38
|
+
rethrowRemoteDispatchBoundaryFailure,
|
|
39
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const REMOTE_MISSING_BRAND = Symbol('openyida.schema.remoteMissing');
|
|
4
|
+
|
|
5
|
+
function createRemoteMissingError() {
|
|
6
|
+
const error = new Error('Bound remote resource is missing.');
|
|
7
|
+
error.code = 'SCHEMA_REMOTE_RESOURCE_MISSING';
|
|
8
|
+
Object.defineProperty(error, REMOTE_MISSING_BRAND, {
|
|
9
|
+
value: true,
|
|
10
|
+
});
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isRemoteMissingError(error) {
|
|
15
|
+
return !!(error && error[REMOTE_MISSING_BRAND] === true);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
createRemoteMissingError,
|
|
20
|
+
isRemoteMissingError,
|
|
21
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { schemaError } = require('./errors');
|
|
4
|
+
const { isSha256 } = require('./hash');
|
|
5
|
+
const { isRemoteMissingError } = require('./remote-missing');
|
|
6
|
+
const { createDefaultRegistry } = require('./resource-registry');
|
|
7
|
+
const { compareResourceIds, getResourceState, hashStable } = require('./state-store');
|
|
8
|
+
|
|
9
|
+
async function readObservedResources(resources, state, options = {}) {
|
|
10
|
+
const registry = options.registry || createDefaultRegistry();
|
|
11
|
+
const context = Object.assign({}, options.context || {}, {
|
|
12
|
+
services: options.services || options.context && options.context.services || {},
|
|
13
|
+
state,
|
|
14
|
+
});
|
|
15
|
+
const observedResources = [];
|
|
16
|
+
const missingResources = [];
|
|
17
|
+
|
|
18
|
+
for (const resource of sortResources(resources || [])) {
|
|
19
|
+
const adapter = registry.get(resource.resourceType);
|
|
20
|
+
assertObservedAdapter(adapter);
|
|
21
|
+
const stateResource = getResourceState(state, resource.resourceType, resource.key);
|
|
22
|
+
if (!stateResource) {
|
|
23
|
+
throw schemaError('SCHEMA_STATE_RESOURCE_BINDING_MISSING', 'State does not contain bindings for resource.', {
|
|
24
|
+
details: {
|
|
25
|
+
resourceType: resource.resourceType,
|
|
26
|
+
key: resource.key,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (stateResource.adapterVersion !== adapter.adapterVersion) {
|
|
31
|
+
throw schemaError('SCHEMA_STATE_ADAPTER_VERSION_UNSUPPORTED', 'State resource adapterVersion is not supported by the current adapter.', {
|
|
32
|
+
details: {
|
|
33
|
+
resourceType: resource.resourceType,
|
|
34
|
+
key: resource.key,
|
|
35
|
+
stateAdapterVersion: stateResource.adapterVersion,
|
|
36
|
+
adapterVersion: adapter.adapterVersion,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const adapterContext = Object.assign({}, context, {
|
|
42
|
+
resource,
|
|
43
|
+
stateResource,
|
|
44
|
+
});
|
|
45
|
+
assertDispatchBoundary(options);
|
|
46
|
+
const observed = await readObservedWithStableError(adapter, stateResource, adapterContext, resource);
|
|
47
|
+
assertDispatchBoundary(options);
|
|
48
|
+
if (isRemoteMissingError(observed)) {
|
|
49
|
+
missingResources.push({
|
|
50
|
+
resourceType: resource.resourceType,
|
|
51
|
+
key: resource.key,
|
|
52
|
+
});
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const projection = projectObservedWithStableError(adapter, observed, stateResource, adapterContext, resource);
|
|
56
|
+
const managed = projection.managed || {};
|
|
57
|
+
const observedManagedHash = projection.observedManagedHash || hashObservedManaged({
|
|
58
|
+
resourceType: resource.resourceType,
|
|
59
|
+
key: resource.key,
|
|
60
|
+
adapterVersion: adapter.adapterVersion,
|
|
61
|
+
managed,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const observedResource = {
|
|
65
|
+
resourceType: resource.resourceType,
|
|
66
|
+
key: resource.key,
|
|
67
|
+
adapterVersion: adapter.adapterVersion,
|
|
68
|
+
managed,
|
|
69
|
+
observedManagedHash,
|
|
70
|
+
bindings: projection.bindings || stateResource.bindings || {},
|
|
71
|
+
};
|
|
72
|
+
if (projection.remoteSchemaHash !== undefined) {
|
|
73
|
+
if (!isSha256(projection.remoteSchemaHash)) {
|
|
74
|
+
throw schemaError('SCHEMA_INTERNAL_ERROR', 'Resource adapter returned an invalid remote Schema hash.');
|
|
75
|
+
}
|
|
76
|
+
observedResource.remoteSchemaHash = projection.remoteSchemaHash;
|
|
77
|
+
}
|
|
78
|
+
copyPlannerObservation(projection, observedResource);
|
|
79
|
+
observedResources.push(observedResource);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
missingResources,
|
|
84
|
+
resources: observedResources,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function assertDispatchBoundary(options) {
|
|
89
|
+
if (typeof options.assertDispatchBoundary === 'function') {
|
|
90
|
+
options.assertDispatchBoundary();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function copyPlannerObservation(projection, target) {
|
|
95
|
+
for (const property of ['observedIdentityHash', 'observedDraftHash']) {
|
|
96
|
+
if (projection[property] === undefined) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (!isSha256(projection[property])) {
|
|
100
|
+
throw schemaError('SCHEMA_INTERNAL_ERROR', 'Resource adapter returned an invalid observed identity hash.');
|
|
101
|
+
}
|
|
102
|
+
target[property] = projection[property];
|
|
103
|
+
}
|
|
104
|
+
if (projection.observedIdentityMatchesBindings !== undefined) {
|
|
105
|
+
if (typeof projection.observedIdentityMatchesBindings !== 'boolean') {
|
|
106
|
+
throw schemaError('SCHEMA_INTERNAL_ERROR', 'Resource adapter returned an invalid observed identity comparison.');
|
|
107
|
+
}
|
|
108
|
+
target.observedIdentityMatchesBindings = projection.observedIdentityMatchesBindings;
|
|
109
|
+
}
|
|
110
|
+
if (projection.observedDraftCount !== undefined) {
|
|
111
|
+
if (!Number.isInteger(projection.observedDraftCount) || projection.observedDraftCount < 0) {
|
|
112
|
+
throw schemaError('SCHEMA_INTERNAL_ERROR', 'Resource adapter returned an invalid observed draft count.');
|
|
113
|
+
}
|
|
114
|
+
target.observedDraftCount = projection.observedDraftCount;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function hashObservedManaged(value) {
|
|
119
|
+
return hashStable({
|
|
120
|
+
contractVersion: 1,
|
|
121
|
+
observed: value,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function assertObservedAdapter(adapter) {
|
|
126
|
+
if (
|
|
127
|
+
!adapter ||
|
|
128
|
+
typeof adapter.readObserved !== 'function' ||
|
|
129
|
+
typeof adapter.projectObserved !== 'function'
|
|
130
|
+
) {
|
|
131
|
+
throw schemaError('SCHEMA_RESOURCE_TYPE_UNSUPPORTED', 'Resource adapter does not support observed reads.', {
|
|
132
|
+
details: { resourceType: adapter && adapter.resourceType },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function readObservedWithStableError(adapter, stateResource, context, resource) {
|
|
138
|
+
try {
|
|
139
|
+
return await adapter.readObserved(stateResource, context);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (isRemoteMissingError(error)) {
|
|
142
|
+
return error;
|
|
143
|
+
}
|
|
144
|
+
if (error && error.code === 'SCHEMA_REMOTE_RESOURCE_MISSING') {
|
|
145
|
+
throwRemoteReadFailed(resource);
|
|
146
|
+
}
|
|
147
|
+
if (error && error.code && String(error.code).startsWith('SCHEMA_')) {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
throwRemoteReadFailed(resource, error && error.code);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function throwRemoteReadFailed(resource, adapterCode) {
|
|
155
|
+
throw schemaError('SCHEMA_REMOTE_READ_FAILED', 'Remote observed read failed.', {
|
|
156
|
+
details: {
|
|
157
|
+
resourceType: resource.resourceType,
|
|
158
|
+
key: resource.key,
|
|
159
|
+
adapterCode,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function projectObservedWithStableError(adapter, observed, stateResource, context, resource) {
|
|
165
|
+
try {
|
|
166
|
+
return adapter.projectObserved(observed, stateResource, context);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (error && error.code && String(error.code).startsWith('SCHEMA_')) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
throw schemaError('SCHEMA_REMOTE_PROJECT_FAILED', 'Remote observed projection failed.', {
|
|
172
|
+
details: {
|
|
173
|
+
resourceType: resource.resourceType,
|
|
174
|
+
key: resource.key,
|
|
175
|
+
adapterCode: error && error.code,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function sortResources(resources) {
|
|
182
|
+
return resources.slice().sort(compareResourceIds);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = {
|
|
186
|
+
hashObservedManaged,
|
|
187
|
+
isRemoteMissingError,
|
|
188
|
+
readObservedResources,
|
|
189
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { schemaError } = require('./errors');
|
|
4
|
+
const { sortStrings } = require('./sort');
|
|
5
|
+
|
|
6
|
+
const OPTIONAL_ADAPTER_METHODS = Object.freeze([
|
|
7
|
+
'buildBindings',
|
|
8
|
+
'classifyObservedConflict',
|
|
9
|
+
'classifyUpdate',
|
|
10
|
+
'create',
|
|
11
|
+
'preflightApply',
|
|
12
|
+
'prepareComparison',
|
|
13
|
+
'prepareOperation',
|
|
14
|
+
'projectObserved',
|
|
15
|
+
'projectOperationResult',
|
|
16
|
+
'readObserved',
|
|
17
|
+
'reconcileStaged',
|
|
18
|
+
'resumeCreate',
|
|
19
|
+
'advanceStagedCheckpoint',
|
|
20
|
+
'isStagedCheckpointRemoteComplete',
|
|
21
|
+
'update',
|
|
22
|
+
'validateStageCheckpoint',
|
|
23
|
+
'validateStateResource',
|
|
24
|
+
'verify',
|
|
25
|
+
]);
|
|
26
|
+
const STAGED_ADAPTER_METHODS = Object.freeze([
|
|
27
|
+
'advanceStagedCheckpoint',
|
|
28
|
+
'isStagedCheckpointRemoteComplete',
|
|
29
|
+
'reconcileStaged',
|
|
30
|
+
'validateStageCheckpoint',
|
|
31
|
+
]);
|
|
32
|
+
const REQUIRED_ADAPTER_METHODS = Object.freeze(['normalize', 'validate']);
|
|
33
|
+
const ADAPTER_METADATA = Object.freeze(['adapterVersion', 'resourceType']);
|
|
34
|
+
const ADAPTER_CONTRACT_KEYS = new Set([
|
|
35
|
+
...ADAPTER_METADATA,
|
|
36
|
+
...REQUIRED_ADAPTER_METHODS,
|
|
37
|
+
...OPTIONAL_ADAPTER_METHODS,
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
class ResourceRegistry {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.adapters = new Map();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
register(adapter) {
|
|
46
|
+
const contract = createAdapterContract(adapter);
|
|
47
|
+
const stagedMethodCount = STAGED_ADAPTER_METHODS.filter(method => typeof contract[method] === 'function').length;
|
|
48
|
+
if (stagedMethodCount > 0 && contract.resourceType !== 'process') {
|
|
49
|
+
throw new Error('Resource adapter staged methods are reserved for process: ' + contract.resourceType);
|
|
50
|
+
}
|
|
51
|
+
if (stagedMethodCount > 0 && stagedMethodCount !== STAGED_ADAPTER_METHODS.length) {
|
|
52
|
+
throw new Error('Process staged adapter must implement the complete staged method contract: ' + contract.resourceType);
|
|
53
|
+
}
|
|
54
|
+
if (this.adapters.has(contract.resourceType)) {
|
|
55
|
+
throw new Error('Duplicate resource adapter: ' + contract.resourceType);
|
|
56
|
+
}
|
|
57
|
+
this.adapters.set(contract.resourceType, contract);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get(resourceType) {
|
|
62
|
+
const adapter = this.adapters.get(resourceType);
|
|
63
|
+
if (!adapter) {
|
|
64
|
+
throw schemaError('SCHEMA_RESOURCE_TYPE_UNSUPPORTED', 'Resource type is not supported by this validator.', {
|
|
65
|
+
details: { resourceType },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return adapter;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
listTypes() {
|
|
72
|
+
return sortStrings(this.adapters.keys());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function createAdapterContract(adapter) {
|
|
77
|
+
if (!adapter || typeof adapter !== 'object' || Array.isArray(adapter)) {
|
|
78
|
+
throw new Error('Resource adapter must be a plain contract object');
|
|
79
|
+
}
|
|
80
|
+
const prototype = Object.getPrototypeOf(adapter);
|
|
81
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
82
|
+
throw new Error('Resource adapter must not inherit contract properties');
|
|
83
|
+
}
|
|
84
|
+
const descriptors = Object.getOwnPropertyDescriptors(adapter);
|
|
85
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
86
|
+
if (typeof key !== 'string' || key === 'capabilities' || !ADAPTER_CONTRACT_KEYS.has(key)) {
|
|
87
|
+
throw new Error('Resource adapter declares an unknown contract property: ' + String(key));
|
|
88
|
+
}
|
|
89
|
+
const descriptor = descriptors[key];
|
|
90
|
+
if (!Object.prototype.hasOwnProperty.call(descriptor, 'value')) {
|
|
91
|
+
throw new Error('Resource adapter contract properties must be data properties: ' + key);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const resourceType = valueFromDescriptor(descriptors, 'resourceType');
|
|
96
|
+
const adapterVersion = valueFromDescriptor(descriptors, 'adapterVersion');
|
|
97
|
+
if (typeof resourceType !== 'string' || !resourceType) {
|
|
98
|
+
throw new Error('Resource adapter must declare resourceType');
|
|
99
|
+
}
|
|
100
|
+
if (!Number.isInteger(adapterVersion) || adapterVersion < 1) {
|
|
101
|
+
throw new Error('Resource adapter must declare a positive integer adapterVersion: ' + resourceType);
|
|
102
|
+
}
|
|
103
|
+
for (const method of REQUIRED_ADAPTER_METHODS) {
|
|
104
|
+
if (typeof valueFromDescriptor(descriptors, method) !== 'function') {
|
|
105
|
+
throw new Error('Resource adapter must implement normalize and validate: ' + resourceType);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const method of OPTIONAL_ADAPTER_METHODS) {
|
|
109
|
+
if (descriptors[method] && typeof descriptors[method].value !== 'function') {
|
|
110
|
+
throw new Error(`Resource adapter method must be a function ${method}: ` + resourceType);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const contract = {};
|
|
115
|
+
for (const key of ADAPTER_METADATA) {
|
|
116
|
+
contract[key] = descriptors[key].value;
|
|
117
|
+
}
|
|
118
|
+
for (const method of [...REQUIRED_ADAPTER_METHODS, ...OPTIONAL_ADAPTER_METHODS]) {
|
|
119
|
+
if (descriptors[method]) {
|
|
120
|
+
contract[method] = descriptors[method].value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return Object.freeze(contract);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function valueFromDescriptor(descriptors, property) {
|
|
127
|
+
const descriptor = descriptors[property];
|
|
128
|
+
return descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value')
|
|
129
|
+
? descriptor.value
|
|
130
|
+
: undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function isStagedAdapter(adapter) {
|
|
134
|
+
return !!(
|
|
135
|
+
adapter &&
|
|
136
|
+
adapter.resourceType === 'process' &&
|
|
137
|
+
STAGED_ADAPTER_METHODS.every(method => typeof adapter[method] === 'function')
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function createDefaultRegistry() {
|
|
142
|
+
const { appAdapter } = require('./adapters/app-adapter');
|
|
143
|
+
const { formAdapter } = require('./adapters/form-adapter');
|
|
144
|
+
const { pageAdapter } = require('./adapters/page-adapter');
|
|
145
|
+
const { processAdapter } = require('./adapters/process-adapter');
|
|
146
|
+
return new ResourceRegistry()
|
|
147
|
+
.register(appAdapter)
|
|
148
|
+
.register(formAdapter)
|
|
149
|
+
.register(pageAdapter)
|
|
150
|
+
.register(processAdapter);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
ResourceRegistry,
|
|
155
|
+
createDefaultRegistry,
|
|
156
|
+
isStagedAdapter,
|
|
157
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { schemaError } = require('./errors');
|
|
4
|
+
|
|
5
|
+
const STALE_SCHEMA_MESSAGE = '页面已变更,请更新后再修改并重新保存';
|
|
6
|
+
const SERVER_REVISION_CONFLICTS = new WeakSet();
|
|
7
|
+
|
|
8
|
+
function getSchemaServerRevision(value) {
|
|
9
|
+
const schema = normalizeSchemaContent(value);
|
|
10
|
+
if (!schema) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const revision = schema.gmtModified;
|
|
14
|
+
return Number.isFinite(revision) && revision >= 0 ? revision : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function requireSchemaServerRevision(value, createError) {
|
|
18
|
+
const revision = getSchemaServerRevision(value);
|
|
19
|
+
if (revision !== undefined) {
|
|
20
|
+
return revision;
|
|
21
|
+
}
|
|
22
|
+
if (typeof createError === 'function') {
|
|
23
|
+
throw createError();
|
|
24
|
+
}
|
|
25
|
+
throw schemaError('SCHEMA_REMOTE_READ_FAILED', 'Remote Schema revision is missing or invalid.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isSaveFormSchemaRevisionConflict(result) {
|
|
29
|
+
if (!result || typeof result !== 'object' || result.success !== false) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (String(result.errorCode || '') !== '500') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return result.errorMsg === STALE_SCHEMA_MESSAGE || result.throwable === STALE_SCHEMA_MESSAGE;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createServerRevisionConflict(resourceType) {
|
|
39
|
+
const error = schemaError('SCHEMA_APPLY_JIT_CONFLICT', 'Resource changed after the JIT read.', {
|
|
40
|
+
details: { resourceType },
|
|
41
|
+
});
|
|
42
|
+
SERVER_REVISION_CONFLICTS.add(error);
|
|
43
|
+
return error;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isServerRevisionConflict(error) {
|
|
47
|
+
return !!error && SERVER_REVISION_CONFLICTS.has(error);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeSchemaContent(value) {
|
|
51
|
+
let schema = value && Object.prototype.hasOwnProperty.call(value, 'content')
|
|
52
|
+
? value.content
|
|
53
|
+
: value;
|
|
54
|
+
if (typeof schema === 'string') {
|
|
55
|
+
try {
|
|
56
|
+
schema = JSON.parse(schema);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return schema && typeof schema === 'object' && !Array.isArray(schema) ? schema : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = Object.freeze({
|
|
65
|
+
STALE_SCHEMA_MESSAGE,
|
|
66
|
+
createServerRevisionConflict,
|
|
67
|
+
getSchemaServerRevision,
|
|
68
|
+
isSaveFormSchemaRevisionConflict,
|
|
69
|
+
isServerRevisionConflict,
|
|
70
|
+
requireSchemaServerRevision,
|
|
71
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function compareCodePoints(left, right) {
|
|
4
|
+
const leftString = String(left);
|
|
5
|
+
const rightString = String(right);
|
|
6
|
+
const leftPoints = Array.from(leftString);
|
|
7
|
+
const rightPoints = Array.from(rightString);
|
|
8
|
+
const length = Math.min(leftPoints.length, rightPoints.length);
|
|
9
|
+
|
|
10
|
+
for (let index = 0; index < length; index++) {
|
|
11
|
+
const leftCode = leftPoints[index].codePointAt(0);
|
|
12
|
+
const rightCode = rightPoints[index].codePointAt(0);
|
|
13
|
+
if (leftCode !== rightCode) {
|
|
14
|
+
return leftCode - rightCode;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return leftPoints.length - rightPoints.length;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function sortStrings(values) {
|
|
22
|
+
return [...values].sort(compareCodePoints);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
compareCodePoints,
|
|
27
|
+
sortStrings,
|
|
28
|
+
};
|