@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.
Files changed (123) hide show
  1. package/README.md +30 -6
  2. package/dist/build-info.json +7 -2
  3. package/dist/descendants.js +11 -2
  4. package/dist/fixed-input-branches.d.ts +12 -0
  5. package/dist/fixed-input-branches.js +180 -0
  6. package/dist/fixed-input-editor.d.ts +39 -0
  7. package/dist/fixed-input-editor.js +538 -0
  8. package/dist/fixed-input-schema.d.ts +15 -0
  9. package/dist/fixed-input-schema.js +21 -0
  10. package/dist/fixed-input-snapshot.d.ts +7 -0
  11. package/dist/fixed-input-snapshot.js +180 -0
  12. package/dist/fixed-input-types.d.ts +138 -0
  13. package/dist/fixed-input-types.js +23 -0
  14. package/dist/fixed-input-view.d.ts +14 -0
  15. package/dist/fixed-input-view.js +168 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.js +4 -0
  18. package/dist/json-value.d.ts +4 -0
  19. package/dist/json-value.js +12 -0
  20. package/dist/prepared-save.d.ts +5 -0
  21. package/dist/prepared-save.js +47 -0
  22. package/dist/prepared-types.d.ts +13 -0
  23. package/dist/prepared-types.js +1 -0
  24. package/dist/react.css +42 -0
  25. package/dist/react.d.ts +3 -0
  26. package/dist/react.js +10 -1
  27. package/dist/registry.d.ts +6 -0
  28. package/dist/registry.js +98 -48
  29. package/dist/rjsf.d.ts +48 -1
  30. package/dist/rjsf.js +92 -8
  31. package/dist/schema-compose.d.ts +6 -0
  32. package/dist/schema-compose.js +103 -0
  33. package/dist/schema-engine.d.ts +18 -0
  34. package/dist/schema-engine.js +298 -0
  35. package/dist/schema-json.d.ts +10 -0
  36. package/dist/schema-json.js +68 -0
  37. package/dist/schema-types.d.ts +161 -0
  38. package/dist/schema-types.js +2 -0
  39. package/dist/schema-view.d.ts +7 -0
  40. package/dist/schema-view.js +102 -0
  41. package/dist/schema.d.ts +10 -0
  42. package/dist/schema.js +87 -0
  43. package/dist/session.d.ts +3 -1
  44. package/dist/session.js +220 -9
  45. package/dist/source-document.d.ts +3 -0
  46. package/dist/source-document.js +415 -0
  47. package/dist/source-editor.d.ts +13 -0
  48. package/dist/source-editor.js +235 -0
  49. package/dist/source-schema-hints.d.ts +3 -0
  50. package/dist/source-schema-hints.js +13 -0
  51. package/dist/source-types.d.ts +47 -0
  52. package/dist/source-types.js +1 -0
  53. package/dist/source-view.d.ts +3 -0
  54. package/dist/source-view.js +318 -0
  55. package/dist/turbowarp/bridge.d.ts +2 -1
  56. package/dist/turbowarp/bridge.js +34 -5
  57. package/dist/turbowarp/fixed-fields.d.ts +82 -0
  58. package/dist/turbowarp/fixed-fields.js +311 -0
  59. package/dist/turbowarp/fixed-transaction.d.ts +18 -0
  60. package/dist/turbowarp/fixed-transaction.js +460 -0
  61. package/dist/turbowarp/host.d.ts +3 -8
  62. package/dist/turbowarp/host.js +68 -1
  63. package/dist/turbowarp.d.ts +4 -0
  64. package/dist/turbowarp.js +1 -1
  65. package/dist/types.d.ts +7 -4
  66. package/dist/view.js +4 -0
  67. package/dist/webpack4/index.js +24 -5
  68. package/dist/webpack4/metafile.json +9886 -8710
  69. package/dist/webpack4/react.js +11 -2
  70. package/dist/webpack4/react.js.map +2 -2
  71. package/dist/webpack4/rjsf.js +15205 -20951
  72. package/dist/webpack4/rjsf.js.map +4 -4
  73. package/dist/webpack4/schema.js +20 -0
  74. package/dist/webpack4/shared-75QIWLZY.js +8375 -0
  75. package/dist/webpack4/shared-75QIWLZY.js.map +7 -0
  76. package/dist/webpack4/shared-BKTRZUZH.js +42 -0
  77. package/dist/webpack4/{shared-KIFRVTSM.js.map → shared-BKTRZUZH.js.map} +1 -1
  78. package/dist/webpack4/shared-FYUILHED.js +1 -0
  79. package/dist/webpack4/shared-FYUILHED.js.map +7 -0
  80. package/dist/webpack4/shared-GQLPPRT2.js +210 -0
  81. package/dist/webpack4/shared-GQLPPRT2.js.map +7 -0
  82. package/dist/webpack4/shared-P2JBRZFP.js +2206 -0
  83. package/dist/webpack4/shared-P2JBRZFP.js.map +7 -0
  84. package/dist/webpack4/{shared-KIFRVTSM.js → shared-RN7GOT65.js} +2 -41
  85. package/dist/webpack4/shared-RN7GOT65.js.map +7 -0
  86. package/dist/webpack4/turbowarp.js +912 -9
  87. package/dist/webpack4/turbowarp.js.map +4 -4
  88. package/docs/README.md +1 -0
  89. package/docs/block-editor-e2e.md +98 -16
  90. package/docs/ci-success-gate.md +84 -0
  91. package/docs/editor/issue-12-public-validation/README.md +61 -0
  92. package/docs/editor/issue-12-public-validation/dark-invalid.png +0 -0
  93. package/docs/editor/issue-12-public-validation/dark-styles.json +173 -0
  94. package/docs/editor/issue-12-public-validation/dark-valid.png +0 -0
  95. package/docs/editor/issue-12-public-validation/deployment-target.json +5 -0
  96. package/docs/editor/issue-12-public-validation/deployment-wait.json +7 -0
  97. package/docs/editor/issue-12-public-validation/light-invalid.png +0 -0
  98. package/docs/editor/issue-12-public-validation/light-styles.json +173 -0
  99. package/docs/editor/issue-12-public-validation/light-valid.png +0 -0
  100. package/docs/editor/issue-12-public-validation/provenance.json +88 -0
  101. package/docs/editor/issue-12-public-validation/results.json +677 -0
  102. package/docs/editor/issue-12-public-validation/summary.json +206 -0
  103. package/docs/fixed-input-contracts.md +77 -0
  104. package/docs/github-pages.md +48 -41
  105. package/docs/package-distribution.md +2 -2
  106. package/docs/schema-stage1-browser.json +10 -0
  107. package/docs/schema-stage1-contracts.json +242 -0
  108. package/docs/schema-stage1-dependencies.json +202 -0
  109. package/docs/schema-stage1-git-artifact-20261018.ja.md +17 -0
  110. package/docs/schema-stage1-profile-compat.json +30 -0
  111. package/docs/schema-stage1-review-fixes-20261018.ja.md +36 -0
  112. package/docs/schema-stage1-verification.json +541 -0
  113. package/docs/schema-stage1.ja.md +80 -0
  114. package/docs/schema.md +43 -0
  115. package/docs/source-forms.ja.md +19 -0
  116. package/docs/stage-2-publication.ja.md +35 -0
  117. package/docs/template-fixture-separation.ja.md +43 -0
  118. package/package.json +18 -3
  119. package/webpack4.cjs +1 -1
  120. package/dist/webpack4/shared-CQB3NG2A.js +0 -1
  121. package/dist/webpack4/shared-WHOAW2VF.js +0 -957
  122. package/dist/webpack4/shared-WHOAW2VF.js.map +0 -7
  123. /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); } },
@@ -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.apiVersion !== EDITOR_API_VERSION)
22
- throw new TypeError('Unsupported editor API version.');
23
- if (!/^[a-z0-9]+$/i.test(definition.extensionId) ||
24
- !/^[a-z0-9][a-z0-9._-]*$/i.test(definition.editorId)) {
25
- throw new TypeError('Invalid extension ID or editor ID.');
26
- }
27
- if (!Array.isArray(definition.opcodes) || definition.opcodes.length === 0 ||
28
- new Set(definition.opcodes).size !== definition.opcodes.length ||
29
- definition.opcodes.some(opcode => typeof opcode !== 'string' ||
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
- const entry = Object.freeze({
48
- ...definition,
49
- opcodes: Object.freeze([...definition.opcodes]),
50
- editableArguments: Object.freeze([...definition.editableArguments])
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
- const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode);
81
- return entry ? new EditorSession(entry, snapshot, host) : undefined;
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
- if (!__classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").has(captured.snapshot.nodes[0].block.opcode))
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 => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode), listener => {
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(), _EditorRegistry_notifyChanges = function _EditorRegistry_notifyChanges() {
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(): BlockEditorService;
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
- /** A host owns the service's lifetime; this module never stores VM or workspace instances. */
21
- export function createBlockEditorService() {
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
- var _a;
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: true,
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
+ }