@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/README.md CHANGED
@@ -6,6 +6,9 @@ TurboWarp拡張が扱う構造化データを、フォームや独自UIで編集
6
6
 
7
7
  日本語の利用手順・API・GUI接続・公開・検証記録は[ドキュメント一覧](docs/README.md)へ集約しています。
8
8
 
9
+ [公開エディター・日英案内](https://kubohiroya.github.io/turbowarp-extension-data-editor/) と
10
+ [公開・検証の運用Issue #26](https://github.com/kubohiroya/turbowarp-extension-data-editor/issues/26) も本リポジトリが所有します。
11
+
9
12
  ## 標準の JSON Schema エディター
10
13
 
11
14
  JSON/YAML を格納する文字列引数と JSON Schema を宣言すると、react-jsonschema-form がフォームを生成します。
@@ -100,23 +103,22 @@ pnpm test
100
103
  `pnpm test` は型宣言を含むビルド後、コアと標準フォームのテストを実行します。
101
104
  パッケージは ESM です。ブラウザには ES2022 の組み込み API が必要です。
102
105
  標準フォームの React / ReactDOM peer は 16.14 / 17 / 18。GUI は既存の 16.14 を共有します。
103
- 初回の実験版は `@kubohiroya/turbowarp-extension-data-editor@0.1.0` です。
106
+ 初回の実験版は `@kubohiroya/turbowarp-extension-data-editor@0.1.0` です。固定SchemaのSource/Form編集は `0.2.0` で追加しました。今回の配布版 `0.2.1` は、既定OFFの固定入力契約・標準field transaction・固定フォームとGUI接続も含みます。
104
107
 
105
108
  ```sh
106
- npm install --save-exact @kubohiroya/turbowarp-extension-data-editor@0.1.0
109
+ npm install --save-exact @kubohiroya/turbowarp-extension-data-editor@0.2.1
107
110
  ```
108
111
 
109
112
  配布物・改名時の互換性・公開手順は[パッケージ配布](docs/package-distribution.md)を参照してください。
110
113
 
111
114
  ## GUI で試す
112
115
 
113
- `scratch-gui` はnpmに公開した本パッケージを固定versionで参照します。通常のGUIビルドに共通リポジトリの隣接配置は不要です。
116
+ `scratch-gui` は本パッケージを検証済みのnpm完全version、またはGitの完全SHAで固定参照します。通常のGUIビルドに共通リポジトリの隣接配置は不要です。
114
117
  [GUIの起動・制約・検証手順](docs/block-editor.md) に従ってください。
115
118
  既存の拡張アイコンをクリックすると、標準モーダルで下書きを編集できます。
116
119
 
117
- 現在の GUI 適用範囲は、配置済みブロックの通常の text shadow に保存された JSON/YAML 文字列です。
118
- reporter、メニュー、独自 validator 付きフィールドは書き換えません。
119
- `.sb3` の実保存と標準 `project_url` 経路での再読込を確認済みです。OS のファイル選択による再読込、アイコンがないブロックへの追加表示、翻訳・全テーマは後続の確認項目です。
120
+ 既存のSOURCE経路は、配置済みブロックの通常の text shadow に保存された JSON/YAML 文字列を扱います。この経路でreporter、メニュー、独自validator付きfieldを書き換えません。固定入力経路は対応hostと明示opt-inで静的menu・数値・色を扱い、reporterや動的menuを評価・置換しません。
121
+ `.sb3` の保存、`project_url` とOSのファイル選択による再読込、日英表示とテーマの検証手順・実行範囲は[GUI接続E2E](docs/block-editor-e2e.md)と[固定入力の契約](docs/fixed-input-contracts.md)を参照してください。
120
122
 
121
123
  ## フラグとロールバック
122
124
 
@@ -129,3 +131,25 @@ GUI の `src/lib/tw-feature-flags.js` で起動時固定の `blockEditorDialogs`
129
131
  ## webpack 4 / E2E
130
132
 
131
133
  [webpack 4配布物と設定API](docs/webpack4.md)、[GUI接続E2EとCIの実行手順](docs/block-editor-e2e.md)を参照。専用編集UIから互換ビルド・E2Eまで共通側で管理し、GUIには明示的なホスト接続とCI呼出しを残す。
134
+
135
+ ## Schemaの共通型と診断変換
136
+
137
+ `@kubohiroya/turbowarp-extension-data-editor/schema`はDOM・React・RJSFをロードせずに利用できる入口です。Schema bundle、正本・生成物・参照用の役割、処理段階別の診断・結果、同期owner検証の型を公開します。
138
+
139
+ `toValidationIssues()`は入力診断を既存の`ValidationIssue`へ変換し、JSON Pointerのpath・順序・件数を維持した凍結結果を返します。不正な診断やcompile/execution診断はTypeErrorで拒否します。既存RJSFのpathや保存時専用の非同期`validateData`の契約は変わりません。
140
+
141
+ `compileSchemaBundle()`はdraft-07/2020-12と同梱resourceからオフラインの同期validatorを生成します。Schemaと入力を変更せず、構造化診断を返します。[pure検証API](docs/schema.md)を参照してください。同期owner合成と明示bundleのRJSF経路も追加済みです。既存公開入口・webpack4/build-info・CSS buildを維持しています。
142
+
143
+ 同期owner検証には`/schema`の`validateSchemaInput`を利用できます。明示bundleフォームは`/rjsf`の`createSchemaBundleEditor` / `registerSchemaBundle`で登録します。context providerは呼出しごとに時刻・設定を渡します。表示上の対応範囲と既存APIの扱いは[Schema基盤](docs/schema.md)を参照してください。
144
+
145
+ 既存SOURCE契約の導入時に採取した横断検証記録は**14契約・8 package**です。[Template分離後の再検証手順](docs/template-fixture-separation.ja.md#current-verification)で、Templateを含まないisolation indexと2つの検証commandを確認してください。この導入時記録では14本番契約と9固定契約を区別しています。固定入力の実装・GUI接続は末尾の契約節を参照してください。旧16契約・9 packageの結果と当時の開始条件は[段階1の履歴](docs/schema-stage1.ja.md)として保持します。
146
+
147
+ ## Optional editing examples
148
+
149
+ Repository examples are opt-in teaching code, not template blocks or additional package exports. See [the JSON/YAML document example](examples/document-properties.mjs) and [its guide](examples/README.ja.md). Each extension owns its actual data contract.
150
+
151
+ Prepared source editing is an optional host capability. See [the source/form contract](docs/source-forms.ja.md) for fixed-schema bindings, atomic saves, compatibility, and rollback.
152
+
153
+ ## 固定入力とGUI接続(S3-1〜S3-3)
154
+
155
+ 固定入力の型・Schema検査・同期prepared契約を追加しています。`createBlockEditorService({fixedInputs: true})`の明示時だけ新しい登録APIを提供し、対応hostがない場合は開けません。lower-level hostの`enableFixedInputs: true`で標準fieldの読取・原子的保存を利用できます。固定フォームとGUIの既定OFFフラグ接続も含みます。各拡張は正本Schema・codec・登録を所有します。[固定入力の契約](docs/fixed-input-contracts.md)を参照してください。配布物には元Git SHAと版更新を含むworking-tree release overlayの区分を記録しています。
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@kubohiroya/turbowarp-extension-data-editor",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "repository": "https://github.com/kubohiroya/turbowarp-extension-data-editor",
5
- "revision": "9e041e5278a1ea70146a76c10732d1401955a12a"
5
+ "revision": "485f0e550e622235624b5e9176820161aeeba252",
6
+ "sourceState": "working-tree",
7
+ "sourceSha256": "175674eaaffce60dd9ebcf4c31f0f02a3bbd9dc6b8659ab3101ea6d8bceeea2f",
8
+ "capabilities": {
9
+ "preparedSource": 1
10
+ }
6
11
  }
@@ -143,9 +143,15 @@ export class DescendantSession {
143
143
  __classPrivateFieldGet(this, _DescendantSession_instances, "m", _DescendantSession_notify).call(this);
144
144
  try {
145
145
  __classPrivateFieldGet(this, _DescendantSession_instances, "m", _DescendantSession_assertSaving).call(this);
146
+ const prepared = new Map();
146
147
  for (const record of __classPrivateFieldGet(this, _DescendantSession_records, "f").values()) {
147
148
  try {
148
- const issues = await abortable(record.definition.validate(cloneJson(record.draft), record.snapshot), __classPrivateFieldGet(this, _DescendantSession_abort, "f").signal);
149
+ const result = isPreparedEditor(record.definition)
150
+ ? capturePreparedSave(record.definition, cloneJson(record.draft), record.snapshot) : undefined;
151
+ if (result)
152
+ prepared.set(record.snapshot.blockId, result);
153
+ const issues = result ? (result.kind === 'invalid' ? result.issues : [])
154
+ : await abortable(record.definition.validate(cloneJson(record.draft), record.snapshot), __classPrivateFieldGet(this, _DescendantSession_abort, "f").signal);
149
155
  __classPrivateFieldGet(this, _DescendantSession_instances, "m", _DescendantSession_assertSaving).call(this);
150
156
  record.issues = copyIssues(issues);
151
157
  }
@@ -161,7 +167,9 @@ export class DescendantSession {
161
167
  const destinations = new Set();
162
168
  for (const record of __classPrivateFieldGet(this, _DescendantSession_records, "f").values()) {
163
169
  try {
164
- const patch = record.definition.createPatch(cloneJson(record.draft), record.snapshot);
170
+ const result = prepared.get(record.snapshot.blockId);
171
+ const patch = (result === null || result === void 0 ? void 0 : result.kind) === 'ready' ? result.changes
172
+ : record.definition.createPatch(cloneJson(record.draft), record.snapshot);
165
173
  __classPrivateFieldGet(this, _DescendantSession_instances, "m", _DescendantSession_assertSaving).call(this);
166
174
  if (!Array.isArray(patch))
167
175
  throw new TypeError('createPatch() must return an array.');
@@ -334,3 +342,4 @@ _DescendantSession_snapshot = new WeakMap(), _DescendantSession_host = new WeakM
334
342
  __classPrivateFieldGet(this, _DescendantSession_detach, "f").clear();
335
343
  __classPrivateFieldGet(this, _DescendantSession_listeners, "f").clear();
336
344
  };
345
+ import { isPreparedEditor, capturePreparedSave } from './prepared-save.js';
@@ -0,0 +1,12 @@
1
+ import type { FixedBlockSnapshot, FixedInputBranchCase, FixedInputBranchRule, FixedInputBranchState, FixedInputDeclaration, FixedInputDependency } from './fixed-input-types.js';
2
+ import type { ArgumentSlot, LiteralArgumentChange, ValidationIssue } from './types.js';
3
+ /** Capture finite JSON policy once; callbacks and getters are never part of host authority. */
4
+ export declare function captureFixedBranchRules(value: unknown, dependencies: readonly FixedInputDependency[], readOnlyArguments: readonly string[]): readonly FixedInputBranchRule[];
5
+ export interface ResolvedFixedBranch extends FixedInputBranchState {
6
+ readonly rule: FixedInputBranchRule;
7
+ readonly selected?: FixedInputBranchCase;
8
+ }
9
+ /** Resolve only captured literal metadata. Reporters and unavailable fields are never evaluated. */
10
+ export declare function resolveFixedBranches(declaration: FixedInputDeclaration, snapshot: FixedBlockSnapshot, arguments_?: Readonly<Record<string, ArgumentSlot>>): readonly ResolvedFixedBranch[];
11
+ /** Shared session/host gate. Schema and owner validation remain the prepared factory's responsibility. */
12
+ export declare function validateFixedBranchChanges(declaration: FixedInputDeclaration, snapshot: FixedBlockSnapshot, changes: readonly LiteralArgumentChange[]): readonly ValidationIssue[];
@@ -0,0 +1,180 @@
1
+ import { snapshotJson } from './schema-json.js';
2
+ const fail = () => { throw new TypeError('Invalid fixed branch declaration.'); };
3
+ const names = (value) => {
4
+ if (!Array.isArray(value) || !value.length || value.some(item => typeof item !== 'string' || !item) ||
5
+ new Set(value).size !== value.length)
6
+ return fail();
7
+ return Object.freeze(value);
8
+ };
9
+ const object = (value) => {
10
+ if (!value || typeof value !== 'object' || Array.isArray(value))
11
+ return fail();
12
+ return value;
13
+ };
14
+ const normalize = (value, rule) => {
15
+ var _a, _b;
16
+ const text = ((_a = rule.normalize) === null || _a === void 0 ? void 0 : _a.trim) ? value.trim() : value;
17
+ return ((_b = rule.normalize) === null || _b === void 0 ? void 0 : _b.lowercase) ? text.toLowerCase() : text;
18
+ };
19
+ /** Capture finite JSON policy once; callbacks and getters are never part of host authority. */
20
+ export function captureFixedBranchRules(value, dependencies, readOnlyArguments) {
21
+ if (value === undefined)
22
+ return Object.freeze([]);
23
+ const captured = snapshotJson(value);
24
+ if (!Array.isArray(captured))
25
+ return fail();
26
+ const selectors = new Set();
27
+ const occupied = new Set();
28
+ const result = [];
29
+ for (const item of captured) {
30
+ const rule = object(item);
31
+ if (typeof rule.selector !== 'string' || selectors.has(rule.selector))
32
+ return fail();
33
+ const group = dependencies.find(dependency => dependency.selector === rule.selector);
34
+ if (!group)
35
+ return fail();
36
+ for (const argument of [group.selector, ...group.dependents]) {
37
+ if (occupied.has(argument) || dependencies.some(other => other !== group &&
38
+ (other.selector === argument || other.dependents.includes(argument))))
39
+ return fail();
40
+ occupied.add(argument);
41
+ }
42
+ selectors.add(rule.selector);
43
+ let normalization;
44
+ if (rule.normalize !== undefined) {
45
+ const input = object(rule.normalize);
46
+ if (Object.keys(input).some(key => key !== 'trim' && key !== 'lowercase') ||
47
+ Object.values(input).some(value => typeof value !== 'boolean'))
48
+ return fail();
49
+ normalization = Object.freeze({ ...input });
50
+ }
51
+ const shell = { selector: rule.selector, cases: [], ...(normalization ? { normalize: normalization } : {}) };
52
+ if (!Array.isArray(rule.cases) || !rule.cases.length)
53
+ return fail();
54
+ const values = new Set();
55
+ const cases = [];
56
+ for (const input of rule.cases) {
57
+ const branch = object(input);
58
+ const caseValues = names(branch.values);
59
+ const normalized = caseValues.map(value => normalize(value, shell));
60
+ if (normalized.some(value => !value || values.has(value)))
61
+ return fail();
62
+ for (const value of normalized)
63
+ values.add(value);
64
+ if (new Set(normalized).size !== normalized.length)
65
+ return fail();
66
+ if (!Array.isArray(branch.activeDependents))
67
+ return fail();
68
+ const active = branch.activeDependents.length ? names(branch.activeDependents) : Object.freeze([]);
69
+ if (active.some(argument => !group.dependents.includes(argument)))
70
+ return fail();
71
+ const requirements = [];
72
+ if (branch.requireOnSelectorChange !== undefined) {
73
+ if (!Array.isArray(branch.requireOnSelectorChange))
74
+ return fail();
75
+ const required = new Set();
76
+ for (const input of branch.requireOnSelectorChange) {
77
+ const requirement = object(input);
78
+ if (typeof requirement.argument !== 'string' || !readOnlyArguments.includes(requirement.argument) ||
79
+ required.has(requirement.argument) || (requirement.rule !== 'literal' && requirement.rule !== 'nonempty-string'))
80
+ return fail();
81
+ required.add(requirement.argument);
82
+ requirements.push(Object.freeze({ argument: requirement.argument, rule: requirement.rule }));
83
+ }
84
+ }
85
+ cases.push(Object.freeze({ values: caseValues, activeDependents: active,
86
+ ...(branch.requireOnSelectorChange === undefined ? {} : { requireOnSelectorChange: Object.freeze(requirements) }) }));
87
+ }
88
+ result.push(Object.freeze({ ...shell, cases: Object.freeze(cases) }));
89
+ }
90
+ return Object.freeze(result);
91
+ }
92
+ /** Resolve only captured literal metadata. Reporters and unavailable fields are never evaluated. */
93
+ export function resolveFixedBranches(declaration, snapshot, arguments_ = snapshot.arguments) {
94
+ var _a;
95
+ return Object.freeze(((_a = declaration.branchRules) !== null && _a !== void 0 ? _a : []).map(rule => {
96
+ const metadata = snapshot.fixedContext.inputs[rule.selector];
97
+ const slot = arguments_[rule.selector];
98
+ let status;
99
+ if (!metadata || metadata.source === 'missing')
100
+ status = 'missing';
101
+ else if (metadata.source === 'reporter' || (slot === null || slot === void 0 ? void 0 : slot.kind) === 'reporter')
102
+ status = 'reporter';
103
+ else if ((metadata.source !== 'field' && metadata.source !== 'shadow') || metadata.role !== 'editable')
104
+ status = 'unsupported';
105
+ else if ((slot === null || slot === void 0 ? void 0 : slot.kind) !== 'literal' || typeof slot.value !== 'string')
106
+ status = 'unknown';
107
+ else {
108
+ const value = normalize(slot.value, rule);
109
+ const selected = rule.cases.find(branch => branch.values.some(item => normalize(item, rule) === value));
110
+ if (selected)
111
+ return Object.freeze({ selector: rule.selector, rule, selected,
112
+ status: slot.value === selected.values[0] ? 'known' : 'alias', value: selected.values[0] });
113
+ status = 'unknown';
114
+ }
115
+ return Object.freeze({ selector: rule.selector, rule, status });
116
+ }));
117
+ }
118
+ const writableLiteral = (snapshot, arguments_, name) => {
119
+ var _a;
120
+ const metadata = snapshot.fixedContext.inputs[name];
121
+ return ((_a = arguments_[name]) === null || _a === void 0 ? void 0 : _a.kind) === 'literal' && (metadata === null || metadata === void 0 ? void 0 : metadata.role) === 'editable' &&
122
+ (metadata.source === 'field' || metadata.source === 'shadow');
123
+ };
124
+ const diagnostic = (declaration, code, argument) => Object.freeze({
125
+ message: `Fixed branch validation failed: ${code}; contract=${JSON.stringify(declaration.contractId)}.`,
126
+ path: `/${argument.replace(/~/g, '~0').replace(/\//g, '~1')}`
127
+ });
128
+ /** Shared session/host gate. Schema and owner validation remain the prepared factory's responsibility. */
129
+ export function validateFixedBranchChanges(declaration, snapshot, changes) {
130
+ var _a;
131
+ const issues = [];
132
+ const finalArguments = { ...snapshot.arguments };
133
+ const changed = new Set();
134
+ const seen = new Set();
135
+ for (const change of changes) {
136
+ if (seen.has(change.argument) || !['string', 'number', 'boolean'].includes(typeof change.value) || !declaration.editableArguments.includes(change.argument) ||
137
+ !writableLiteral(snapshot, snapshot.arguments, change.argument)) {
138
+ issues.push(diagnostic(declaration, 'disallowed-change', change.argument));
139
+ continue;
140
+ }
141
+ seen.add(change.argument);
142
+ const original = snapshot.arguments[change.argument];
143
+ if ((original === null || original === void 0 ? void 0 : original.kind) === 'literal' && Object.is(original.value, change.value))
144
+ continue;
145
+ changed.add(change.argument);
146
+ Object.defineProperty(finalArguments, change.argument, { value: { kind: 'literal', value: change.value }, enumerable: true, configurable: true });
147
+ }
148
+ const resolved = resolveFixedBranches(declaration, snapshot, finalArguments);
149
+ for (const group of declaration.dependencies) {
150
+ if (![group.selector, ...group.dependents].some(name => changed.has(name)))
151
+ continue;
152
+ const branch = resolved.find(item => item.selector === group.selector);
153
+ if (!(branch === null || branch === void 0 ? void 0 : branch.selected)) {
154
+ issues.push(diagnostic(declaration, branch ? 'selector-unavailable' : 'fixed-dependency-validation-unavailable', group.selector));
155
+ continue;
156
+ }
157
+ for (const argument of group.dependents) {
158
+ if (changed.has(argument) && !branch.selected.activeDependents.includes(argument)) {
159
+ issues.push(diagnostic(declaration, 'inactive-change', argument));
160
+ }
161
+ }
162
+ if (!changed.has(group.selector))
163
+ continue;
164
+ for (const argument of branch.selected.activeDependents) {
165
+ if (!writableLiteral(snapshot, finalArguments, argument))
166
+ issues.push(diagnostic(declaration, 'dependency-unverifiable', argument));
167
+ }
168
+ for (const requirement of (_a = branch.selected.requireOnSelectorChange) !== null && _a !== void 0 ? _a : []) {
169
+ const metadata = snapshot.fixedContext.inputs[requirement.argument];
170
+ const slot = finalArguments[requirement.argument];
171
+ if (!metadata || metadata.role !== 'readonly' || (metadata.source !== 'field' && metadata.source !== 'shadow') || (slot === null || slot === void 0 ? void 0 : slot.kind) !== 'literal') {
172
+ issues.push(diagnostic(declaration, 'dependency-unverifiable', requirement.argument));
173
+ }
174
+ else if (requirement.rule === 'nonempty-string' && (typeof slot.value !== 'string' || !slot.value.trim())) {
175
+ issues.push(diagnostic(declaration, 'dependency-invalid', requirement.argument));
176
+ }
177
+ }
178
+ }
179
+ return Object.freeze(issues);
180
+ }
@@ -0,0 +1,39 @@
1
+ import type { FixedBlockSnapshot, FixedInputBranchRule, FixedInputControl, FixedInputDeclaration, FixedInputEditorDefinition, FixedPreparedSave } from './fixed-input-types.js';
2
+ import type { SchemaBundle, SchemaValidationContext } from './schema-types.js';
3
+ import type { EditorDefinition, JsonObject, JsonValue, LiteralValue, ValidationIssue } from './types.js';
4
+ export interface FixedInputBinding<C = null> {
5
+ readonly argument: string;
6
+ readonly schemaBundle: SchemaBundle;
7
+ readonly control?: FixedInputControl;
8
+ /** Produce a JSON editing projection. Throw to retain the raw literal as readonly. */
9
+ read(value: LiteralValue): JsonValue;
10
+ /** Construct the exact literal candidate once. A numeric buffer supplies its validated string token, which must be returned unchanged. Neither this nor validation may be asynchronous. */
11
+ encode(value: JsonValue, context: SchemaValidationContext<C>): LiteralValue;
12
+ /** Check the exact candidate. Messages from this callback are never rendered verbatim. */
13
+ validateCandidate(candidate: LiteralValue, context: SchemaValidationContext<C>, snapshot: FixedBlockSnapshot): readonly ValidationIssue[];
14
+ }
15
+ export interface FixedInputBranchPolicy<C = null> extends Omit<FixedInputBranchRule, 'cases'> {
16
+ readonly cases: readonly (FixedInputBranchRule['cases'][number] & {
17
+ readonly bindings?: readonly FixedInputBinding<C>[];
18
+ })[];
19
+ }
20
+ export interface FixedInputEditorOptions<C = null> {
21
+ readonly apiVersion: 1;
22
+ readonly extensionId: string;
23
+ readonly editorId: string;
24
+ readonly opcodes: readonly string[];
25
+ readonly contractId: string;
26
+ readonly contractRevision: string;
27
+ readonly readOnlyArguments?: readonly string[];
28
+ readonly dependencies?: FixedInputDeclaration['dependencies'];
29
+ readonly branchPolicies?: readonly FixedInputBranchPolicy<C>[];
30
+ readonly bindings: readonly FixedInputBinding<C>[];
31
+ readonly validationContext: () => SchemaValidationContext<C>;
32
+ }
33
+ /** Capture exactly the callbacks and declaration values subsequently registered. */
34
+ export declare function captureFixedInputEditorDefinition<C = null>(value: FixedInputEditorDefinition<C>): FixedInputEditorDefinition<C>;
35
+ export declare function isFixedInputEditor(definition: EditorDefinition): definition is FixedInputEditorDefinition<unknown>;
36
+ /** Capture a synchronous callback result without invoking getters, iterators, or toJSON. */
37
+ export declare function captureFixedPreparedSave<C = null>(definition: FixedInputEditorDefinition<C>, draft: JsonObject, snapshot: FixedBlockSnapshot): FixedPreparedSave<C>;
38
+ /** Fixed inputs are additive: legacy callbacks cannot accidentally save their drafts. */
39
+ export declare function createFixedInputEditor<C = null>(options: FixedInputEditorOptions<C>): FixedInputEditorDefinition<C>;