behavior-contracts 0.7.2 → 0.8.0

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 (68) hide show
  1. package/README.md +0 -1
  2. package/dist/authoring.d.ts +215 -3
  3. package/dist/authoring.d.ts.map +1 -1
  4. package/dist/authoring.js +338 -8
  5. package/dist/authoring.js.map +1 -1
  6. package/dist/behavior.d.ts +101 -3
  7. package/dist/behavior.d.ts.map +1 -1
  8. package/dist/behavior.js +126 -4
  9. package/dist/behavior.js.map +1 -1
  10. package/dist/bind.d.ts +64 -0
  11. package/dist/bind.d.ts.map +1 -0
  12. package/dist/bind.js +131 -0
  13. package/dist/bind.js.map +1 -0
  14. package/dist/builder.d.ts +30 -88
  15. package/dist/builder.d.ts.map +1 -1
  16. package/dist/builder.js +114 -59
  17. package/dist/builder.js.map +1 -1
  18. package/dist/expr-operator-types.d.ts +89 -0
  19. package/dist/expr-operator-types.d.ts.map +1 -0
  20. package/dist/expr-operator-types.js +134 -0
  21. package/dist/expr-operator-types.js.map +1 -0
  22. package/dist/generator/async-plan.d.ts.map +1 -1
  23. package/dist/generator/async-plan.js +4 -0
  24. package/dist/generator/async-plan.js.map +1 -1
  25. package/dist/generator/core.d.ts +15 -0
  26. package/dist/generator/core.d.ts.map +1 -1
  27. package/dist/generator/core.js +2 -0
  28. package/dist/generator/core.js.map +1 -1
  29. package/dist/generator/emit-php.d.ts.map +1 -1
  30. package/dist/generator/emit-php.js +1 -0
  31. package/dist/generator/emit-php.js.map +1 -1
  32. package/dist/generator/emit-python.d.ts.map +1 -1
  33. package/dist/generator/emit-python.js +1 -0
  34. package/dist/generator/emit-python.js.map +1 -1
  35. package/dist/generator/emit-straightline-typed-typescript.d.ts.map +1 -1
  36. package/dist/generator/emit-straightline-typed-typescript.js +1 -0
  37. package/dist/generator/emit-straightline-typed-typescript.js.map +1 -1
  38. package/dist/generator/emit-typed-native-go.d.ts.map +1 -1
  39. package/dist/generator/emit-typed-native-go.js +364 -14
  40. package/dist/generator/emit-typed-native-go.js.map +1 -1
  41. package/dist/generator/emit-typed-native-rust.d.ts.map +1 -1
  42. package/dist/generator/emit-typed-native-rust.js +347 -21
  43. package/dist/generator/emit-typed-native-rust.js.map +1 -1
  44. package/dist/generator/emit-typescript.d.ts.map +1 -1
  45. package/dist/generator/emit-typescript.js +24 -8
  46. package/dist/generator/emit-typescript.js.map +1 -1
  47. package/dist/generator/index.d.ts +1 -1
  48. package/dist/generator/index.d.ts.map +1 -1
  49. package/dist/generator/index.js +7 -5
  50. package/dist/generator/index.js.map +1 -1
  51. package/dist/generator/straightline.d.ts.map +1 -1
  52. package/dist/generator/straightline.js +21 -1
  53. package/dist/generator/straightline.js.map +1 -1
  54. package/dist/guard.js +20 -1
  55. package/dist/guard.js.map +1 -1
  56. package/dist/index.d.ts +42 -9
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +28 -7
  59. package/dist/index.js.map +1 -1
  60. package/dist/provenance.d.ts +102 -0
  61. package/dist/provenance.d.ts.map +1 -0
  62. package/dist/provenance.js +128 -0
  63. package/dist/provenance.js.map +1 -0
  64. package/dist/type-gate.d.ts +63 -0
  65. package/dist/type-gate.d.ts.map +1 -0
  66. package/dist/type-gate.js +295 -0
  67. package/dist/type-gate.js.map +1 -0
  68. package/package.json +4 -3
package/dist/bind.js ADDED
@@ -0,0 +1,131 @@
1
+ import { runBehavior, runBehaviorAsync } from "./behavior.js";
2
+ import { assertCompiled, getLeafImplRegistry } from "./provenance.js";
3
+ export class BindFailure extends Error {
4
+ code;
5
+ constructor(code, message) {
6
+ super(message);
7
+ this.code = code;
8
+ this.name = "BindFailure";
9
+ }
10
+ }
11
+ function bindFail(code, message) {
12
+ throw new BindFailure(code, message);
13
+ }
14
+ /**
15
+ * IR の leaf 参照を全 component から収集し、name→dispatch 形態({@link LeafRef})を返す。
16
+ * fanout は常に batched、`map.batched:true` は batched、それ以外(componentRef / 非 batched map)は
17
+ * 非 batched。cond は leaf を呼ばない。key 集合が bind 時完全性ゲートの検査対象、`LeafRef` が adapter の
18
+ * batched 再構成の指示(曖昧なら bind で loud reject)。
19
+ */
20
+ function referencedLeafNames(ir) {
21
+ const names = new Map();
22
+ const mark = (name, batched) => {
23
+ const ref = names.get(name) ?? { batched: false, nonBatched: false };
24
+ if (batched)
25
+ ref.batched = true;
26
+ else
27
+ ref.nonBatched = true;
28
+ names.set(name, ref);
29
+ };
30
+ for (const comp of ir.components) {
31
+ for (const n of comp.body) {
32
+ if ("fanout" in n)
33
+ mark(n.fanout.component, true); // fanout は常に batched dispatch
34
+ else if ("map" in n)
35
+ mark(n.map.component, n.map.batched === true);
36
+ else if ("cond" in n)
37
+ continue;
38
+ else
39
+ mark(n.component, false);
40
+ }
41
+ }
42
+ return names;
43
+ }
44
+ /**
45
+ * registry の leaf impl(`defineLeaf` の**per-element 型付き静的関数** `(p, ctx) => value`)を、
46
+ * 既存 `Handler`(`(ports, ctx) => ExecOutcome`)へ adapt する。`ctx` は bind 時に閉じ込めた環境境界を
47
+ * 全 impl に渡す(name→function は注入しない — SA8)。
48
+ *
49
+ * batched ABI の機械的再構成: runBehavior は `map.batched` / fanout ノードで handler を
50
+ * `handler({items:[<各要素の評価済み ports>…]}, ctx)` と **1 回**呼び、items と整列した結果リストを
51
+ * 期待する。一方 `defineLeaf` の impl は **per-element**(型付きシグネチャ = 1 要素の Port → 1 要素の
52
+ * 出力)。両者の橋渡しは bind の責務とし、batched 参照される leaf は impl を各 item へ写して整列リストを
53
+ * 組む(consumer は impl 本体だけを per-element で書き、`{items}` ループを手書きしない)。`batched` フラグ
54
+ * は IR ノード種から導出済み({@link referencedLeafNames})。
55
+ */
56
+ function adaptHandlers(registry, refs, ctx) {
57
+ // null-proto: behavior.ts の `handlers[name]` lookup が prototype メソッド(Object.prototype.toString 等)を
58
+ // 拾わないようにする(leaf 名 'constructor' 等で `if (!handler)` が誤って通るのを防ぐ)。
59
+ const handlers = Object.create(null);
60
+ for (const name of Object.keys(registry)) {
61
+ const impl = registry[name];
62
+ // 曖昧性(batched & 非 batched の混在)は bind ゲートで既に reject 済み → 一意な形態に確定。
63
+ const batched = refs.get(name)?.batched === true;
64
+ handlers[name] = (ports) => {
65
+ if (batched) {
66
+ const items = ports.items;
67
+ return { ok: items.map((it) => impl(it, ctx)) };
68
+ }
69
+ return { ok: impl(ports, ctx) };
70
+ };
71
+ }
72
+ return handlers;
73
+ }
74
+ /** async 版 adapter(impl が Promise を返してもよい — runBehaviorAsync が await する)。 */
75
+ function adaptAsyncHandlers(registry, refs, ctx) {
76
+ const handlers = {};
77
+ for (const name of Object.keys(registry)) {
78
+ const impl = registry[name];
79
+ const batched = refs.get(name)?.batched === true;
80
+ handlers[name] = async (ports) => {
81
+ if (batched) {
82
+ const items = ports.items;
83
+ return { ok: (await Promise.all(items.map((it) => impl(it, ctx)))) };
84
+ }
85
+ return { ok: (await impl(ports, ctx)) };
86
+ };
87
+ }
88
+ return handlers;
89
+ }
90
+ /**
91
+ * bindBehaviors — 登録 → 自動 bind の consumer 入口(SA8 / #126)。
92
+ *
93
+ * compile 済み handle の side-channel から name→impl レジストリを読み、**bind 時完全性ゲート**を
94
+ * 1 回実行(IR が参照する全 leaf 名に impl があるか — 欠落は `MISSING_LEAF_IMPL` で**実行前に**
95
+ * fail-closed)してから runnable を返す。以降 `.run` / `.runAsync` は registry から自動 dispatch し、
96
+ * 注入は `ctx`(環境境界)だけ。**handlers dict を手組みする必要はない**(SA8)。
97
+ *
98
+ * @param compiled `compileBehaviors` が返した opaque handle(token + impl レジストリを内蔵)。
99
+ * @param ctx 環境境界注入(SDK client / config / hooks)。全 leaf impl の第2引数に渡る。
100
+ */
101
+ export function bindBehaviors(compiled, ctx = {}) {
102
+ assertCompiled(compiled); // 出自ゲート: 手組み/無 token IR は NON_COMPILED_IR で fail-closed
103
+ const registry = getLeafImplRegistry(compiled);
104
+ if (registry === undefined)
105
+ bindFail("NO_REGISTRY", "bindBehaviors: the compiled handle carries no leaf-impl registry (author leaves via defineLeaf/behaviorComponents and compile with compileBehaviors — a boundary-adopted IR from loadCompiledIR has no in-process impls; use codegen static linking instead)");
106
+ // ── bind 時完全性ゲート(SA8): IR 参照 leaf 名の impl 網羅を実行前に検査(fail-closed)─────
107
+ // `Object.hasOwn` を使う(`name in registry` は prototype 継承キー — 'toString'/'constructor' 等の
108
+ // leaf 名で false-positive を招き、impl 不在を silent に通してしまう)。
109
+ const referenced = referencedLeafNames(compiled);
110
+ const missing = [...referenced.keys()].filter((name) => !Object.hasOwn(registry, name));
111
+ if (missing.length > 0)
112
+ bindFail("MISSING_LEAF_IMPL", `bindBehaviors: the IR references leaf component(s) with no registered impl: ${missing
113
+ .map((m) => `'${m}'`)
114
+ .join(", ")} — every referenced leaf must be registered via defineLeaf before bind (fail-closed at bind, not lazily at run)`);
115
+ // 同名 leaf が batched と非 batched の両方で参照される IR は、per-element impl({@link BoundLeafImpl})を
116
+ // handler へ写す際 name 単位で dispatch 形態を一意に決められない(runtime ABI が per-element / {items} で
117
+ // 異なり、per-call の信頼できる判別信号が無い)。silent に誤 dispatch せず bind で loud reject する。
118
+ const ambiguous = [...referenced.entries()].filter(([, r]) => r.batched && r.nonBatched).map(([n]) => n);
119
+ if (ambiguous.length > 0)
120
+ bindFail("AMBIGUOUS_DISPATCH", `bindBehaviors: leaf(s) ${ambiguous
121
+ .map((m) => `'${m}'`)
122
+ .join(", ")} are referenced BOTH batched (map.batched / fanout) and non-batched (componentRef / plain map) — a per-element impl cannot be dispatched unambiguously under both ABIs from one registry entry (split into distinct leaf names, one per dispatch shape)`);
123
+ // adapter は bind で 1 回だけ組む(handlers dict は registry + batched フラグから機械導出)。
124
+ const handlers = adaptHandlers(registry, referenced, ctx);
125
+ const asyncHandlers = adaptAsyncHandlers(registry, referenced, ctx);
126
+ return {
127
+ run: (method, input = {}) => runBehavior(compiled, handlers, input, method),
128
+ runAsync: (method, input = {}) => runBehaviorAsync(compiled, asyncHandlers, input, method),
129
+ };
130
+ }
131
+ //# sourceMappingURL=bind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind.js","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"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"}
package/dist/builder.d.ts CHANGED
@@ -1,63 +1,43 @@
1
1
  /**
2
- * builder.ts — programmatic component-definition builder(合成グラフの「データ登録」IF,
3
- * bc#26)+ recorder 経路(`compileBehaviors`)と共有する検証・正規化 core。
2
+ * builder.ts — component-graph の検証・正規化・plan 導出の **内部 core**。
4
3
  *
5
- * ## 責務分離(オーナー決定 bc#26 / graphddb#297 SP5)
4
+ * ## SCP-only 化(0.8.0・#127/A5)— データ登録 IF は撤去済み
6
5
  *
7
- * model 由来 lowering(@model メタデータ → relation DAG 展開)は bc#24 の evaluation-mode
8
- * recorder に乗らないことが実証された(node-id が意味論を持つ load-bearing 構造 /
9
- * relationKind・bindField emit / 動的 port 群が fail-closed schema と衝突 / compile
10
- * エントリが class-source-scan gate)。そこで:
6
+ * かつて存在した公開「データ登録」IF(`buildComponentDefinition` / `ComponentDefinitionData` /
7
+ * `BuildComponentDefinitionOptions` / `BuilderFailure`)は **0.8.0 で完全撤去**した
8
+ * (scp-only-authoring-contract.md §SA6・§7 step 3)。生 IR を手組みして登録する経路(route B)は
9
+ * フラットな意味グラフを壊す矛盾の根源であり、IR の唯一の正規生産者を compile seam
10
+ * (`compileBehaviors`)に一本化するための撤去である(§1.2)。**internal 猶予も残さない**(§SA6)。
11
11
  *
12
- * - **抽出ロジック(model 合成グラフの導出)は consumer(graphddb / litedbmodel / …)が
13
- * 保持**する。builder closure を受け取らない(source scan も recorder も非適用)。
14
- * - **IR の形・検証・正規化の所有権は bc(C1)**: consumer は事前合成されたグラフを
15
- * **データとして登録**し({@link buildComponentDefinition})、検証済み・正準化済みの
16
- * `Component`(`components[]` エントリ)を受け取る。
12
+ * このモジュールが今も所有するのは、その **検証・正規化・plan 導出の共有 core**({@link buildComponentCore}
13
+ * と周辺ヘルパ)だけで、これは **`compileBehaviors`(authoring.ts)が lowering の末尾で委譲する
14
+ * internal な実装**である。**公開されるのは何も無い**(builder のシンボルは index.ts から export しない)。
17
15
  *
18
- * ## 受け入れるデータ(B-1)
19
- *
20
- * - 呼び出し側指定の **node id**(任意文字列 — consumer が意味論を持たせてよい。例:
21
- * graphddb の resultPath 由来 `"root"` / `"groups.items.group.permissions.items"`)。
22
- * - body ノード種 `componentRef`(`parent` / `bindField` / `relationKind` / `policy` 込み)/
23
- * `map`(v2 語彙 `when` / `into` / `batched` 込み)/ `cond`。
24
- * - **動的 port 群**(`item.<field>` / `key.<attr>` / `changes.<field>` /
25
- * `condition.equals.<field>` 等の flatten キー): catalog エントリの
26
- * {@link CatalogEntry.additionalPorts} `: true`(opt-in)で受容する。既定は fail-closed
27
- * (未宣言 port は `UNKNOWN_PORT`)。**宣言済み port は additionalPorts に関わらず検査**
28
- * される(required / スカラリテラル型)。
29
- * - `inputPorts` / `output` / `plan`(省略時は依存から導出。`plan: null` は「plan なし」=
30
- * runBehavior の逐次 fallback — graphddb transaction 形)。
31
- *
32
- * ## 検証・正規化(B-2)
16
+ * ## core が担う検証・正規化(`buildComponentCore` — recorder 経路が委譲)
33
17
  *
34
18
  * 1. **構造検証**: ノード種・許可キー集合(未知キーは fail-closed)・id 一意性・
35
19
  * id と input port 名の衝突禁止(runBehavior の flat scope が曖昧になる)・parent 実在・
36
20
  * 依存の非循環・**参照 root の実在**(`{ref:[root,…]}` の root は body ノード id /
37
21
  * 宣言 input port / その map 自身の `as` 束縛のみ — fail-closed `UNKNOWN_REF`)。
38
- * 2. **catalog 照合**: エントリ実在・`portableToIR`・未宣言 port(additionalPorts
39
- * ポリシー)・required port・宣言 port のスカラリテラル型検査。
40
- * 3. **plan**: 省略時は依存から導出(recorder 経路と同一の Kahn levels)。指定時は
41
- * 被覆(各 index がちょうど 1 stage)と依存順序(dep の stage < node の stage)を検証。
22
+ * 2. **catalog 照合**(recorder 経路は record 時に共有関数で照合済みのため core には catalog を
23
+ * 渡さないが、共有関数 {@link checkPortableEntry} / {@link checkPortNames} /
24
+ * {@link checkDeclaredPortTypes} は当モジュールが保持する)。
25
+ * 3. **plan**: 省略時は依存から導出(Kahn levels)。指定時は被覆(各 index がちょうど 1 stage)と
26
+ * 依存順序(dep の stage < node の stage)を検証。
42
27
  * 4. **決定的正規化**: 全 object キーを code-point 順にソートして再構築(配列順序は保持)。
43
28
  * **構造的に同じ入力データ(キー挿入順が違っても)→ byte 同一の出力**(consumer の
44
29
  * golden 規律を壊さない)。入力データは変異しない。
45
30
  * 5. `assertPortableComponentGraph` を内部適用(fail-closed の自己検査、P0-2 Guard)。
46
31
  *
47
- * ## recorder 経路との単一実装(B-3)
32
+ * ## recorder 経路との単一実装
48
33
  *
49
34
  * `compileBehaviors` は lowering の末尾(Component 組み立て・plan 導出・正規化・Guard)を
50
35
  * {@link buildComponentCore} に委譲し、catalog port 照合は record 時に
51
36
  * {@link checkPortableEntry} / {@link checkPortNames} / {@link checkDeclaredPortTypes} を
52
- * 共有する。2 経路(recorder / データ登録)は同じ検証・正規化を通る。
37
+ * 共有する。これらの内部関数の検証・正規化は `compileBehaviors` 経由で網羅的に行使される。
53
38
  */
54
39
  import type { BodyNode, Catalog, CatalogEntry, Component, PortSchema } from "./behavior.js";
55
40
  import type { ExecutionPlanSpec } from "./plan.js";
56
- export type BuilderFailureCode = "INVALID_DEFINITION" | "INVALID_NODE" | "DUPLICATE_NODE_ID" | "UNKNOWN_PARENT" | "DEPENDENCY_CYCLE" | "UNKNOWN_COMPONENT" | "NOT_PORTABLE_COMPONENT" | "UNKNOWN_PORT" | "MISSING_PORT" | "PORT_TYPE_MISMATCH" | "UNKNOWN_REF" | "INVALID_PLAN";
57
- export declare class BuilderFailure extends Error {
58
- code: BuilderFailureCode;
59
- constructor(code: BuilderFailureCode, message: string);
60
- }
61
41
  /** 共有 port 照合が投げる失敗コード(AuthoringFailureCode / BuilderFailureCode の共通部分)。 */
62
42
  export type PortCheckFailCode = "NOT_PORTABLE_COMPONENT" | "UNKNOWN_PORT" | "MISSING_PORT" | "PORT_TYPE_MISMATCH";
63
43
  export type PortCheckFail = (code: PortCheckFailCode, message: string) => never;
@@ -83,13 +63,16 @@ export declare function checkDeclaredPortTypes(name: string, entry: CatalogEntry
83
63
  export declare function collectNodeIdRefs(node: unknown, ids: ReadonlySet<string>, out?: string[]): string[];
84
64
  export declare const DEFAULT_PLAN_CONCURRENCY = 16;
85
65
  /**
86
- * buildComponentDefinition が受け取る事前合成グラフデータ。フィールドは canonical
87
- * `Component`(behavior.ts / scp-ir-architecture.md §5)と同形で、`plan` だけ 3 値:
66
+ * {@link buildComponentCore} が受け取る事前合成グラフデータの形(internal)。フィールドは
67
+ * canonical `Component`(behavior.ts / scp-ir-architecture.md §5)と同形で、`plan` だけ 3 値:
88
68
  * - 省略(undefined) → 依存から導出(Kahn levels・`concurrency` オプション)。
89
69
  * - `null` → plan なし(runBehavior の逐次 fallback。graphddb transaction 形)。
90
70
  * - `ExecutionPlanSpec` → 供給値を検証して使う(graphddb query 形)。
71
+ *
72
+ * かつては公開データ登録 IF(`buildComponentDefinition`)の引数型 `ComponentDefinitionData` だったが、
73
+ * その IF もろとも 0.8.0 で撤去した(§SA6)。`compileBehaviors` が core に渡す internal な形として残す。
91
74
  */
92
- export interface ComponentDefinitionData {
75
+ interface ComponentDefinitionData {
93
76
  name: string;
94
77
  /** 省略時は `{}`(Input Port なし)。 */
95
78
  inputPorts?: Record<string, PortSchema>;
@@ -98,57 +81,16 @@ export interface ComponentDefinitionData {
98
81
  output: unknown;
99
82
  plan?: ExecutionPlanSpec | null;
100
83
  }
101
- export interface BuildComponentDefinitionOptions {
102
- /**
103
- * 照合する catalog(必須 — fail-closed: catalog なしに component 参照は検証できない)。
104
- * 動的 port 群を受けるエントリは `additionalPorts: true` を宣言する。
105
- */
106
- catalog: Catalog;
107
- /** plan 導出時の concurrency(既定 16)。供給 plan には影響しない。 */
108
- concurrency?: number;
109
- }
110
84
  /**
111
- * buildComponentCore — 検証・plan 導出・正規化・Guard の単一実装。
112
- * `buildComponentDefinition`(データ登録 IF、catalog 必須)と `compileBehaviors`
113
- * (recorder 経路 catalog port 照合は record 時に共有関数で済ませているため
114
- * `catalog` なし)の両方がこの core を通る。
85
+ * buildComponentCore — 検証・plan 導出・正規化・Guard の単一実装(internal)。
86
+ * `compileBehaviors`(recorder 経路 — catalog port 照合は record 時に共有関数で済ませているため
87
+ * `catalog` なし)が lowering の末尾でこの core を通る。#127/A5 まで存在した公開データ登録 IF
88
+ * `buildComponentDefinition`(catalog 必須で core を呼んでいた)は撤去済みで、`opts.catalog` を
89
+ * 渡す呼び出し口はもう無い(フィールドは互換のため残すが recorder 経路は常に undefined)。
115
90
  */
116
91
  export declare function buildComponentCore(data: ComponentDefinitionData, opts: {
117
92
  catalog?: Catalog;
118
93
  concurrency: number;
119
94
  }): Component;
120
- /**
121
- * buildComponentDefinition — 事前合成されたグラフ**データ**を受け取り、検証済み・正準化
122
- * 済みの `Component`(`components[]` エントリ)を返す(bc#26 のデータ登録 IF)。
123
- *
124
- * evaluation-mode recorder(`compileBehaviors`)とは独立: source scan は適用されず、
125
- * 抽出ロジック(model → グラフ導出)は consumer 側に残る。検証・正規化の規則は
126
- * モジュール docstring(B-2)を参照。
127
- *
128
- * ```ts
129
- * const component = buildComponentDefinition(
130
- * {
131
- * name: "ArticleWithTagsRead__get",
132
- * inputPorts: { articleId: { type: "string", required: true } },
133
- * body: [
134
- * { id: "root", component: "GetItem",
135
- * ports: { table: "T", PK: { concat: ["ARTICLE#", { ref: ["articleId"] }] }, SK: "META" } },
136
- * { id: "tags.items",
137
- * map: { over: { ref: ["root", "tagRefs"] }, as: "$tagId", component: "BatchGetItem",
138
- * ports: { table: "T", PK: { concat: ["TAG#", { ref: ["$tagId", "tagId"] }] } },
139
- * parent: "root", relationKind: "connection", batched: true } },
140
- * ],
141
- * output: { obj: { root: { ref: ["root"] }, tags: { ref: ["tags.items"] } } },
142
- * plan: { groups: [[0], [1]], concurrency: 16 },
143
- * },
144
- * { catalog },
145
- * );
146
- * // 実行: runBehavior({ irVersion: 1, exprVersion: SPEC_VERSIONS.expression,
147
- * // components: [component] }, handlers, input)
148
- * ```
149
- *
150
- * @throws {BuilderFailure} 検証違反(コードは {@link BuilderFailureCode})。
151
- * @throws {PortabilityError} Guard 自己検査(`assertPortableComponentGraph`)の違反。
152
- */
153
- export declare function buildComponentDefinition(data: ComponentDefinitionData, options: BuildComponentDefinitionOptions): Component;
95
+ export {};
154
96
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAK,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,EAIT,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAMnD,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,cAAc,GACd,mBAAmB,GACnB,gBAAgB,GAChB,kBAAkB,GAClB,mBAAmB,GACnB,wBAAwB,GACxB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,aAAa,GACb,cAAc,CAAC;AAEnB,qBAAa,cAAe,SAAQ,KAAK;IACvC,IAAI,EAAE,kBAAkB,CAAC;gBACb,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM;CAKtD;AAOD,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GACzB,wBAAwB,GACxB,cAAc,GACd,cAAc,GACd,oBAAoB,CAAC;AAEzB,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;AAEhF,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAM/F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,IAAI,EAAE,aAAa,GAClB,IAAI,CAaN;AAwBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,aAAa,GAClB,IAAI,CAYN;AAGD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAgBvG;AAuBD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAiE3C;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC1B,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAuPD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,uBAAuB,EAC7B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC/C,SAAS,CAkFX;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,uBAAuB,EAC7B,OAAO,EAAE,+BAA+B,GACvC,SAAS,CASX"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,EAKT,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAqCnD,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GACzB,wBAAwB,GACxB,cAAc,GACd,cAAc,GACd,oBAAoB,CAAC;AAEzB,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;AAEhF,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAM/F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,IAAI,EAAE,aAAa,GAClB,IAAI,CAaN;AAwBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,aAAa,GAClB,IAAI,CAYN;AAGD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAgBvG;AA2BD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAiE3C;;;;;;;;;GASG;AACH,UAAU,uBAAuB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC1B,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACjC;AAmUD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,uBAAuB,EAC7B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC/C,SAAS,CAoFX"}
package/dist/builder.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { cmpCodePoints, FORBIDDEN_OBJECT_KEY } from "./expr-eval.js";
2
2
  import { SPEC_VERSIONS } from "./index.js";
3
- import { assertPortableComponentGraph, assertPortableTypeNotation } from "./guard.js";
4
- export class BuilderFailure extends Error {
3
+ import { assertPortableComponentGraph, assertPortableTypeNotation, PortabilityError } from "./guard.js";
4
+ class BuilderFailure extends Error {
5
5
  code;
6
6
  constructor(code, message) {
7
7
  super(message);
@@ -102,7 +102,13 @@ export function collectNodeIdRefs(node, ids, out = []) {
102
102
  /** body ノードの依存 id 集合(ports/over/when/cond 式の ref root + parent。`ids` 内のみ)。 */
103
103
  function bodyNodeDeps(n, ids) {
104
104
  const deps = [];
105
- if ("map" in n) {
105
+ if ("fanout" in n) {
106
+ collectNodeIdRefs(n.fanout.over, ids, deps);
107
+ collectNodeIdRefs(n.fanout.ports, ids, deps);
108
+ if (n.fanout.parent !== undefined && ids.has(n.fanout.parent) && !deps.includes(n.fanout.parent))
109
+ deps.push(n.fanout.parent);
110
+ }
111
+ else if ("map" in n) {
106
112
  collectNodeIdRefs(n.map.over, ids, deps);
107
113
  collectNodeIdRefs(n.map.ports, ids, deps);
108
114
  if (n.map.when !== undefined)
@@ -189,11 +195,19 @@ function canonicalizeDeep(v) {
189
195
  }
190
196
  // ── 内部: 構造検証ヘルパ ───────────────────────────────────────────────────────────
191
197
  const DEFINITION_KEYS = /* @__PURE__ */ new Set(["name", "inputPorts", "body", "output", "plan"]);
192
- const REF_NODE_KEYS = /* @__PURE__ */ new Set(["id", "component", "ports", "parent", "bindField", "relationKind", "policy"]);
193
- const MAP_NODE_KEYS = /* @__PURE__ */ new Set(["id", "map"]);
198
+ // node-level outType additive・省略可の確定型注記(B0 / bc#44・§5.2)。node ラッパ層に載る
199
+ // (map/cond/fanout id+ラッパ+outType、componentRef inline なので REF_NODE_KEYS に含める)。
200
+ // 定義レベル `Component.outputType`(behavior.ts:255)は Phase A では buildComponentCore が
201
+ // 生成も検証もしないため DEFINITION_KEYS に含めない(含めると accept-and-drop の fail-open に
202
+ // なる)。将来ここで設定するなら assertPortableTypeNotation で検証し assembled component へ
203
+ // 持ち回ること。それまでは未知キーとして loud-reject する。
204
+ const REF_NODE_KEYS = /* @__PURE__ */ new Set(["id", "component", "ports", "parent", "bindField", "relationKind", "policy", "outType"]);
205
+ const MAP_NODE_KEYS = /* @__PURE__ */ new Set(["id", "map", "outType"]);
194
206
  const MAP_KEYS = /* @__PURE__ */ new Set(["over", "as", "component", "ports", "when", "into", "batched", "parent", "relationKind", "policy"]);
195
- const COND_NODE_KEYS = /* @__PURE__ */ new Set(["id", "cond"]);
207
+ const COND_NODE_KEYS = /* @__PURE__ */ new Set(["id", "cond", "outType"]);
196
208
  const COND_KEYS = /* @__PURE__ */ new Set(["if", "then", "else", "parent"]);
209
+ const FANOUT_NODE_KEYS = /* @__PURE__ */ new Set(["id", "fanout", "outType"]);
210
+ const FANOUT_KEYS = /* @__PURE__ */ new Set(["over", "as", "component", "ports", "dedupeKey", "drop", "implicitSource", "relationKind", "parent", "policy"]);
197
211
  const SCHEMA_KEYS = /* @__PURE__ */ new Set(["type", "required", "elemType"]);
198
212
  const PLAN_KEYS = /* @__PURE__ */ new Set(["groups", "concurrency"]);
199
213
  const RELATION_KINDS = /* @__PURE__ */ new Set(["single", "connection"]);
@@ -244,6 +258,24 @@ function checkPortsShape(ports, at) {
244
258
  bfail("INVALID_NODE", `${at}: port name "${FORBIDDEN_OBJECT_KEY}" is forbidden (fail-closed)`);
245
259
  return ports;
246
260
  }
261
+ /**
262
+ * node ラッパの `outType`(確定型注記・§5.2)を正規化ノードへ写す。additive・省略可
263
+ * (`.as<T>()` 由来 / consumer 注記)。存在時のみ可搬型記法の閉集合を fail-closed で検証する
264
+ * (後段の guard と同一強度。ここで先に落とすことで INVALID_NODE ではなく型記法エラーで拒否)。
265
+ */
266
+ function copyOutType(rec, node, at) {
267
+ if (rec.outType === undefined)
268
+ return;
269
+ try {
270
+ assertPortableTypeNotation(rec.outType, `${at}.outType`);
271
+ }
272
+ catch (e) {
273
+ if (e instanceof PortabilityError)
274
+ bfail("INVALID_NODE", e.message);
275
+ throw e;
276
+ }
277
+ node.outType = rec.outType;
278
+ }
247
279
  /** 1 body ノードを構造検証し、既知キーだけの正規化コピーを返す(undefined 値の optional は落とす)。 */
248
280
  function normalizeNode(raw, ids, at) {
249
281
  const rec = raw; // pass 1 で plain object / id は検証済み
@@ -281,6 +313,57 @@ function normalizeNode(raw, ids, at) {
281
313
  node.map.relationKind = wire.relationKind;
282
314
  if (wire.policy !== undefined)
283
315
  node.map.policy = wire.policy;
316
+ copyOutType(rec, node, at);
317
+ return node;
318
+ }
319
+ if (rec.fanout !== undefined) {
320
+ checkAllowedKeys(rec, FANOUT_NODE_KEYS, "INVALID_NODE", at);
321
+ if (!isPlainObject(rec.fanout))
322
+ bfail("INVALID_NODE", `${at}: 'fanout' must be a plain object`);
323
+ const fo = rec.fanout;
324
+ checkAllowedKeys(fo, FANOUT_KEYS, "INVALID_NODE", `${at}.fanout`);
325
+ if (fo.over === undefined)
326
+ bfail("INVALID_NODE", `${at}.fanout: 'over' is required`);
327
+ const as = checkScopeKeyName(fo.as, "'as'", `${at}.fanout`);
328
+ if (!as.startsWith("$") || as.length < 2)
329
+ bfail("INVALID_NODE", `${at}.fanout: 'as' must be a "$"-prefixed element binding (e.g. "$ref"), got '${as}'`);
330
+ if (ids.has(as))
331
+ bfail("INVALID_NODE", `${at}.fanout: 'as' binding '${as}' collides with a body node id`);
332
+ if (typeof fo.component !== "string" || fo.component === "")
333
+ bfail("INVALID_NODE", `${at}.fanout: 'component' must be a non-empty catalog name string`);
334
+ const fports = checkPortsShape(fo.ports, `${at}.fanout`);
335
+ const dedupeKey = checkScopeKeyName(fo.dedupeKey, "'dedupeKey'", `${at}.fanout`);
336
+ if (fo.drop !== "dangling" && fo.drop !== "none")
337
+ bfail("INVALID_NODE", `${at}.fanout: 'drop' must be "dangling" or "none"`);
338
+ if (fo.relationKind !== "connection")
339
+ bfail("INVALID_NODE", `${at}.fanout: 'relationKind' must be "connection"`);
340
+ const node = {
341
+ id,
342
+ fanout: {
343
+ over: fo.over,
344
+ as,
345
+ component: fo.component,
346
+ ports: fports,
347
+ dedupeKey,
348
+ drop: fo.drop,
349
+ relationKind: "connection",
350
+ },
351
+ };
352
+ if (fo.implicitSource !== undefined)
353
+ node.fanout.implicitSource = checkScopeKeyName(fo.implicitSource, "'implicitSource'", `${at}.fanout`);
354
+ if (fo.parent !== undefined) {
355
+ if (typeof fo.parent !== "string")
356
+ bfail("INVALID_NODE", `${at}.fanout: 'parent' must be a node id string`);
357
+ if (!ids.has(fo.parent))
358
+ bfail("UNKNOWN_PARENT", `${at}.fanout: parent '${fo.parent}' is not a body node id`);
359
+ node.fanout.parent = fo.parent;
360
+ }
361
+ if (fo.policy !== undefined) {
362
+ if (typeof fo.policy !== "string" || !POLICY_KINDS.has(fo.policy))
363
+ bfail("INVALID_NODE", `${at}.fanout: 'policy' must be "fail", "retry" or "continue"`);
364
+ node.fanout.policy = fo.policy;
365
+ }
366
+ copyOutType(rec, node, at);
284
367
  return node;
285
368
  }
286
369
  if (rec.cond !== undefined) {
@@ -301,6 +384,7 @@ function normalizeNode(raw, ids, at) {
301
384
  const wire = checkNodeWire(c, ids, `${at}.cond`);
302
385
  if (wire.parent !== undefined)
303
386
  node.cond.parent = wire.parent;
387
+ copyOutType(rec, node, at);
304
388
  return node;
305
389
  }
306
390
  if (rec.component !== undefined) {
@@ -321,6 +405,7 @@ function normalizeNode(raw, ids, at) {
321
405
  node.relationKind = wire.relationKind;
322
406
  if (wire.policy !== undefined)
323
407
  node.policy = wire.policy;
408
+ copyOutType(rec, node, at);
324
409
  return node;
325
410
  }
326
411
  bfail("INVALID_NODE", `${at}: body node must be componentRef ('component'), 'map' or 'cond'`);
@@ -380,7 +465,13 @@ function nodeRefsAndDeps(n, ids, inputs, at) {
380
465
  deps.push(d);
381
466
  },
382
467
  };
383
- if ("map" in n) {
468
+ if ("fanout" in n) {
469
+ walkRefs(n.fanout.over, null, ctx, `${at}.fanout.over`); // over は束縛の外で評価される
470
+ walkRefs(n.fanout.ports, n.fanout.as, ctx, `${at}.fanout.ports`);
471
+ if (n.fanout.parent !== undefined && !deps.includes(n.fanout.parent))
472
+ deps.push(n.fanout.parent);
473
+ }
474
+ else if ("map" in n) {
384
475
  walkRefs(n.map.over, null, ctx, `${at}.map.over`); // over は束縛の外で評価される
385
476
  walkRefs(n.map.ports, n.map.as, ctx, `${at}.map.ports`);
386
477
  if (n.map.when !== undefined)
@@ -439,12 +530,13 @@ function validateSuppliedPlan(raw, body, depsByIndex) {
439
530
  });
440
531
  return { groups: groups.map((stage) => [...stage]), concurrency };
441
532
  }
442
- // ── core(recorder 経路と共有される単一実装 B-3)────────────────────────────────────
533
+ // ── core(compileBehaviors が委譲する internal な単一実装)──────────────────────────────
443
534
  /**
444
- * buildComponentCore — 検証・plan 導出・正規化・Guard の単一実装。
445
- * `buildComponentDefinition`(データ登録 IF、catalog 必須)と `compileBehaviors`
446
- * (recorder 経路 catalog port 照合は record 時に共有関数で済ませているため
447
- * `catalog` なし)の両方がこの core を通る。
535
+ * buildComponentCore — 検証・plan 導出・正規化・Guard の単一実装(internal)。
536
+ * `compileBehaviors`(recorder 経路 — catalog port 照合は record 時に共有関数で済ませているため
537
+ * `catalog` なし)が lowering の末尾でこの core を通る。#127/A5 まで存在した公開データ登録 IF
538
+ * `buildComponentDefinition`(catalog 必須で core を呼んでいた)は撤去済みで、`opts.catalog` を
539
+ * 渡す呼び出し口はもう無い(フィールドは互換のため残すが recorder 経路は常に undefined)。
448
540
  */
449
541
  export function buildComponentCore(data, opts) {
450
542
  if (!isPlainObject(data))
@@ -498,14 +590,16 @@ export function buildComponentCore(data, opts) {
498
590
  });
499
591
  // body — pass 2: ノード構造の正規化コピー。
500
592
  const body = rec.body.map((raw, i) => normalizeNode(raw, ids, `body[${i}]`));
501
- // catalog 照合(データ登録経路。recorder 経路は record 時に同じ共有関数で照合済み)。
593
+ // catalog 照合(defensive)。#127/A5 でデータ登録経路が撤去されて以降、recorder 経路は record 時に
594
+ // 同じ共有関数(checkPortableEntry/checkPortNames/checkDeclaredPortTypes — authoring.ts)で照合済みの
595
+ // ため catalog は渡さない。将来 core を catalog 付きで呼ぶ internal 用途に備え、ゲートだけ残す。
502
596
  if (opts.catalog !== undefined) {
503
597
  const catalog = opts.catalog;
504
598
  body.forEach((n, i) => {
505
- const cname = "map" in n ? n.map.component : "cond" in n ? null : n.component;
599
+ const cname = "fanout" in n ? n.fanout.component : "map" in n ? n.map.component : "cond" in n ? null : n.component;
506
600
  if (cname === null)
507
601
  return;
508
- const ports = "map" in n ? n.map.ports : n.ports;
602
+ const ports = "fanout" in n ? n.fanout.ports : "map" in n ? n.map.ports : n.ports;
509
603
  if (!Object.hasOwn(catalog, cname))
510
604
  bfail("UNKNOWN_COMPONENT", `body[${i}]: component '${cname}' is not in the catalog (fail-closed)`);
511
605
  const entry = catalog[cname];
@@ -534,48 +628,9 @@ export function buildComponentCore(data, opts) {
534
628
  assertPortableComponentGraph({ irVersion: 1, exprVersion: SPEC_VERSIONS.expression, components: [component] });
535
629
  return component;
536
630
  }
537
- // ── データ登録 IF(B-1)──────────────────────────────────────────────────────────────
538
- /**
539
- * buildComponentDefinition 事前合成されたグラフ**データ**を受け取り、検証済み・正準化
540
- * 済みの `Component`(`components[]` エントリ)を返す(bc#26 のデータ登録 IF)。
541
- *
542
- * evaluation-mode recorder(`compileBehaviors`)とは独立: source scan は適用されず、
543
- * 抽出ロジック(model → グラフ導出)は consumer 側に残る。検証・正規化の規則は
544
- * モジュール docstring(B-2)を参照。
545
- *
546
- * ```ts
547
- * const component = buildComponentDefinition(
548
- * {
549
- * name: "ArticleWithTagsRead__get",
550
- * inputPorts: { articleId: { type: "string", required: true } },
551
- * body: [
552
- * { id: "root", component: "GetItem",
553
- * ports: { table: "T", PK: { concat: ["ARTICLE#", { ref: ["articleId"] }] }, SK: "META" } },
554
- * { id: "tags.items",
555
- * map: { over: { ref: ["root", "tagRefs"] }, as: "$tagId", component: "BatchGetItem",
556
- * ports: { table: "T", PK: { concat: ["TAG#", { ref: ["$tagId", "tagId"] }] } },
557
- * parent: "root", relationKind: "connection", batched: true } },
558
- * ],
559
- * output: { obj: { root: { ref: ["root"] }, tags: { ref: ["tags.items"] } } },
560
- * plan: { groups: [[0], [1]], concurrency: 16 },
561
- * },
562
- * { catalog },
563
- * );
564
- * // 実行: runBehavior({ irVersion: 1, exprVersion: SPEC_VERSIONS.expression,
565
- * // components: [component] }, handlers, input)
566
- * ```
567
- *
568
- * @throws {BuilderFailure} 検証違反(コードは {@link BuilderFailureCode})。
569
- * @throws {PortabilityError} Guard 自己検査(`assertPortableComponentGraph`)の違反。
570
- */
571
- export function buildComponentDefinition(data, options) {
572
- if (!isPlainObject(options) || !isPlainObject(options.catalog))
573
- bfail("INVALID_DEFINITION", "buildComponentDefinition requires options.catalog (fail-closed: component references cannot be validated without a catalog)");
574
- if (options.concurrency !== undefined && (!Number.isSafeInteger(options.concurrency) || options.concurrency < 1))
575
- bfail("INVALID_DEFINITION", "options.concurrency must be a positive integer");
576
- return buildComponentCore(data, {
577
- catalog: options.catalog,
578
- concurrency: options.concurrency ?? DEFAULT_PLAN_CONCURRENCY,
579
- });
580
- }
631
+ // ── データ登録 IF(route B)は撤去済み(#127/A5・§SA6)─────────────────────────────────
632
+ // 公開 `buildComponentDefinition(data, { catalog })` と引数型 `BuildComponentDefinitionOptions` は
633
+ // 0.8.0 で完全撤去した。IR の唯一の正規生産者は compile seam(`compileBehaviors`)であり、生 IR を
634
+ // データとして登録する経路はもう存在しない。上の `buildComponentCore` は残るが internal 専用で、
635
+ // catalog 照合を含む完全な検証は record 時(authoring.ts の recordComponentCall)で行われる。
581
636
  //# sourceMappingURL=builder.js.map