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.
- package/README.md +0 -1
- package/dist/authoring.d.ts +215 -3
- package/dist/authoring.d.ts.map +1 -1
- package/dist/authoring.js +338 -8
- package/dist/authoring.js.map +1 -1
- package/dist/behavior.d.ts +101 -3
- package/dist/behavior.d.ts.map +1 -1
- package/dist/behavior.js +126 -4
- package/dist/behavior.js.map +1 -1
- package/dist/bind.d.ts +64 -0
- package/dist/bind.d.ts.map +1 -0
- package/dist/bind.js +131 -0
- package/dist/bind.js.map +1 -0
- package/dist/builder.d.ts +30 -88
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +114 -59
- package/dist/builder.js.map +1 -1
- package/dist/expr-operator-types.d.ts +89 -0
- package/dist/expr-operator-types.d.ts.map +1 -0
- package/dist/expr-operator-types.js +134 -0
- package/dist/expr-operator-types.js.map +1 -0
- package/dist/generator/async-plan.d.ts.map +1 -1
- package/dist/generator/async-plan.js +4 -0
- package/dist/generator/async-plan.js.map +1 -1
- package/dist/generator/core.d.ts +15 -0
- package/dist/generator/core.d.ts.map +1 -1
- package/dist/generator/core.js +2 -0
- package/dist/generator/core.js.map +1 -1
- package/dist/generator/emit-php.d.ts.map +1 -1
- package/dist/generator/emit-php.js +1 -0
- package/dist/generator/emit-php.js.map +1 -1
- package/dist/generator/emit-python.d.ts.map +1 -1
- package/dist/generator/emit-python.js +1 -0
- package/dist/generator/emit-python.js.map +1 -1
- package/dist/generator/emit-straightline-typed-typescript.d.ts.map +1 -1
- package/dist/generator/emit-straightline-typed-typescript.js +1 -0
- package/dist/generator/emit-straightline-typed-typescript.js.map +1 -1
- package/dist/generator/emit-typed-native-go.d.ts.map +1 -1
- package/dist/generator/emit-typed-native-go.js +364 -14
- package/dist/generator/emit-typed-native-go.js.map +1 -1
- package/dist/generator/emit-typed-native-rust.d.ts.map +1 -1
- package/dist/generator/emit-typed-native-rust.js +347 -21
- package/dist/generator/emit-typed-native-rust.js.map +1 -1
- package/dist/generator/emit-typescript.d.ts.map +1 -1
- package/dist/generator/emit-typescript.js +24 -8
- package/dist/generator/emit-typescript.js.map +1 -1
- package/dist/generator/index.d.ts +1 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +7 -5
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/straightline.d.ts.map +1 -1
- package/dist/generator/straightline.js +21 -1
- package/dist/generator/straightline.js.map +1 -1
- package/dist/guard.js +20 -1
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +42 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -7
- package/dist/index.js.map +1 -1
- package/dist/provenance.d.ts +102 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +128 -0
- package/dist/provenance.js.map +1 -0
- package/dist/type-gate.d.ts +63 -0
- package/dist/type-gate.d.ts.map +1 -0
- package/dist/type-gate.js +295 -0
- package/dist/type-gate.js.map +1 -0
- 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
|
package/dist/bind.js.map
ADDED
|
@@ -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 —
|
|
3
|
-
* bc#26)+ recorder 経路(`compileBehaviors`)と共有する検証・正規化 core。
|
|
2
|
+
* builder.ts — component-graph の検証・正規化・plan 導出の **内部 core**。
|
|
4
3
|
*
|
|
5
|
-
* ##
|
|
4
|
+
* ## SCP-only 化(0.8.0・#127/A5)— データ登録 IF は撤去済み
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
-
* ##
|
|
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
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
* `
|
|
113
|
-
*
|
|
114
|
-
* `catalog
|
|
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
|
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA
|
|
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
|
-
|
|
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 ("
|
|
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
|
-
|
|
193
|
-
|
|
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 ("
|
|
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(
|
|
533
|
+
// ── core(compileBehaviors が委譲する internal な単一実装)──────────────────────────────
|
|
443
534
|
/**
|
|
444
|
-
* buildComponentCore — 検証・plan 導出・正規化・Guard
|
|
445
|
-
* `
|
|
446
|
-
*
|
|
447
|
-
* `catalog
|
|
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
|
|
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
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|