@mawaru/sdk 0.5.0 → 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/_schemas/ai-manifest.test.ts +17 -2
- package/_schemas/ai-manifest.ts +9 -9
- package/_schemas/extract-component.test.ts +220 -0
- package/_schemas/extract-component.ts +224 -0
- package/_schemas/graph.test.ts +201 -12
- package/_schemas/graph.ts +99 -10
- package/_schemas/node.ts +3 -0
- package/_schemas/port-spec.test.ts +190 -37
- package/_schemas/port-spec.ts +165 -58
- package/_schemas/program-manifest.ts +9 -8
- package/dist/_schemas/ai-manifest.js +6 -9
- package/dist/_schemas/extract-component.d.ts +35 -0
- package/dist/_schemas/extract-component.js +146 -0
- package/dist/_schemas/graph.d.ts +19 -0
- package/dist/_schemas/graph.js +82 -11
- package/dist/_schemas/node.d.ts +1 -0
- package/dist/_schemas/node.js +3 -0
- package/dist/_schemas/port-spec.d.ts +10 -2
- package/dist/_schemas/port-spec.js +136 -54
- package/dist/_schemas/program-manifest.js +6 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init.js +3 -0
- package/index.ts +1 -0
- package/package.json +1 -1
- package/templates/gitignore +4 -0
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
2
|
import type { GraphPort, JsonSchema } from "./node.js"
|
|
3
3
|
import {
|
|
4
|
-
ANY_SCHEMA,
|
|
5
4
|
dataFormatOf,
|
|
6
5
|
dataSchemaOf,
|
|
7
6
|
envelopeDataSchemaOf,
|
|
8
7
|
envelopeSchemaOf,
|
|
9
8
|
fileSchema,
|
|
9
|
+
hasInboundConflict,
|
|
10
|
+
instructionSchema,
|
|
10
11
|
isAiEnvelopeSchema,
|
|
11
12
|
isFileSchema,
|
|
12
13
|
isSchemaSubset,
|
|
13
14
|
kindPortsViolation,
|
|
15
|
+
mergeInboundSchemas,
|
|
14
16
|
propagateDerivedPorts,
|
|
15
17
|
resolvePortSchemas,
|
|
18
|
+
schemaFit,
|
|
19
|
+
UNDECLARED_SCHEMA,
|
|
16
20
|
} from "./port-spec.js"
|
|
17
21
|
|
|
18
22
|
let seq = 0
|
|
@@ -27,7 +31,7 @@ const port = (
|
|
|
27
31
|
io,
|
|
28
32
|
key,
|
|
29
33
|
name: key,
|
|
30
|
-
schema: schema ??
|
|
34
|
+
schema: schema ?? UNDECLARED_SCHEMA,
|
|
31
35
|
})
|
|
32
36
|
|
|
33
37
|
describe("データフォーマットカタログ(S)", () => {
|
|
@@ -107,7 +111,7 @@ describe("封筒型 { data: T, description: string }", () => {
|
|
|
107
111
|
}),
|
|
108
112
|
).toBeNull()
|
|
109
113
|
expect(envelopeDataSchemaOf({ type: "string" })).toBeNull()
|
|
110
|
-
expect(envelopeDataSchemaOf(
|
|
114
|
+
expect(envelopeDataSchemaOf(UNDECLARED_SCHEMA)).toBeNull()
|
|
111
115
|
})
|
|
112
116
|
|
|
113
117
|
it("isAiEnvelopeSchema:data と description: string の両方が必須のときだけ true", () => {
|
|
@@ -141,28 +145,33 @@ describe("封筒型 { data: T, description: string }", () => {
|
|
|
141
145
|
required: ["data", "description"],
|
|
142
146
|
}),
|
|
143
147
|
).toBe(false)
|
|
144
|
-
expect(isAiEnvelopeSchema(
|
|
148
|
+
expect(isAiEnvelopeSchema(UNDECLARED_SCHEMA)).toBe(false)
|
|
145
149
|
})
|
|
146
150
|
})
|
|
147
151
|
|
|
148
152
|
describe("kindPortsViolation(kind 別ポート構成)", () => {
|
|
149
|
-
it("ai は out の key 自由・複数可。in
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
).toBeNull()
|
|
153
|
-
expect(
|
|
154
|
-
kindPortsViolation("ai", [port("in", "in"), port("out", "draft")]),
|
|
155
|
-
).toBeNull()
|
|
153
|
+
it("ai は out の key 自由・複数可。in は in / instruction の2口固定(AI の複数出口)", () => {
|
|
154
|
+
const ins = [port("in", "in"), port("in", "instruction")]
|
|
155
|
+
expect(kindPortsViolation("ai", [...ins, port("out", "main")])).toBeNull()
|
|
156
|
+
expect(kindPortsViolation("ai", [...ins, port("out", "draft")])).toBeNull()
|
|
156
157
|
expect(
|
|
157
158
|
kindPortsViolation("ai", [
|
|
158
|
-
|
|
159
|
+
...ins,
|
|
159
160
|
port("out", "spam"),
|
|
160
161
|
port("out", "sales"),
|
|
161
162
|
port("out", "support"),
|
|
162
163
|
]),
|
|
163
164
|
).toBeNull()
|
|
165
|
+
// instruction が無い・key が違うのは違反
|
|
166
|
+
expect(
|
|
167
|
+
kindPortsViolation("ai", [port("in", "in"), port("out", "main")]),
|
|
168
|
+
).toMatch(/instruction/)
|
|
164
169
|
expect(
|
|
165
|
-
kindPortsViolation("ai", [
|
|
170
|
+
kindPortsViolation("ai", [
|
|
171
|
+
port("in", "input"),
|
|
172
|
+
port("in", "instruction"),
|
|
173
|
+
port("out", "main"),
|
|
174
|
+
]),
|
|
166
175
|
).toMatch(/ai/)
|
|
167
176
|
})
|
|
168
177
|
|
|
@@ -210,9 +219,13 @@ describe("kindPortsViolation(kind 別ポート構成)", () => {
|
|
|
210
219
|
describe("型伝播(導出ポートの解決)", () => {
|
|
211
220
|
const envelope = envelopeSchemaOf({ type: "string" })
|
|
212
221
|
|
|
213
|
-
it("ai → human 直結:approve は封筒を剥がした data 型、reject
|
|
222
|
+
it("ai → human 直結:approve は封筒を剥がした data 型、reject は指示エンベロープ", () => {
|
|
214
223
|
const aiOut = port("out", "main", envelope)
|
|
215
|
-
const ai = {
|
|
224
|
+
const ai = {
|
|
225
|
+
id: uuid(),
|
|
226
|
+
kind: "ai",
|
|
227
|
+
ports: [port("in", "in"), port("in", "instruction"), aiOut],
|
|
228
|
+
}
|
|
216
229
|
const humanIn = port("in", "in")
|
|
217
230
|
const approve = port("out", "approve")
|
|
218
231
|
const reject = port("out", "reject")
|
|
@@ -227,10 +240,10 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
227
240
|
)
|
|
228
241
|
expect(resolved.get(humanIn.id)).toEqual(envelope)
|
|
229
242
|
expect(resolved.get(approve.id)).toEqual({ type: "string" })
|
|
230
|
-
expect(resolved.get(reject.id)).toEqual(
|
|
243
|
+
expect(resolved.get(reject.id)).toEqual(instructionSchema())
|
|
231
244
|
})
|
|
232
245
|
|
|
233
|
-
it("ai → wait → human:素通しで封筒型が伝播し、reject
|
|
246
|
+
it("ai → wait → human:素通しで封筒型が伝播し、reject は指示エンベロープのまま", () => {
|
|
234
247
|
// ai の in は宣言型(nodes/ai/<dir>/config.json の inputs.in 由来。実行repoの構造見直し)
|
|
235
248
|
const aiInSchema = {
|
|
236
249
|
type: "object",
|
|
@@ -244,7 +257,11 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
244
257
|
const humanIn = port("in", "in")
|
|
245
258
|
const approve = port("out", "approve")
|
|
246
259
|
const reject = port("out", "reject")
|
|
247
|
-
const ai = {
|
|
260
|
+
const ai = {
|
|
261
|
+
id: uuid(),
|
|
262
|
+
kind: "ai",
|
|
263
|
+
ports: [aiIn, port("in", "instruction"), aiOut],
|
|
264
|
+
}
|
|
248
265
|
const wait = { id: uuid(), kind: "wait", ports: [waitIn, waitOut] }
|
|
249
266
|
const human = {
|
|
250
267
|
id: uuid(),
|
|
@@ -261,7 +278,7 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
261
278
|
expect(resolved.get(humanIn.id)).toEqual(envelope)
|
|
262
279
|
// 封筒剥がしは「in の直接の接続元が AI ノード」のときだけ。wait 越しは素通し
|
|
263
280
|
expect(resolved.get(approve.id)).toEqual(envelope)
|
|
264
|
-
expect(resolved.get(reject.id)).toEqual(
|
|
281
|
+
expect(resolved.get(reject.id)).toEqual(instructionSchema())
|
|
265
282
|
// ai の in は宣言型がそのまま残る(const 扱いは廃止)
|
|
266
283
|
expect(resolved.get(aiIn.id)).toEqual(aiInSchema)
|
|
267
284
|
})
|
|
@@ -281,8 +298,8 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
281
298
|
{ from_port_id: out2.id, to_port_id: in1.id },
|
|
282
299
|
],
|
|
283
300
|
)
|
|
284
|
-
expect(resolved.get(out1.id)).toEqual(
|
|
285
|
-
expect(resolved.get(out2.id)).toEqual(
|
|
301
|
+
expect(resolved.get(out1.id)).toEqual(UNDECLARED_SCHEMA)
|
|
302
|
+
expect(resolved.get(out2.id)).toEqual(UNDECLARED_SCHEMA)
|
|
286
303
|
})
|
|
287
304
|
|
|
288
305
|
it("program の in は接続鏡映(sticky):具体型の接続元だけ上書きする", () => {
|
|
@@ -292,7 +309,11 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
292
309
|
required: ["inquiry"],
|
|
293
310
|
}
|
|
294
311
|
const aiOut = port("out", "main", envelope)
|
|
295
|
-
const ai = {
|
|
312
|
+
const ai = {
|
|
313
|
+
id: uuid(),
|
|
314
|
+
kind: "ai",
|
|
315
|
+
ports: [port("in", "in"), port("in", "instruction"), aiOut],
|
|
316
|
+
}
|
|
296
317
|
const progIn = port("in", "in", declared)
|
|
297
318
|
const program = {
|
|
298
319
|
id: uuid(),
|
|
@@ -318,7 +339,11 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
318
339
|
ports: [port("in", "in"), progOut],
|
|
319
340
|
}
|
|
320
341
|
const aiIn = port("in", "in", declared)
|
|
321
|
-
const ai = {
|
|
342
|
+
const ai = {
|
|
343
|
+
id: uuid(),
|
|
344
|
+
kind: "ai",
|
|
345
|
+
ports: [aiIn, port("in", "instruction"), port("out", "main")],
|
|
346
|
+
}
|
|
322
347
|
// 具体型の接続元 → 上書き(接続両端の同値同期)
|
|
323
348
|
const resolved = resolvePortSchemas(
|
|
324
349
|
[program, ai],
|
|
@@ -333,7 +358,11 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
333
358
|
ports: [port("in", "in"), waitOut],
|
|
334
359
|
}
|
|
335
360
|
const aiIn2 = port("in", "in", declared)
|
|
336
|
-
const ai2 = {
|
|
361
|
+
const ai2 = {
|
|
362
|
+
id: uuid(),
|
|
363
|
+
kind: "ai",
|
|
364
|
+
ports: [aiIn2, port("in", "instruction"), port("out", "main")],
|
|
365
|
+
}
|
|
337
366
|
const kept = resolvePortSchemas(
|
|
338
367
|
[wait, ai2],
|
|
339
368
|
[{ from_port_id: waitOut.id, to_port_id: aiIn2.id }],
|
|
@@ -373,7 +402,11 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
373
402
|
|
|
374
403
|
it("program の in:wait 越しでも具体型が鏡映される(固定点反復)", () => {
|
|
375
404
|
const aiOut = port("out", "main", envelope)
|
|
376
|
-
const ai = {
|
|
405
|
+
const ai = {
|
|
406
|
+
id: uuid(),
|
|
407
|
+
kind: "ai",
|
|
408
|
+
ports: [port("in", "in"), port("in", "instruction"), aiOut],
|
|
409
|
+
}
|
|
377
410
|
const waitIn = port("in", "in")
|
|
378
411
|
const waitOut = port("out", "main")
|
|
379
412
|
const wait = { id: uuid(), kind: "wait", ports: [waitIn, waitOut] }
|
|
@@ -437,8 +470,8 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
437
470
|
const outMain = port("out", "main", { type: "object" })
|
|
438
471
|
const start = { id: uuid(), kind: "start", ports: [inPort, outMain] }
|
|
439
472
|
const resolved = resolvePortSchemas([start], [])
|
|
440
|
-
expect(resolved.get(outMain.id)).toEqual(
|
|
441
|
-
expect(resolved.get(inPort.id)).toEqual(
|
|
473
|
+
expect(resolved.get(outMain.id)).toEqual(UNDECLARED_SCHEMA)
|
|
474
|
+
expect(resolved.get(inPort.id)).toEqual(UNDECLARED_SCHEMA)
|
|
442
475
|
})
|
|
443
476
|
|
|
444
477
|
it("start:fan-out で非 any が1種類(program+導出 wait)ならそれを採用し、wait へも流れる", () => {
|
|
@@ -483,8 +516,8 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
483
516
|
{ from_port_id: start.outMain.id, to_port_id: b.inPort.id },
|
|
484
517
|
],
|
|
485
518
|
)
|
|
486
|
-
expect(resolved.get(start.outMain.id)).toEqual(
|
|
487
|
-
expect(resolved.get(start.inPort.id)).toEqual(
|
|
519
|
+
expect(resolved.get(start.outMain.id)).toEqual(UNDECLARED_SCHEMA)
|
|
520
|
+
expect(resolved.get(start.inPort.id)).toEqual(UNDECLARED_SCHEMA)
|
|
488
521
|
})
|
|
489
522
|
|
|
490
523
|
it("start:宣言を持たない経路(start → wait → …)は any(パラメトリック閉路の割り切り)", () => {
|
|
@@ -496,8 +529,8 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
496
529
|
[start.node, wait],
|
|
497
530
|
[{ from_port_id: start.outMain.id, to_port_id: waitIn.id }],
|
|
498
531
|
)
|
|
499
|
-
expect(resolved.get(start.inPort.id)).toEqual(
|
|
500
|
-
expect(resolved.get(start.outMain.id)).toEqual(
|
|
532
|
+
expect(resolved.get(start.inPort.id)).toEqual(UNDECLARED_SCHEMA)
|
|
533
|
+
expect(resolved.get(start.outMain.id)).toEqual(UNDECLARED_SCHEMA)
|
|
501
534
|
})
|
|
502
535
|
|
|
503
536
|
it("propagateDerivedPorts:schema を書き戻し、変化のないノードは同じ参照を返す", () => {
|
|
@@ -522,8 +555,8 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
522
555
|
// 接続を消すと any に戻る
|
|
523
556
|
const cleared = propagateDerivedPorts(next, [])
|
|
524
557
|
expect(cleared[1]?.ports.map((p) => p.schema)).toEqual([
|
|
525
|
-
|
|
526
|
-
|
|
558
|
+
UNDECLARED_SCHEMA,
|
|
559
|
+
UNDECLARED_SCHEMA,
|
|
527
560
|
])
|
|
528
561
|
})
|
|
529
562
|
|
|
@@ -552,6 +585,73 @@ describe("型伝播(導出ポートの解決)", () => {
|
|
|
552
585
|
})
|
|
553
586
|
})
|
|
554
587
|
|
|
588
|
+
describe("型伝播:fan-in(同じ in ポートへの流入2本以上)", () => {
|
|
589
|
+
const str = { type: "string" } as const
|
|
590
|
+
const num = { type: "number" } as const
|
|
591
|
+
|
|
592
|
+
// program の out は宣言型なので、fan-in の元として具体型を持たせられる
|
|
593
|
+
const source = (schema: Record<string, unknown>) => {
|
|
594
|
+
const out = port("out", "main", schema)
|
|
595
|
+
return {
|
|
596
|
+
node: { id: uuid(), kind: "program", ports: [port("in", "in"), out] },
|
|
597
|
+
out,
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const sink = () => {
|
|
601
|
+
const inPort = port("in", "in")
|
|
602
|
+
const out = port("out", "main")
|
|
603
|
+
return {
|
|
604
|
+
node: { id: uuid(), kind: "wait", ports: [inPort, out] },
|
|
605
|
+
inPort,
|
|
606
|
+
out,
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
it("流入が全部同じ型ならその型が伝播する(従来は any に落ちていた)", () => {
|
|
611
|
+
const a = source(str)
|
|
612
|
+
const b = source(str)
|
|
613
|
+
const w = sink()
|
|
614
|
+
const resolved = resolvePortSchemas(
|
|
615
|
+
[a.node, b.node, w.node],
|
|
616
|
+
[
|
|
617
|
+
{ from_port_id: a.out.id, to_port_id: w.inPort.id },
|
|
618
|
+
{ from_port_id: b.out.id, to_port_id: w.inPort.id },
|
|
619
|
+
],
|
|
620
|
+
)
|
|
621
|
+
expect(resolved.get(w.inPort.id)).toEqual(str)
|
|
622
|
+
// 素通しなので下流にも伝わる
|
|
623
|
+
expect(resolved.get(w.out.id)).toEqual(str)
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
it("具体型1種類+未宣言 の混在は、その具体型が採用される", () => {
|
|
627
|
+
const a = source(str)
|
|
628
|
+
const b = sink() // out main は導出=未宣言
|
|
629
|
+
const w = sink()
|
|
630
|
+
const resolved = resolvePortSchemas(
|
|
631
|
+
[a.node, b.node, w.node],
|
|
632
|
+
[
|
|
633
|
+
{ from_port_id: a.out.id, to_port_id: w.inPort.id },
|
|
634
|
+
{ from_port_id: b.out.id, to_port_id: w.inPort.id },
|
|
635
|
+
],
|
|
636
|
+
)
|
|
637
|
+
expect(resolved.get(w.inPort.id)).toEqual(str)
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
it("型が割れているときは未宣言に落ちる(保存時に graph が弾く)", () => {
|
|
641
|
+
const a = source(str)
|
|
642
|
+
const b = source(num)
|
|
643
|
+
const w = sink()
|
|
644
|
+
const resolved = resolvePortSchemas(
|
|
645
|
+
[a.node, b.node, w.node],
|
|
646
|
+
[
|
|
647
|
+
{ from_port_id: a.out.id, to_port_id: w.inPort.id },
|
|
648
|
+
{ from_port_id: b.out.id, to_port_id: w.inPort.id },
|
|
649
|
+
],
|
|
650
|
+
)
|
|
651
|
+
expect(resolved.get(w.inPort.id)).toEqual(UNDECLARED_SCHEMA)
|
|
652
|
+
})
|
|
653
|
+
})
|
|
654
|
+
|
|
555
655
|
describe("file 型(nominal)", () => {
|
|
556
656
|
it("fileSchema はマーカー付き object、isFileSchema で識別できる", () => {
|
|
557
657
|
const schema = fileSchema()
|
|
@@ -579,8 +679,8 @@ describe("file 型(nominal)", () => {
|
|
|
579
679
|
// file ↔ file
|
|
580
680
|
expect(isSchemaSubset(file, fileSchema())).toBe(true)
|
|
581
681
|
// file → any / any → file
|
|
582
|
-
expect(isSchemaSubset(file,
|
|
583
|
-
expect(isSchemaSubset(
|
|
682
|
+
expect(isSchemaSubset(file, UNDECLARED_SCHEMA)).toBe(true)
|
|
683
|
+
expect(isSchemaSubset(UNDECLARED_SCHEMA, file)).toBe(true)
|
|
584
684
|
// 構造が同じだけの object は file に繋がない(nominal)
|
|
585
685
|
expect(isSchemaSubset(lookalike, file)).toBe(false)
|
|
586
686
|
expect(isSchemaSubset(file, lookalike)).toBe(false)
|
|
@@ -596,8 +696,8 @@ describe("file 型(nominal)", () => {
|
|
|
596
696
|
|
|
597
697
|
describe("isSchemaSubset(伝播型 ⊆ 宣言スキーマ)", () => {
|
|
598
698
|
it("any はどちら向きも許可", () => {
|
|
599
|
-
expect(isSchemaSubset(
|
|
600
|
-
expect(isSchemaSubset({ type: "object" },
|
|
699
|
+
expect(isSchemaSubset(UNDECLARED_SCHEMA, { type: "object" })).toBe(true)
|
|
700
|
+
expect(isSchemaSubset({ type: "object" }, UNDECLARED_SCHEMA)).toBe(true)
|
|
601
701
|
})
|
|
602
702
|
|
|
603
703
|
it("object:宣言側の required が伝播側に必須として存在し、型互換であること", () => {
|
|
@@ -655,3 +755,56 @@ describe("isSchemaSubset(伝播型 ⊆ 宣言スキーマ)", () => {
|
|
|
655
755
|
).toBe(false)
|
|
656
756
|
})
|
|
657
757
|
})
|
|
758
|
+
|
|
759
|
+
describe("schemaFit(3値の型判定)", () => {
|
|
760
|
+
const str = { type: "string" } as const
|
|
761
|
+
const num = { type: "number" } as const
|
|
762
|
+
|
|
763
|
+
it("具体型どうしは ok / mismatch を返す", () => {
|
|
764
|
+
expect(schemaFit(str, str)).toBe("ok")
|
|
765
|
+
expect(schemaFit(str, num)).toBe("mismatch")
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
it("どちらかが未宣言なら unknown(適合とも不適合とも言わない)", () => {
|
|
769
|
+
expect(schemaFit(UNDECLARED_SCHEMA, str)).toBe("unknown")
|
|
770
|
+
expect(schemaFit(str, UNDECLARED_SCHEMA)).toBe("unknown")
|
|
771
|
+
expect(schemaFit(UNDECLARED_SCHEMA, UNDECLARED_SCHEMA)).toBe("unknown")
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
it("isSchemaSubset は unknown を通す(保存を止めない)", () => {
|
|
775
|
+
expect(isSchemaSubset(UNDECLARED_SCHEMA, str)).toBe(true)
|
|
776
|
+
expect(isSchemaSubset(str, num)).toBe(false)
|
|
777
|
+
})
|
|
778
|
+
|
|
779
|
+
it("file は nominal(file 同士だけ ok)", () => {
|
|
780
|
+
expect(schemaFit(fileSchema(), fileSchema())).toBe("ok")
|
|
781
|
+
expect(schemaFit({ type: "object" }, fileSchema())).toBe("mismatch")
|
|
782
|
+
})
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
describe("mergeInboundSchemas / hasInboundConflict(fan-in)", () => {
|
|
786
|
+
const str = { type: "string" } as const
|
|
787
|
+
const num = { type: "number" } as const
|
|
788
|
+
|
|
789
|
+
it("流入が全部同じ具体型ならその型を採用する", () => {
|
|
790
|
+
expect(mergeInboundSchemas([str, str])).toEqual(str)
|
|
791
|
+
expect(hasInboundConflict([str, str])).toBe(false)
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
it("具体型1種類+未宣言 の混在は、その具体型を採用する", () => {
|
|
795
|
+
expect(mergeInboundSchemas([UNDECLARED_SCHEMA, str])).toEqual(str)
|
|
796
|
+
expect(hasInboundConflict([UNDECLARED_SCHEMA, str])).toBe(false)
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
it("具体型が2種類以上なら未宣言に落とし、衝突として検出する", () => {
|
|
800
|
+
expect(mergeInboundSchemas([str, num])).toEqual(UNDECLARED_SCHEMA)
|
|
801
|
+
expect(hasInboundConflict([str, num])).toBe(true)
|
|
802
|
+
})
|
|
803
|
+
|
|
804
|
+
it("すべて未宣言なら未宣言", () => {
|
|
805
|
+
expect(mergeInboundSchemas([UNDECLARED_SCHEMA, UNDECLARED_SCHEMA])).toEqual(
|
|
806
|
+
UNDECLARED_SCHEMA,
|
|
807
|
+
)
|
|
808
|
+
expect(hasInboundConflict([])).toBe(false)
|
|
809
|
+
})
|
|
810
|
+
})
|