@kubohiroya/turbowarp-extension-data-editor 0.1.0 → 0.2.1
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 +30 -6
- package/dist/build-info.json +7 -2
- package/dist/descendants.js +11 -2
- package/dist/fixed-input-branches.d.ts +12 -0
- package/dist/fixed-input-branches.js +180 -0
- package/dist/fixed-input-editor.d.ts +39 -0
- package/dist/fixed-input-editor.js +538 -0
- package/dist/fixed-input-schema.d.ts +15 -0
- package/dist/fixed-input-schema.js +21 -0
- package/dist/fixed-input-snapshot.d.ts +7 -0
- package/dist/fixed-input-snapshot.js +180 -0
- package/dist/fixed-input-types.d.ts +138 -0
- package/dist/fixed-input-types.js +23 -0
- package/dist/fixed-input-view.d.ts +14 -0
- package/dist/fixed-input-view.js +168 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4 -0
- package/dist/json-value.d.ts +4 -0
- package/dist/json-value.js +12 -0
- package/dist/prepared-save.d.ts +5 -0
- package/dist/prepared-save.js +47 -0
- package/dist/prepared-types.d.ts +13 -0
- package/dist/prepared-types.js +1 -0
- package/dist/react.css +42 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.js +10 -1
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js +98 -48
- package/dist/rjsf.d.ts +48 -1
- package/dist/rjsf.js +92 -8
- package/dist/schema-compose.d.ts +6 -0
- package/dist/schema-compose.js +103 -0
- package/dist/schema-engine.d.ts +18 -0
- package/dist/schema-engine.js +298 -0
- package/dist/schema-json.d.ts +10 -0
- package/dist/schema-json.js +68 -0
- package/dist/schema-types.d.ts +161 -0
- package/dist/schema-types.js +2 -0
- package/dist/schema-view.d.ts +7 -0
- package/dist/schema-view.js +102 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +87 -0
- package/dist/session.d.ts +3 -1
- package/dist/session.js +220 -9
- package/dist/source-document.d.ts +3 -0
- package/dist/source-document.js +415 -0
- package/dist/source-editor.d.ts +13 -0
- package/dist/source-editor.js +235 -0
- package/dist/source-schema-hints.d.ts +3 -0
- package/dist/source-schema-hints.js +13 -0
- package/dist/source-types.d.ts +47 -0
- package/dist/source-types.js +1 -0
- package/dist/source-view.d.ts +3 -0
- package/dist/source-view.js +318 -0
- package/dist/turbowarp/bridge.d.ts +2 -1
- package/dist/turbowarp/bridge.js +34 -5
- package/dist/turbowarp/fixed-fields.d.ts +82 -0
- package/dist/turbowarp/fixed-fields.js +311 -0
- package/dist/turbowarp/fixed-transaction.d.ts +18 -0
- package/dist/turbowarp/fixed-transaction.js +460 -0
- package/dist/turbowarp/host.d.ts +3 -8
- package/dist/turbowarp/host.js +68 -1
- package/dist/turbowarp.d.ts +4 -0
- package/dist/turbowarp.js +1 -1
- package/dist/types.d.ts +7 -4
- package/dist/view.js +4 -0
- package/dist/webpack4/index.js +24 -5
- package/dist/webpack4/metafile.json +9886 -8710
- package/dist/webpack4/react.js +11 -2
- package/dist/webpack4/react.js.map +2 -2
- package/dist/webpack4/rjsf.js +15205 -20951
- package/dist/webpack4/rjsf.js.map +4 -4
- package/dist/webpack4/schema.js +20 -0
- package/dist/webpack4/shared-75QIWLZY.js +8375 -0
- package/dist/webpack4/shared-75QIWLZY.js.map +7 -0
- package/dist/webpack4/shared-BKTRZUZH.js +42 -0
- package/dist/webpack4/{shared-KIFRVTSM.js.map → shared-BKTRZUZH.js.map} +1 -1
- package/dist/webpack4/shared-FYUILHED.js +1 -0
- package/dist/webpack4/shared-FYUILHED.js.map +7 -0
- package/dist/webpack4/shared-GQLPPRT2.js +210 -0
- package/dist/webpack4/shared-GQLPPRT2.js.map +7 -0
- package/dist/webpack4/shared-P2JBRZFP.js +2206 -0
- package/dist/webpack4/shared-P2JBRZFP.js.map +7 -0
- package/dist/webpack4/{shared-KIFRVTSM.js → shared-RN7GOT65.js} +2 -41
- package/dist/webpack4/shared-RN7GOT65.js.map +7 -0
- package/dist/webpack4/turbowarp.js +912 -9
- package/dist/webpack4/turbowarp.js.map +4 -4
- package/docs/README.md +1 -0
- package/docs/block-editor-e2e.md +98 -16
- package/docs/ci-success-gate.md +84 -0
- package/docs/editor/issue-12-public-validation/README.md +61 -0
- package/docs/editor/issue-12-public-validation/dark-invalid.png +0 -0
- package/docs/editor/issue-12-public-validation/dark-styles.json +173 -0
- package/docs/editor/issue-12-public-validation/dark-valid.png +0 -0
- package/docs/editor/issue-12-public-validation/deployment-target.json +5 -0
- package/docs/editor/issue-12-public-validation/deployment-wait.json +7 -0
- package/docs/editor/issue-12-public-validation/light-invalid.png +0 -0
- package/docs/editor/issue-12-public-validation/light-styles.json +173 -0
- package/docs/editor/issue-12-public-validation/light-valid.png +0 -0
- package/docs/editor/issue-12-public-validation/provenance.json +88 -0
- package/docs/editor/issue-12-public-validation/results.json +677 -0
- package/docs/editor/issue-12-public-validation/summary.json +206 -0
- package/docs/fixed-input-contracts.md +77 -0
- package/docs/github-pages.md +48 -41
- package/docs/package-distribution.md +2 -2
- package/docs/schema-stage1-browser.json +10 -0
- package/docs/schema-stage1-contracts.json +242 -0
- package/docs/schema-stage1-dependencies.json +202 -0
- package/docs/schema-stage1-git-artifact-20261018.ja.md +17 -0
- package/docs/schema-stage1-profile-compat.json +30 -0
- package/docs/schema-stage1-review-fixes-20261018.ja.md +36 -0
- package/docs/schema-stage1-verification.json +541 -0
- package/docs/schema-stage1.ja.md +80 -0
- package/docs/schema.md +43 -0
- package/docs/source-forms.ja.md +19 -0
- package/docs/stage-2-publication.ja.md +35 -0
- package/docs/template-fixture-separation.ja.md +43 -0
- package/package.json +18 -3
- package/webpack4.cjs +1 -1
- package/dist/webpack4/shared-CQB3NG2A.js +0 -1
- package/dist/webpack4/shared-WHOAW2VF.js +0 -957
- package/dist/webpack4/shared-WHOAW2VF.js.map +0 -7
- /package/dist/webpack4/{shared-CQB3NG2A.js.map → schema.js.map} +0 -0
package/dist/react.css
CHANGED
|
@@ -181,3 +181,45 @@
|
|
|
181
181
|
outline: 2px solid currentColor;
|
|
182
182
|
outline-offset: 2px;
|
|
183
183
|
}
|
|
184
|
+
|
|
185
|
+
.body :global(.block-editor-fixed-inputs) > fieldset {
|
|
186
|
+
margin: 0 0 1rem;
|
|
187
|
+
padding: 0.75rem;
|
|
188
|
+
border: 1px solid var(--block-editor-border, #ccc);
|
|
189
|
+
border-radius: var(--block-editor-radius, 0.5rem);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.body :global(.block-editor-fixed-inputs) output {
|
|
193
|
+
white-space: pre-wrap;
|
|
194
|
+
overflow-wrap: anywhere;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.body :global(.block-editor-fixed-color) {
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
gap: 0.5rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.body :global(.block-editor-fixed-color) input[type="text"] {
|
|
204
|
+
flex: 1;
|
|
205
|
+
min-width: 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.body :global(.block-editor-fixed-color) input[type="color"] {
|
|
209
|
+
width: 3rem;
|
|
210
|
+
min-width: 3rem;
|
|
211
|
+
height: 2.75rem;
|
|
212
|
+
padding: 0.25rem;
|
|
213
|
+
box-sizing: border-box;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.body :global(.block-editor-fixed-readonly),
|
|
217
|
+
.body :global(.block-editor-fixed-unverified) {
|
|
218
|
+
margin-bottom: 0;
|
|
219
|
+
font-size: 0.875em;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.body :global(.block-editor-fixed-error) {
|
|
223
|
+
color: var(--block-editor-error-text, #a61b1b);
|
|
224
|
+
margin-bottom: 0;
|
|
225
|
+
}
|
package/dist/react.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface DialogSessionState {
|
|
|
11
11
|
}
|
|
12
12
|
export interface DialogSession {
|
|
13
13
|
getState(): DialogSessionState;
|
|
14
|
+
getFixedInputView?(): unknown;
|
|
14
15
|
subscribe(listener: () => void): () => void;
|
|
15
16
|
mount?(container: HTMLElement): () => void;
|
|
16
17
|
getNodes?(): readonly DescendantNodeDescriptor[];
|
|
@@ -29,6 +30,8 @@ export interface EditorDialogMessages {
|
|
|
29
30
|
readonly conflict: string;
|
|
30
31
|
readonly failed: string;
|
|
31
32
|
readonly structuralNode: string;
|
|
33
|
+
/** Fixed forms retain technical diagnostics in the API while showing a localized summary. */
|
|
34
|
+
readonly invalidInput?: string;
|
|
32
35
|
}
|
|
33
36
|
export interface EditorNodeAppearance {
|
|
34
37
|
readonly name?: string;
|
package/dist/react.js
CHANGED
|
@@ -206,6 +206,15 @@ export class BlockEditorDialog extends React.Component {
|
|
|
206
206
|
const { styles, messages, ModalComponent, session } = this.props;
|
|
207
207
|
const saving = this.state.status === 'saving';
|
|
208
208
|
const hierarchy = typeof session.getNodes === 'function';
|
|
209
|
+
let fixed = false;
|
|
210
|
+
if (session.getFixedInputView && (this.state.status === 'editing' || saving)) {
|
|
211
|
+
try {
|
|
212
|
+
fixed = Boolean(session.getFixedInputView());
|
|
213
|
+
}
|
|
214
|
+
catch (_a) {
|
|
215
|
+
fixed = true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
209
218
|
return React.createElement(ModalComponent, { className: `${styles.modalContent || ''} ${hierarchy ? styles.hierarchyModal : ''} ${this.props.className || ''}`, contentLabel: messages.title, id: "blockEditorModal", onRequestClose: this.handleClose },
|
|
210
219
|
React.createElement("div", { "aria-busy": saving, className: styles.body, onCompositionStart: () => { this.composing = true; }, onCompositionEnd: () => { this.composing = false; }, onKeyDownCapture: event => {
|
|
211
220
|
if (this.composing || event.nativeEvent.isComposing || event.keyCode === 229)
|
|
@@ -215,7 +224,7 @@ export class BlockEditorDialog extends React.Component {
|
|
|
215
224
|
React.createElement("div", { ref: this.handleBody }, hierarchy ? React.createElement(DescendantTree, { session: session, styles: styles, messages: messages, disabled: saving, nodeAppearances: this.props.nodeAppearances, nodeLabels: this.props.nodeLabels, onError: this.handleMountError, ref: tree => { this.tree = tree; } }) : null),
|
|
216
225
|
React.createElement("div", { "aria-live": "polite", className: styles.errors, role: "status" },
|
|
217
226
|
saving ? messages.saving : null,
|
|
218
|
-
this.state.issues.map((issue, index) => {
|
|
227
|
+
fixed && this.state.issues.length ? React.createElement("p", null, messages.invalidInput || messages.failed) : this.state.issues.map((issue, index) => {
|
|
219
228
|
var _a;
|
|
220
229
|
return hierarchy && 'reference' in issue ?
|
|
221
230
|
React.createElement("button", { className: styles.nodeError, "data-block-editor-issue": index, disabled: saving, key: index, type: "button", onClick: () => { var _a; return (_a = this.tree) === null || _a === void 0 ? void 0 : _a.reveal(issue); } },
|
package/dist/registry.d.ts
CHANGED
|
@@ -2,10 +2,16 @@ import { EditorSession } from './session.js';
|
|
|
2
2
|
import { DescendantSession } from './descendants.js';
|
|
3
3
|
import type { DescendantLimits } from './descendant-types.js';
|
|
4
4
|
import type { BlockEditorHost, BlockReference, EditorDefinition, EditorDescriptor } from './types.js';
|
|
5
|
+
import type { PreparedEditorDefinition } from './prepared-types.js';
|
|
6
|
+
import type { FixedInputDeclaration, FixedInputEditorDefinition } from './fixed-input-types.js';
|
|
5
7
|
export declare class EditorRegistry {
|
|
6
8
|
#private;
|
|
7
9
|
register(definition: EditorDefinition): () => void;
|
|
10
|
+
registerPrepared(definition: PreparedEditorDefinition): () => void;
|
|
11
|
+
registerFixed<C = null>(definition: FixedInputEditorDefinition<C>): () => void;
|
|
8
12
|
resolve(opcode: string): EditorDescriptor | undefined;
|
|
13
|
+
/** Stable, callback-free authority for this exact registration, never caller metadata. */
|
|
14
|
+
resolveFixed(opcode: string): FixedInputDeclaration | undefined;
|
|
9
15
|
open(reference: BlockReference, host: BlockEditorHost): EditorSession | undefined;
|
|
10
16
|
openDescendants(reference: BlockReference, host: BlockEditorHost, limits?: DescendantLimits): DescendantSession | undefined;
|
|
11
17
|
}
|
package/dist/registry.js
CHANGED
|
@@ -3,64 +3,40 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
3
3
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
|
-
var _EditorRegistry_instances, _EditorRegistry_editors, _EditorRegistry_opcodes, _EditorRegistry_changes, _EditorRegistry_notifyChanges;
|
|
6
|
+
var _EditorRegistry_instances, _EditorRegistry_editors, _EditorRegistry_opcodes, _EditorRegistry_fixedDeclarations, _EditorRegistry_changes, _EditorRegistry_register, _EditorRegistry_notifyChanges;
|
|
7
7
|
import { captureReference, captureSnapshot } from './data.js';
|
|
8
8
|
import { EditorSession } from './session.js';
|
|
9
9
|
import { DescendantSession } from './descendants.js';
|
|
10
10
|
import { supportsDescendantEditing } from './descendant-types.js';
|
|
11
11
|
import { captureSubtree, descendantLimits } from './subtree.js';
|
|
12
12
|
import { EDITOR_API_VERSION } from './types.js';
|
|
13
|
+
import { isPreparedEditor } from './prepared-save.js';
|
|
14
|
+
import { isFixedInputEditor, captureFixedInputEditorDefinition } from './fixed-input-editor.js';
|
|
15
|
+
import { captureFixedInputDeclaration, captureFixedSnapshot } from './fixed-input-snapshot.js';
|
|
16
|
+
import { supportsFixedInputEditing } from './fixed-input-types.js';
|
|
13
17
|
export class EditorRegistry {
|
|
14
18
|
constructor() {
|
|
15
19
|
_EditorRegistry_instances.add(this);
|
|
16
20
|
_EditorRegistry_editors.set(this, new Map());
|
|
17
21
|
_EditorRegistry_opcodes.set(this, new Map());
|
|
22
|
+
_EditorRegistry_fixedDeclarations.set(this, new Map());
|
|
18
23
|
_EditorRegistry_changes.set(this, new Set());
|
|
19
24
|
}
|
|
20
25
|
register(definition) {
|
|
21
|
-
if (definition
|
|
22
|
-
throw new TypeError('
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
!opcode.startsWith(`${definition.extensionId}_`) || opcode === `${definition.extensionId}_`)) {
|
|
31
|
-
throw new TypeError('Expected distinct full opcodes belonging to the extension.');
|
|
32
|
-
}
|
|
33
|
-
if (!Array.isArray(definition.editableArguments) ||
|
|
34
|
-
new Set(definition.editableArguments).size !== definition.editableArguments.length ||
|
|
35
|
-
definition.editableArguments.some(name => typeof name !== 'string' || !name)) {
|
|
36
|
-
throw new TypeError('Expected distinct editable argument names.');
|
|
37
|
-
}
|
|
38
|
-
if (typeof definition.read !== 'function' || typeof definition.validate !== 'function' ||
|
|
39
|
-
typeof definition.createPatch !== 'function' ||
|
|
40
|
-
(definition.mount !== undefined && typeof definition.mount !== 'function')) {
|
|
41
|
-
throw new TypeError('Invalid editor callbacks.');
|
|
42
|
-
}
|
|
43
|
-
const key = JSON.stringify([definition.extensionId, definition.editorId]);
|
|
44
|
-
if (__classPrivateFieldGet(this, _EditorRegistry_editors, "f").has(key) || definition.opcodes.some(opcode => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").has(opcode))) {
|
|
45
|
-
throw new Error('The editor ID or an opcode is already registered.');
|
|
26
|
+
if (isFixedInputEditor(definition))
|
|
27
|
+
throw new TypeError('Fixed input editors require registerFixed().');
|
|
28
|
+
if (isPreparedEditor(definition))
|
|
29
|
+
throw new TypeError('Prepared editors require registerPrepared().');
|
|
30
|
+
return __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_register).call(this, definition);
|
|
31
|
+
}
|
|
32
|
+
registerPrepared(definition) {
|
|
33
|
+
if (!isPreparedEditor(definition) || typeof definition.prepareSave !== 'function') {
|
|
34
|
+
throw new TypeError('Expected a prepared source editor.');
|
|
46
35
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
__classPrivateFieldGet(this, _EditorRegistry_editors, "f").set(key, entry);
|
|
53
|
-
for (const opcode of entry.opcodes)
|
|
54
|
-
__classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").set(opcode, entry);
|
|
55
|
-
__classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_notifyChanges).call(this);
|
|
56
|
-
return () => {
|
|
57
|
-
if (__classPrivateFieldGet(this, _EditorRegistry_editors, "f").get(key) !== entry)
|
|
58
|
-
return;
|
|
59
|
-
__classPrivateFieldGet(this, _EditorRegistry_editors, "f").delete(key);
|
|
60
|
-
for (const opcode of entry.opcodes)
|
|
61
|
-
__classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").delete(opcode);
|
|
62
|
-
__classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_notifyChanges).call(this);
|
|
63
|
-
};
|
|
36
|
+
return __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_register).call(this, definition);
|
|
37
|
+
}
|
|
38
|
+
registerFixed(definition) {
|
|
39
|
+
return __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_register).call(this, captureFixedInputEditorDefinition(definition));
|
|
64
40
|
}
|
|
65
41
|
resolve(opcode) {
|
|
66
42
|
const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode);
|
|
@@ -71,14 +47,37 @@ export class EditorRegistry {
|
|
|
71
47
|
opcodes: entry.opcodes, editableArguments: entry.editableArguments
|
|
72
48
|
});
|
|
73
49
|
}
|
|
50
|
+
/** Stable, callback-free authority for this exact registration, never caller metadata. */
|
|
51
|
+
resolveFixed(opcode) {
|
|
52
|
+
const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode);
|
|
53
|
+
return entry ? __classPrivateFieldGet(this, _EditorRegistry_fixedDeclarations, "f").get(entry) : undefined;
|
|
54
|
+
}
|
|
74
55
|
open(reference, host) {
|
|
75
56
|
const capturedReference = captureReference(reference);
|
|
76
57
|
const live = host.readBlock(capturedReference);
|
|
77
58
|
if (!live)
|
|
78
59
|
return undefined;
|
|
60
|
+
const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(live.opcode);
|
|
61
|
+
if (!entry) {
|
|
62
|
+
// Preserve legacy snapshot validation even when no editor is registered.
|
|
63
|
+
captureSnapshot(live, capturedReference);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
if (isFixedInputEditor(entry)) {
|
|
67
|
+
if (!supportsFixedInputEditing(host))
|
|
68
|
+
return undefined;
|
|
69
|
+
const fixed = host.fixedInputs.readBlock(capturedReference, entry);
|
|
70
|
+
if (!fixed || fixed.opcode !== live.opcode)
|
|
71
|
+
return undefined;
|
|
72
|
+
const snapshot = captureFixedSnapshot(fixed, capturedReference, entry);
|
|
73
|
+
return new EditorSession(entry, snapshot, host, () => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode) === entry, listener => {
|
|
74
|
+
__classPrivateFieldGet(this, _EditorRegistry_changes, "f").add(listener);
|
|
75
|
+
return () => { __classPrivateFieldGet(this, _EditorRegistry_changes, "f").delete(listener); };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
79
78
|
const snapshot = captureSnapshot(live, capturedReference);
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
return new EditorSession(entry, snapshot, host, isPreparedEditor(entry)
|
|
80
|
+
? () => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode) === entry : undefined);
|
|
82
81
|
}
|
|
83
82
|
openDescendants(reference, host, limits) {
|
|
84
83
|
if (!supportsDescendantEditing(host))
|
|
@@ -90,15 +89,66 @@ export class EditorRegistry {
|
|
|
90
89
|
if (!live)
|
|
91
90
|
return undefined;
|
|
92
91
|
const captured = captureSubtree(live, capturedReference, resolvedLimits);
|
|
93
|
-
|
|
92
|
+
const root = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(captured.snapshot.nodes[0].block.opcode);
|
|
93
|
+
if (!root || isFixedInputEditor(root))
|
|
94
94
|
return undefined;
|
|
95
|
-
return new DescendantSession(captured, port, opcode =>
|
|
95
|
+
return new DescendantSession(captured, port, opcode => {
|
|
96
|
+
const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode);
|
|
97
|
+
return entry && !isFixedInputEditor(entry) ? entry : undefined;
|
|
98
|
+
}, listener => {
|
|
96
99
|
__classPrivateFieldGet(this, _EditorRegistry_changes, "f").add(listener);
|
|
97
100
|
return () => { __classPrivateFieldGet(this, _EditorRegistry_changes, "f").delete(listener); };
|
|
98
101
|
}, resolvedLimits);
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
|
-
_EditorRegistry_editors = new WeakMap(), _EditorRegistry_opcodes = new WeakMap(), _EditorRegistry_changes = new WeakMap(), _EditorRegistry_instances = new WeakSet(),
|
|
104
|
+
_EditorRegistry_editors = new WeakMap(), _EditorRegistry_opcodes = new WeakMap(), _EditorRegistry_fixedDeclarations = new WeakMap(), _EditorRegistry_changes = new WeakMap(), _EditorRegistry_instances = new WeakSet(), _EditorRegistry_register = function _EditorRegistry_register(definition) {
|
|
105
|
+
if (definition.apiVersion !== EDITOR_API_VERSION)
|
|
106
|
+
throw new TypeError('Unsupported editor API version.');
|
|
107
|
+
if (!/^[a-z0-9]+$/i.test(definition.extensionId) ||
|
|
108
|
+
!/^[a-z0-9][a-z0-9._-]*$/i.test(definition.editorId)) {
|
|
109
|
+
throw new TypeError('Invalid extension ID or editor ID.');
|
|
110
|
+
}
|
|
111
|
+
if (!Array.isArray(definition.opcodes) || definition.opcodes.length === 0 ||
|
|
112
|
+
new Set(definition.opcodes).size !== definition.opcodes.length ||
|
|
113
|
+
definition.opcodes.some(opcode => typeof opcode !== 'string' ||
|
|
114
|
+
!opcode.startsWith(`${definition.extensionId}_`) || opcode === `${definition.extensionId}_`)) {
|
|
115
|
+
throw new TypeError('Expected distinct full opcodes belonging to the extension.');
|
|
116
|
+
}
|
|
117
|
+
if (!Array.isArray(definition.editableArguments) ||
|
|
118
|
+
new Set(definition.editableArguments).size !== definition.editableArguments.length ||
|
|
119
|
+
definition.editableArguments.some(name => typeof name !== 'string' || !name)) {
|
|
120
|
+
throw new TypeError('Expected distinct editable argument names.');
|
|
121
|
+
}
|
|
122
|
+
if (typeof definition.read !== 'function' || typeof definition.validate !== 'function' ||
|
|
123
|
+
typeof definition.createPatch !== 'function' ||
|
|
124
|
+
(definition.mount !== undefined && typeof definition.mount !== 'function')) {
|
|
125
|
+
throw new TypeError('Invalid editor callbacks.');
|
|
126
|
+
}
|
|
127
|
+
const key = JSON.stringify([definition.extensionId, definition.editorId]);
|
|
128
|
+
if (__classPrivateFieldGet(this, _EditorRegistry_editors, "f").has(key) || definition.opcodes.some(opcode => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").has(opcode))) {
|
|
129
|
+
throw new Error('The editor ID or an opcode is already registered.');
|
|
130
|
+
}
|
|
131
|
+
const entry = Object.freeze({
|
|
132
|
+
...definition,
|
|
133
|
+
opcodes: Object.freeze([...definition.opcodes]),
|
|
134
|
+
editableArguments: Object.freeze([...definition.editableArguments])
|
|
135
|
+
});
|
|
136
|
+
if (isFixedInputEditor(entry))
|
|
137
|
+
__classPrivateFieldGet(this, _EditorRegistry_fixedDeclarations, "f").set(entry, captureFixedInputDeclaration(entry));
|
|
138
|
+
__classPrivateFieldGet(this, _EditorRegistry_editors, "f").set(key, entry);
|
|
139
|
+
for (const opcode of entry.opcodes)
|
|
140
|
+
__classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").set(opcode, entry);
|
|
141
|
+
__classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_notifyChanges).call(this);
|
|
142
|
+
return () => {
|
|
143
|
+
if (__classPrivateFieldGet(this, _EditorRegistry_editors, "f").get(key) !== entry)
|
|
144
|
+
return;
|
|
145
|
+
__classPrivateFieldGet(this, _EditorRegistry_editors, "f").delete(key);
|
|
146
|
+
for (const opcode of entry.opcodes)
|
|
147
|
+
__classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").delete(opcode);
|
|
148
|
+
__classPrivateFieldGet(this, _EditorRegistry_fixedDeclarations, "f").delete(entry);
|
|
149
|
+
__classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_notifyChanges).call(this);
|
|
150
|
+
};
|
|
151
|
+
}, _EditorRegistry_notifyChanges = function _EditorRegistry_notifyChanges() {
|
|
102
152
|
for (const listener of [...__classPrivateFieldGet(this, _EditorRegistry_changes, "f")])
|
|
103
153
|
listener();
|
|
104
154
|
};
|
package/dist/rjsf.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
import type { FixedInputMessageSource } from './fixed-input-view.js';
|
|
2
|
+
export type { FixedInputViewMessages, FixedInputMessageSource } from './fixed-input-view.js';
|
|
3
|
+
import type { FixedInputEditorOptions } from './fixed-input-editor.js';
|
|
4
|
+
export { createFixedInputEditor } from './fixed-input-editor.js';
|
|
5
|
+
export type { FixedInputEditorOptions, FixedInputBinding, FixedInputBranchPolicy } from './fixed-input-editor.js';
|
|
6
|
+
export type * from './fixed-input-types.js';
|
|
7
|
+
import type { SourceEditorOptions } from './source-types.js';
|
|
8
|
+
export { createSourceEditor } from './source-editor.js';
|
|
9
|
+
export type * from './source-types.js';
|
|
10
|
+
export type * from './prepared-types.js';
|
|
1
11
|
import type { RJSFSchema } from '@rjsf/utils';
|
|
12
|
+
import type { SchemaBundle, SchemaValidationContext, SyncOwnerValidator } from './schema-types.js';
|
|
2
13
|
import type { SourceFormat } from './codec.js';
|
|
3
14
|
import { EditorRegistry } from './registry.js';
|
|
4
15
|
import { EDITOR_API_VERSION } from './types.js';
|
|
@@ -17,10 +28,31 @@ export interface SchemaEditorOptions {
|
|
|
17
28
|
/** Optional business validation, always run after schema validation when saving. */
|
|
18
29
|
validateData?(data: JsonValue, snapshot: BlockSnapshot): readonly ValidationIssue[] | Promise<readonly ValidationIssue[]>;
|
|
19
30
|
}
|
|
31
|
+
/** Explicit bundle opt-in; the provider supplies fresh immutable validation inputs per call. */
|
|
32
|
+
export interface SchemaBundleEditorOptions<C = null> extends Omit<SchemaEditorOptions, 'schema'> {
|
|
33
|
+
readonly schemaBundle: SchemaBundle;
|
|
34
|
+
readonly validationContext: () => SchemaValidationContext<C>;
|
|
35
|
+
readonly ownerValidate?: SyncOwnerValidator<C>;
|
|
36
|
+
}
|
|
37
|
+
export declare function createSchemaBundleEditor<C>(options: SchemaBundleEditorOptions<C>): EditorDefinition;
|
|
20
38
|
export interface BlockEditorRegistrationAPI {
|
|
21
39
|
readonly apiVersion: typeof EDITOR_API_VERSION;
|
|
22
40
|
register(definition: EditorDefinition | SchemaEditorOptions): () => void;
|
|
23
41
|
registerSchema(definition: SchemaEditorOptions): () => void;
|
|
42
|
+
registerSchemaBundle?<C>(definition: SchemaBundleEditorOptions<C>): () => void;
|
|
43
|
+
registerSourceEditor?<C = null>(definition: SourceEditorOptions<C>): () => void;
|
|
44
|
+
registerFixedInputEditor?<C = null>(definition: FixedInputEditorOptions<C>): () => void;
|
|
45
|
+
}
|
|
46
|
+
/** Capability guaranteed by the service factory, optional on a legacy host-supplied API. */
|
|
47
|
+
export interface SchemaBundleBlockEditorRegistrationAPI extends BlockEditorRegistrationAPI {
|
|
48
|
+
registerSchemaBundle<C>(definition: SchemaBundleEditorOptions<C>): () => void;
|
|
49
|
+
}
|
|
50
|
+
/** New prepared-source capability; existing bundle-only host implementations remain compatible. */
|
|
51
|
+
export interface SourceBlockEditorRegistrationAPI extends SchemaBundleBlockEditorRegistrationAPI {
|
|
52
|
+
registerSourceEditor<C = null>(definition: SourceEditorOptions<C>): () => void;
|
|
53
|
+
}
|
|
54
|
+
export interface FixedBlockEditorRegistrationAPI extends SourceBlockEditorRegistrationAPI {
|
|
55
|
+
registerFixedInputEditor<C = null>(definition: FixedInputEditorOptions<C>): () => void;
|
|
24
56
|
}
|
|
25
57
|
export interface BlockEditorService {
|
|
26
58
|
readonly registry: EditorRegistry;
|
|
@@ -28,6 +60,21 @@ export interface BlockEditorService {
|
|
|
28
60
|
subscribe(listener: () => void): () => void;
|
|
29
61
|
}
|
|
30
62
|
/** A host owns the service's lifetime; this module never stores VM or workspace instances. */
|
|
31
|
-
export declare function createBlockEditorService(
|
|
63
|
+
export declare function createBlockEditorService(options: {
|
|
64
|
+
readonly fixedInputs: true;
|
|
65
|
+
readonly fixedInputMessages?: FixedInputMessageSource;
|
|
66
|
+
}): BlockEditorService & {
|
|
67
|
+
readonly api: FixedBlockEditorRegistrationAPI;
|
|
68
|
+
};
|
|
69
|
+
export declare function createBlockEditorService(options?: {
|
|
70
|
+
readonly fixedInputs?: false;
|
|
71
|
+
readonly fixedInputMessages?: FixedInputMessageSource;
|
|
72
|
+
}): BlockEditorService & {
|
|
73
|
+
readonly api: SourceBlockEditorRegistrationAPI;
|
|
74
|
+
};
|
|
75
|
+
export declare function createBlockEditorService(options: {
|
|
76
|
+
readonly fixedInputs?: boolean;
|
|
77
|
+
readonly fixedInputMessages?: FixedInputMessageSource;
|
|
78
|
+
}): BlockEditorService;
|
|
32
79
|
/** Build an editor with JSON/YAML serialization, schema validation and the standard RJSF body. */
|
|
33
80
|
export declare function createSchemaEditor(options: SchemaEditorOptions): EditorDefinition;
|
package/dist/rjsf.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { createSourceEditor } from './source-editor.js';
|
|
3
|
+
import { createFixedInputEditor } from './fixed-input-editor.js';
|
|
4
|
+
import { captureFixedInputMessages, resolveFixedInputMessages, mountFixedInputView } from './fixed-input-view.js';
|
|
5
|
+
export { createFixedInputEditor } from './fixed-input-editor.js';
|
|
6
|
+
export { createSourceEditor } from './source-editor.js';
|
|
2
7
|
import * as ReactDOM from 'react-dom';
|
|
3
8
|
import * as RjsfCore from '@rjsf/core';
|
|
4
9
|
import * as RjsfUtils from '@rjsf/utils';
|
|
5
10
|
import * as RjsfAjv from '@rjsf/validator-ajv8';
|
|
11
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
12
|
+
import { compileSchemaBundle, validateSchemaInput, toValidationIssues } from './schema.js';
|
|
13
|
+
import { schemaView, fieldErrors } from './schema-view.js';
|
|
14
|
+
import { snapshotJson } from './schema-json.js';
|
|
6
15
|
import { cloneJson } from './data.js';
|
|
7
16
|
import { decodeSource, encodeSource, sameData } from './codec.js';
|
|
8
17
|
import { EditorRegistry } from './registry.js';
|
|
@@ -17,13 +26,72 @@ const Form = moduleExports(RjsfCore).default;
|
|
|
17
26
|
const { getDefaultFormState } = moduleExports(RjsfUtils);
|
|
18
27
|
const { customizeValidator } = moduleExports(RjsfAjv);
|
|
19
28
|
let nextViewId = 0;
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
export function createSchemaBundleEditor(options) {
|
|
30
|
+
const { schemaBundle, validationContext: provider, ownerValidate, apiVersion, extensionId, editorId, opcodes, argument, format, uiSchema, validateData } = options;
|
|
31
|
+
let bundle;
|
|
32
|
+
try {
|
|
33
|
+
bundle = snapshotJson(schemaBundle);
|
|
34
|
+
}
|
|
35
|
+
catch (_a) {
|
|
36
|
+
throw new TypeError('Invalid schema bundle.');
|
|
37
|
+
}
|
|
38
|
+
const result = compileSchemaBundle(bundle);
|
|
39
|
+
if (result.kind !== 'compiled')
|
|
40
|
+
throw new TypeError(`Schema bundle compilation failed: ${result.diagnostics[0].code}`);
|
|
41
|
+
const schema = schemaView(bundle);
|
|
42
|
+
const issuesFor = (data) => {
|
|
43
|
+
let context;
|
|
44
|
+
try {
|
|
45
|
+
context = provider();
|
|
46
|
+
if (context && typeof context === 'object') {
|
|
47
|
+
const prototype = Object.getPrototypeOf(context);
|
|
48
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
49
|
+
// Drain rejected native/cross-realm promises from a malformed synchronous provider.
|
|
50
|
+
try {
|
|
51
|
+
void Promise.prototype.then.call(context, () => undefined, () => undefined);
|
|
52
|
+
}
|
|
53
|
+
catch ( /* The common context guard rejects other non-JSON instances below. */_a) { /* The common context guard rejects other non-JSON instances below. */ }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (_b) {
|
|
58
|
+
return [{ message: 'Validation could not complete: owner-context-invalid.' }];
|
|
59
|
+
}
|
|
60
|
+
const validation = validateSchemaInput(result.validator, data, { context, ...(ownerValidate !== undefined ? { ownerValidate } : {}) });
|
|
61
|
+
if (validation.kind === 'valid')
|
|
62
|
+
return [];
|
|
63
|
+
if (validation.kind === 'execution-error')
|
|
64
|
+
return validation.diagnostics.map(d => ({ message: `Validation could not complete: ${d.code}.` }));
|
|
65
|
+
return toValidationIssues(validation.diagnostics);
|
|
66
|
+
};
|
|
67
|
+
return createSchemaEditorImpl({ apiVersion, extensionId, editorId, opcodes, argument, schema,
|
|
68
|
+
...(format === undefined ? {} : { format }), ...(uiSchema === undefined ? {} : { uiSchema }),
|
|
69
|
+
...(validateData === undefined ? {} : { validateData }) }, { issuesFor, dialect: result.validator.dialect });
|
|
70
|
+
}
|
|
71
|
+
export function createBlockEditorService(options) {
|
|
72
|
+
if ((options === null || options === void 0 ? void 0 : options.fixedInputs) !== undefined && typeof options.fixedInputs !== 'boolean')
|
|
73
|
+
throw new TypeError('Invalid fixed input capability option.');
|
|
74
|
+
const messageSource = options === null || options === void 0 ? void 0 : options.fixedInputMessages;
|
|
75
|
+
const fixedMessages = typeof messageSource === 'function' ? messageSource : captureFixedInputMessages(messageSource);
|
|
22
76
|
const registry = new EditorRegistry();
|
|
23
77
|
const listeners = new Set();
|
|
24
78
|
const notify = () => { for (const listener of [...listeners])
|
|
25
79
|
listener(); };
|
|
80
|
+
const fixed = (options === null || options === void 0 ? void 0 : options.fixedInputs) === true ? {
|
|
81
|
+
registerFixedInputEditor: (definition) => {
|
|
82
|
+
const fixedDefinition = createFixedInputEditor(definition);
|
|
83
|
+
const remove = registry.registerFixed({ ...fixedDefinition, mount: (container, context) => mountFixedInputView(container, context, resolveFixedInputMessages(fixedMessages)) });
|
|
84
|
+
notify();
|
|
85
|
+
let active = true;
|
|
86
|
+
return () => { if (active) {
|
|
87
|
+
active = false;
|
|
88
|
+
remove();
|
|
89
|
+
notify();
|
|
90
|
+
} };
|
|
91
|
+
}
|
|
92
|
+
} : {};
|
|
26
93
|
const api = Object.freeze({
|
|
94
|
+
...fixed,
|
|
27
95
|
apiVersion: EDITOR_API_VERSION,
|
|
28
96
|
register: (definition) => {
|
|
29
97
|
const custom = definition;
|
|
@@ -42,7 +110,18 @@ export function createBlockEditorService() {
|
|
|
42
110
|
notify();
|
|
43
111
|
};
|
|
44
112
|
},
|
|
45
|
-
registerSchema: (definition) => api.register(createSchemaEditor(definition))
|
|
113
|
+
registerSchema: (definition) => api.register(createSchemaEditor(definition)),
|
|
114
|
+
registerSchemaBundle: (definition) => api.register(createSchemaBundleEditor(definition)),
|
|
115
|
+
registerSourceEditor: (definition) => {
|
|
116
|
+
const remove = registry.registerPrepared(createSourceEditor(definition));
|
|
117
|
+
notify();
|
|
118
|
+
let active = true;
|
|
119
|
+
return () => { if (active) {
|
|
120
|
+
active = false;
|
|
121
|
+
remove();
|
|
122
|
+
notify();
|
|
123
|
+
} };
|
|
124
|
+
}
|
|
46
125
|
});
|
|
47
126
|
return { registry, api, subscribe: listener => {
|
|
48
127
|
listeners.add(listener);
|
|
@@ -51,7 +130,10 @@ export function createBlockEditorService() {
|
|
|
51
130
|
}
|
|
52
131
|
/** Build an editor with JSON/YAML serialization, schema validation and the standard RJSF body. */
|
|
53
132
|
export function createSchemaEditor(options) {
|
|
54
|
-
|
|
133
|
+
return createSchemaEditorImpl(options);
|
|
134
|
+
}
|
|
135
|
+
function createSchemaEditorImpl(options, bundle) {
|
|
136
|
+
var _a, _b;
|
|
55
137
|
if (options.apiVersion !== EDITOR_API_VERSION || typeof options.argument !== 'string' || !options.argument) {
|
|
56
138
|
throw new TypeError('A supported API version and JSON argument name are required.');
|
|
57
139
|
}
|
|
@@ -63,19 +145,20 @@ export function createSchemaEditor(options) {
|
|
|
63
145
|
const uiSchema = options.uiSchema ? cloneJson(options.uiSchema) : {};
|
|
64
146
|
const validateData = options.validateData;
|
|
65
147
|
const validator = customizeValidator({
|
|
148
|
+
...((bundle === null || bundle === void 0 ? void 0 : bundle.dialect) === '2020-12' ? { AjvClass: Ajv2020 } : {}),
|
|
66
149
|
ajvOptionsOverrides: { allErrors: true, coerceTypes: false, useDefaults: false, removeAdditional: false }
|
|
67
150
|
});
|
|
68
151
|
const compilation = validator.rawValidation(schema);
|
|
69
152
|
if (compilation.validationError) {
|
|
70
153
|
throw new TypeError(`Invalid or unsupported JSON Schema: ${compilation.validationError.message}`);
|
|
71
154
|
}
|
|
72
|
-
const issuesFor = (data) => {
|
|
155
|
+
const issuesFor = (_b = bundle === null || bundle === void 0 ? void 0 : bundle.issuesFor) !== null && _b !== void 0 ? _b : ((data) => {
|
|
73
156
|
const result = validator.validateFormData(data, schema, undefined, undefined, uiSchema);
|
|
74
157
|
return result.errors.map(error => ({
|
|
75
158
|
message: error.stack || error.message || 'Invalid value.',
|
|
76
159
|
...(error.property === undefined ? {} : { path: error.property })
|
|
77
160
|
}));
|
|
78
|
-
};
|
|
161
|
+
});
|
|
79
162
|
// RJSF uses undefined for cleared controls; persist these as omitted properties (or null at the root).
|
|
80
163
|
const formValue = (value) => JSON.parse(JSON.stringify(value !== null && value !== void 0 ? value : null));
|
|
81
164
|
const dataFor = (draft) => {
|
|
@@ -94,7 +177,7 @@ export function createSchemaEditor(options) {
|
|
|
94
177
|
}
|
|
95
178
|
const data = decodeSource(slot.value, format);
|
|
96
179
|
// Initialize the session draft once; remounting must not reset edits or errors.
|
|
97
|
-
return { data: formValue(getDefaultFormState(validator, schema, data, schema)) };
|
|
180
|
+
return { data: bundle ? data : formValue(getDefaultFormState(validator, schema, data, schema)) };
|
|
98
181
|
},
|
|
99
182
|
validate: (draft, snapshot) => {
|
|
100
183
|
const data = dataFor(draft);
|
|
@@ -134,7 +217,8 @@ export function createSchemaEditor(options) {
|
|
|
134
217
|
constAsDefaults: 'never'
|
|
135
218
|
},
|
|
136
219
|
tagName: 'div',
|
|
137
|
-
liveValidate:
|
|
220
|
+
liveValidate: !bundle,
|
|
221
|
+
...(bundle ? { noValidate: true, extraErrors: fieldErrors(state.issues) } : {}),
|
|
138
222
|
showErrorList: false,
|
|
139
223
|
disabled: state.status !== 'editing',
|
|
140
224
|
onChange: (event) => {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CompiledSchema, SchemaInputValidationResult, SchemaJsonValue, SchemaValidationContext, SyncOwnerValidator } from './schema-types.js';
|
|
2
|
+
/** Validate one protected candidate/context snapshot, then invoke a synchronous owner at most once. */
|
|
3
|
+
export declare function validateSchemaInput<C>(compiled: CompiledSchema, data: SchemaJsonValue, options: {
|
|
4
|
+
readonly context: SchemaValidationContext<C>;
|
|
5
|
+
readonly ownerValidate?: SyncOwnerValidator<C>;
|
|
6
|
+
}): SchemaInputValidationResult;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { scopeFor } from './schema-engine.js';
|
|
2
|
+
import { isJsonRecord, JsonSnapshotError, snapshotJson } from './schema-json.js';
|
|
3
|
+
function deepFreeze(value) {
|
|
4
|
+
if (value !== null && typeof value === 'object') {
|
|
5
|
+
Object.values(value).forEach(deepFreeze);
|
|
6
|
+
Object.freeze(value);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
function ownerFailure(scope, code) {
|
|
11
|
+
return Object.freeze({ kind: 'execution-error', diagnostics: Object.freeze([
|
|
12
|
+
Object.freeze({ scope, phase: 'execution', operation: 'semantic', code })
|
|
13
|
+
]) });
|
|
14
|
+
}
|
|
15
|
+
function asyncResult(value) {
|
|
16
|
+
// Handle rejected native/cross-realm promises without running a user-defined `then` getter.
|
|
17
|
+
try {
|
|
18
|
+
void Promise.prototype.then.call(value, () => undefined, () => undefined);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch ( /* Non-promises have no Promise internal slots. */_a) { /* Non-promises have no Promise internal slots. */ }
|
|
22
|
+
return 'then' in value;
|
|
23
|
+
}
|
|
24
|
+
/** Validate one protected candidate/context snapshot, then invoke a synchronous owner at most once. */
|
|
25
|
+
export function validateSchemaInput(compiled, data, options) {
|
|
26
|
+
let scope = scopeFor(compiled);
|
|
27
|
+
let context;
|
|
28
|
+
let owner;
|
|
29
|
+
try {
|
|
30
|
+
if (!isJsonRecord(options))
|
|
31
|
+
return ownerFailure(scope, 'owner-context-invalid');
|
|
32
|
+
const descriptor = Object.getOwnPropertyDescriptor(options, 'context');
|
|
33
|
+
if (!descriptor || !('value' in descriptor))
|
|
34
|
+
return ownerFailure(scope, 'owner-context-invalid');
|
|
35
|
+
const candidate = snapshotJson(descriptor.value);
|
|
36
|
+
if (!isJsonRecord(candidate) || typeof candidate.consumerId !== 'string' ||
|
|
37
|
+
typeof candidate.nowMs !== 'number' || !Number.isFinite(candidate.nowMs) || !Object.hasOwn(candidate, 'settings')) {
|
|
38
|
+
return ownerFailure(scope, 'owner-context-invalid');
|
|
39
|
+
}
|
|
40
|
+
context = deepFreeze(candidate);
|
|
41
|
+
scope = Object.freeze({ ...scope, consumerId: context.consumerId });
|
|
42
|
+
const ownerDescriptor = Object.getOwnPropertyDescriptor(options, 'ownerValidate');
|
|
43
|
+
if (ownerDescriptor && !('value' in ownerDescriptor))
|
|
44
|
+
return ownerFailure(scope, 'owner-result-invalid');
|
|
45
|
+
owner = ownerDescriptor === null || ownerDescriptor === void 0 ? void 0 : ownerDescriptor.value;
|
|
46
|
+
if (owner !== undefined && typeof owner !== 'function')
|
|
47
|
+
return ownerFailure(scope, 'owner-result-invalid');
|
|
48
|
+
}
|
|
49
|
+
catch (_a) {
|
|
50
|
+
return ownerFailure(scope, 'owner-context-invalid');
|
|
51
|
+
}
|
|
52
|
+
let input;
|
|
53
|
+
try {
|
|
54
|
+
input = deepFreeze(snapshotJson(data));
|
|
55
|
+
const structural = compiled.validate(input, { consumerId: context.consumerId });
|
|
56
|
+
if (!isJsonRecord(structural) || !Array.isArray(structural.diagnostics) ||
|
|
57
|
+
(structural.kind === 'valid' ? structural.diagnostics.length !== 0 : structural.diagnostics.length === 0) ||
|
|
58
|
+
!['valid', 'invalid', 'execution-error'].includes(structural.kind))
|
|
59
|
+
throw new Error();
|
|
60
|
+
if (structural.kind !== 'valid')
|
|
61
|
+
return structural;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof JsonSnapshotError && error.kind === 'invalid')
|
|
65
|
+
return Object.freeze({ kind: 'invalid', diagnostics: Object.freeze([
|
|
66
|
+
Object.freeze({ scope, phase: 'structure', code: 'input-not-json', dataPath: error.dataPath })
|
|
67
|
+
]) });
|
|
68
|
+
return Object.freeze({ kind: 'execution-error', diagnostics: Object.freeze([Object.freeze({ scope, phase: 'execution', operation: 'structure',
|
|
69
|
+
code: error instanceof JsonSnapshotError && error.kind === 'limit' || error instanceof RangeError ? 'limit-exceeded' : 'engine-failed' })]) });
|
|
70
|
+
}
|
|
71
|
+
if (!owner)
|
|
72
|
+
return Object.freeze({ kind: 'valid', diagnostics: Object.freeze([]) });
|
|
73
|
+
let result;
|
|
74
|
+
try {
|
|
75
|
+
result = owner(input, context);
|
|
76
|
+
}
|
|
77
|
+
catch (_b) {
|
|
78
|
+
return ownerFailure(scope, 'owner-validation-failed');
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
if (result !== null && (typeof result === 'object' || typeof result === 'function') && asyncResult(result))
|
|
82
|
+
return ownerFailure(scope, 'async-owner-unsupported');
|
|
83
|
+
const snapshot = snapshotJson(result);
|
|
84
|
+
if (!isJsonRecord(snapshot) || !Array.isArray(snapshot.issues))
|
|
85
|
+
return ownerFailure(scope, 'owner-result-invalid');
|
|
86
|
+
if (snapshot.kind === 'valid' && snapshot.issues.length === 0)
|
|
87
|
+
return Object.freeze({ kind: 'valid', diagnostics: Object.freeze([]) });
|
|
88
|
+
if (snapshot.kind !== 'invalid' || snapshot.issues.length === 0)
|
|
89
|
+
return ownerFailure(scope, 'owner-result-invalid');
|
|
90
|
+
const diagnostics = [];
|
|
91
|
+
for (const issue of snapshot.issues) {
|
|
92
|
+
if (!isJsonRecord(issue) || typeof issue.dataPath !== 'string' ||
|
|
93
|
+
(issue.dataPath !== '' && !issue.dataPath.startsWith('/')) || /~(?:[^01]|$)/u.test(issue.dataPath) || typeof issue.ruleId !== 'string') {
|
|
94
|
+
return ownerFailure(scope, 'owner-result-invalid');
|
|
95
|
+
}
|
|
96
|
+
diagnostics.push(Object.freeze({ scope, phase: 'semantic', code: 'owner-input-invalid', dataPath: issue.dataPath, ruleId: issue.ruleId }));
|
|
97
|
+
}
|
|
98
|
+
return Object.freeze({ kind: 'invalid', diagnostics: Object.freeze(diagnostics) });
|
|
99
|
+
}
|
|
100
|
+
catch (_c) {
|
|
101
|
+
return ownerFailure(scope, 'owner-result-invalid');
|
|
102
|
+
}
|
|
103
|
+
}
|