@kiwa-test/component 0.2.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -531,4 +531,150 @@ declare const buildCard: ComponentRender<CardArgs>;
531
531
  /** 全 component の render function を 1 record にまとめる (test 一括登録用)。 */
532
532
  declare const componentFixtures: Record<string, ComponentRender<Record<string, unknown>>>;
533
533
 
534
- export { type A11yViolation, type ButtonArgs, type CanvasElement, type CardArgs, type ChromaticConfig, type ChromaticVisualMock, type ComponentLocator, type ComponentProvider, type ComponentRender, type FormArgs, type FormField, type InputArgs, type MockEvent, type MockNode, type ModalArgs, type NodeLocator, type NodeOptions, type PlayContext, type PlaywrightCTMock, type StoryEntry, type StoryMeta, type StoryMountResult, type StoryObj, type StoryParameters, type StoryPlayResult, type StoryRegistry, type VisualBaseline, type VisualDiff, type VisualReviewAction, type VisualReviewEntry, addHandler, appendChild, buildButton, buildCard, buildForm, buildInput, buildModal, componentFixtures, createCanvas, createChromaticVisualMock, createNode, createPlaywrightCTMock, createStoryRegistry, findByRole, findByText, fireEvent, hashMarkup, query, renderMarkup };
534
+ /**
535
+ * Advanced component semantics — target-neutral axis SSOT.
536
+ *
537
+ * The component package spans Storybook 8, Playwright Component Testing, and
538
+ * Chromatic. These helpers model the observable semantics without importing
539
+ * any of those runtimes, so the same axis can be replayed against each target.
540
+ */
541
+ type ComponentTarget = 'storybook8' | 'playwright-ct' | 'chromatic';
542
+ type ComponentAxis = 'rsc-harness' | 'streaming-ssr' | 'view-transitions' | 'form-action-advanced';
543
+ type NeutralEventName = 'rsc.render_started' | 'rsc.suspense_boundary' | 'rsc.html_chunk_streamed' | 'rsc.render_completed' | 'ssr.suspense_pending' | 'ssr.error_boundary_captured' | 'ssr.progressive_hydration_started' | 'ssr.selective_hydration_completed' | 'transition.element_started' | 'transition.element_finished' | 'transition.document_started' | 'transition.animation_asserted' | 'form.status_pending' | 'form.optimistic_applied' | 'form.progressive_enhanced' | 'form.action_resolved';
544
+ interface AxisStep<TState extends string> {
545
+ neutralEvent: NeutralEventName;
546
+ providerEvent: string;
547
+ state: TState;
548
+ amountCents: number;
549
+ metadata: Record<string, string | number | boolean>;
550
+ }
551
+ declare function providerEventName(target: ComponentTarget, neutral: NeutralEventName): string;
552
+
553
+ type RscHarnessState = 'idle' | 'rendering' | 'suspended' | 'streaming' | 'completed' | 'errored';
554
+ interface RscHarnessSession {
555
+ target: ComponentTarget;
556
+ componentId: string;
557
+ state: RscHarnessState;
558
+ chunks: string[];
559
+ suspenseFallback: string | null;
560
+ history: AxisStep<RscHarnessState>[];
561
+ error: string | null;
562
+ }
563
+ declare function startRscHarness(input: {
564
+ target: ComponentTarget;
565
+ componentId: string;
566
+ suspenseFallback?: string;
567
+ }): RscHarnessSession;
568
+ declare function beginRscRender(session: RscHarnessSession): AxisStep<RscHarnessState>;
569
+ declare function enterSuspenseBoundary(session: RscHarnessSession, fallback?: string): AxisStep<RscHarnessState>;
570
+ declare function streamHtmlChunk(session: RscHarnessSession, chunk: string): AxisStep<RscHarnessState>;
571
+ declare function completeRscRender(session: RscHarnessSession): AxisStep<RscHarnessState>;
572
+ declare function failRscRender(session: RscHarnessSession, error: Error | string): AxisStep<RscHarnessState>;
573
+
574
+ type StreamingSsrState = 'idle' | 'suspense-pending' | 'error-captured' | 'progressive-hydrating' | 'selective-hydrated';
575
+ interface StreamingSsrSession {
576
+ target: ComponentTarget;
577
+ routeId: string;
578
+ state: StreamingSsrState;
579
+ pendingBoundaries: Set<string>;
580
+ hydratedBoundaries: Set<string>;
581
+ errors: Array<{
582
+ boundaryId: string;
583
+ message: string;
584
+ }>;
585
+ history: AxisStep<StreamingSsrState>[];
586
+ }
587
+ declare function startStreamingSsr(input: {
588
+ target: ComponentTarget;
589
+ routeId: string;
590
+ }): StreamingSsrSession;
591
+ declare function markSuspensePending(session: StreamingSsrSession, boundaryId: string): AxisStep<StreamingSsrState>;
592
+ declare function captureErrorBoundary(session: StreamingSsrSession, input: {
593
+ boundaryId: string;
594
+ error: Error | string;
595
+ recoverable?: boolean;
596
+ }): AxisStep<StreamingSsrState>;
597
+ declare function startProgressiveHydration(session: StreamingSsrSession, boundaryId: string): AxisStep<StreamingSsrState>;
598
+ declare function completeSelectiveHydration(session: StreamingSsrSession, boundaryId: string): AxisStep<StreamingSsrState>;
599
+
600
+ type ViewTransitionState = 'idle' | 'element-transitioning' | 'document-transitioning' | 'asserted' | 'finished';
601
+ interface ViewTransitionSession {
602
+ target: ComponentTarget;
603
+ transitionId: string;
604
+ state: ViewTransitionState;
605
+ activeElements: Set<string>;
606
+ documentTransition: string | null;
607
+ assertions: string[];
608
+ history: AxisStep<ViewTransitionState>[];
609
+ }
610
+ declare function startViewTransitionSession(input: {
611
+ target: ComponentTarget;
612
+ transitionId: string;
613
+ }): ViewTransitionSession;
614
+ declare function startElementTransition(session: ViewTransitionSession, input: {
615
+ elementId: string;
616
+ from: string;
617
+ to: string;
618
+ }): AxisStep<ViewTransitionState>;
619
+ declare function finishElementTransition(session: ViewTransitionSession, elementId: string): AxisStep<ViewTransitionState>;
620
+ declare function startDocumentTransition(session: ViewTransitionSession, input: {
621
+ name: string;
622
+ fromUrl: string;
623
+ toUrl: string;
624
+ }): AxisStep<ViewTransitionState>;
625
+ declare function assertAnimation(session: ViewTransitionSession, input: {
626
+ assertionId: string;
627
+ durationMs: number;
628
+ easing?: string;
629
+ }): AxisStep<ViewTransitionState>;
630
+
631
+ type FormActionState = 'idle' | 'pending' | 'optimistic' | 'enhanced' | 'resolved' | 'rejected';
632
+ interface FormActionSession<TForm extends Record<string, unknown> = Record<string, unknown>> {
633
+ target: ComponentTarget;
634
+ formId: string;
635
+ state: FormActionState;
636
+ form: TForm;
637
+ optimisticPatches: Array<Partial<TForm>>;
638
+ enhanced: boolean;
639
+ history: AxisStep<FormActionState>[];
640
+ error: string | null;
641
+ }
642
+ declare function startFormActionSession<TForm extends Record<string, unknown>>(input: {
643
+ target: ComponentTarget;
644
+ formId: string;
645
+ initial: TForm;
646
+ }): FormActionSession<TForm>;
647
+ declare function markFormStatusPending<TForm extends Record<string, unknown>>(session: FormActionSession<TForm>, submitter: string): AxisStep<FormActionState>;
648
+ declare function applyOptimisticUpdate<TForm extends Record<string, unknown>>(session: FormActionSession<TForm>, patch: Partial<TForm>): AxisStep<FormActionState>;
649
+ declare function enableProgressiveEnhancement<TForm extends Record<string, unknown>>(session: FormActionSession<TForm>, input: {
650
+ method?: 'post' | 'get';
651
+ actionUrl: string;
652
+ }): AxisStep<FormActionState>;
653
+ declare function resolveFormAction<TForm extends Record<string, unknown>>(session: FormActionSession<TForm>, result: Partial<TForm>): AxisStep<FormActionState>;
654
+ declare function rejectFormAction<TForm extends Record<string, unknown>>(session: FormActionSession<TForm>, error: Error | string): AxisStep<FormActionState>;
655
+
656
+ interface FidelityRow {
657
+ provider: ComponentTarget;
658
+ axis: ComponentAxis;
659
+ neutralEvents: NeutralEventName[];
660
+ providerEvents: string[];
661
+ }
662
+ interface FidelityCoverage {
663
+ providers: ComponentTarget[];
664
+ axes: ComponentAxis[];
665
+ rows: FidelityRow[];
666
+ }
667
+ declare const COMPONENT_AXIS_TO_EVENTS: Record<ComponentAxis, NeutralEventName[]>;
668
+ declare function collectFidelityCoverage(providers?: ComponentTarget[]): FidelityCoverage;
669
+
670
+ type KiwaTestMode = 'mock' | 'real';
671
+ interface ResolvedMode {
672
+ mode: KiwaTestMode;
673
+ provider: ComponentTarget;
674
+ reason: 'default-mock' | 'kiwa-mode-real' | 'missing-key' | 'invalid-mode';
675
+ }
676
+ declare function resolveMode(provider: ComponentTarget, env?: Record<string, string | undefined>): ResolvedMode;
677
+ declare function resolveAllModes(env?: Record<string, string | undefined>): ResolvedMode[];
678
+ declare function assertMode(provider: ComponentTarget, expected: KiwaTestMode, env?: Record<string, string | undefined>): void;
679
+
680
+ export { type A11yViolation, type AxisStep, type ButtonArgs, COMPONENT_AXIS_TO_EVENTS, type CanvasElement, type CardArgs, type ChromaticConfig, type ChromaticVisualMock, type ComponentAxis, type ComponentLocator, type ComponentProvider, type ComponentRender, type ComponentTarget, type FidelityCoverage, type FidelityRow, type FormActionSession, type FormActionState, type FormArgs, type FormField, type InputArgs, type KiwaTestMode, type MockEvent, type MockNode, type ModalArgs, type NeutralEventName, type NodeLocator, type NodeOptions, type PlayContext, type PlaywrightCTMock, type ResolvedMode, type RscHarnessSession, type RscHarnessState, type StoryEntry, type StoryMeta, type StoryMountResult, type StoryObj, type StoryParameters, type StoryPlayResult, type StoryRegistry, type StreamingSsrSession, type StreamingSsrState, type ViewTransitionSession, type ViewTransitionState, type VisualBaseline, type VisualDiff, type VisualReviewAction, type VisualReviewEntry, addHandler, appendChild, applyOptimisticUpdate, assertAnimation, assertMode, beginRscRender, buildButton, buildCard, buildForm, buildInput, buildModal, captureErrorBoundary, collectFidelityCoverage, completeRscRender, completeSelectiveHydration, componentFixtures, createCanvas, createChromaticVisualMock, createNode, createPlaywrightCTMock, createStoryRegistry, enableProgressiveEnhancement, enterSuspenseBoundary, failRscRender, findByRole, findByText, finishElementTransition, fireEvent, hashMarkup, markFormStatusPending, markSuspensePending, providerEventName, query, rejectFormAction, renderMarkup, resolveAllModes, resolveFormAction, resolveMode, startDocumentTransition, startElementTransition, startFormActionSession, startProgressiveHydration, startRscHarness, startStreamingSsr, startViewTransitionSession, streamHtmlChunk };
package/dist/index.js CHANGED
@@ -733,25 +733,489 @@ var componentFixtures = {
733
733
  Modal: buildModal,
734
734
  Card: buildCard
735
735
  };
736
+
737
+ // src/semantics/types.ts
738
+ var dialect = {
739
+ storybook8: {
740
+ "rsc.render_started": "storybook.play.start",
741
+ "rsc.suspense_boundary": "storybook.suspense.fallback",
742
+ "rsc.html_chunk_streamed": "storybook.stream.chunk",
743
+ "rsc.render_completed": "storybook.play.done",
744
+ "ssr.suspense_pending": "storybook.suspense.pending",
745
+ "ssr.error_boundary_captured": "storybook.error.boundary",
746
+ "ssr.progressive_hydration_started": "storybook.hydration.progressive",
747
+ "ssr.selective_hydration_completed": "storybook.hydration.selective",
748
+ "transition.element_started": "storybook.transition.element.start",
749
+ "transition.element_finished": "storybook.transition.element.finish",
750
+ "transition.document_started": "storybook.transition.document.start",
751
+ "transition.animation_asserted": "storybook.animation.assert",
752
+ "form.status_pending": "storybook.form.pending",
753
+ "form.optimistic_applied": "storybook.form.optimistic",
754
+ "form.progressive_enhanced": "storybook.form.enhanced",
755
+ "form.action_resolved": "storybook.form.resolved"
756
+ },
757
+ "playwright-ct": {
758
+ "rsc.render_started": "pwct.mount.rsc.start",
759
+ "rsc.suspense_boundary": "pwct.locator.suspense.fallback",
760
+ "rsc.html_chunk_streamed": "pwct.response.chunk",
761
+ "rsc.render_completed": "pwct.mount.rsc.done",
762
+ "ssr.suspense_pending": "pwct.suspense.pending",
763
+ "ssr.error_boundary_captured": "pwct.error.boundary",
764
+ "ssr.progressive_hydration_started": "pwct.hydration.progressive",
765
+ "ssr.selective_hydration_completed": "pwct.hydration.selective",
766
+ "transition.element_started": "pwct.transition.element.start",
767
+ "transition.element_finished": "pwct.transition.element.finish",
768
+ "transition.document_started": "pwct.transition.document.start",
769
+ "transition.animation_asserted": "pwct.animation.assert",
770
+ "form.status_pending": "pwct.form.pending",
771
+ "form.optimistic_applied": "pwct.form.optimistic",
772
+ "form.progressive_enhanced": "pwct.form.enhanced",
773
+ "form.action_resolved": "pwct.form.resolved"
774
+ },
775
+ chromatic: {
776
+ "rsc.render_started": "chromatic.capture.rsc.start",
777
+ "rsc.suspense_boundary": "chromatic.capture.suspense",
778
+ "rsc.html_chunk_streamed": "chromatic.capture.chunk",
779
+ "rsc.render_completed": "chromatic.capture.rsc.done",
780
+ "ssr.suspense_pending": "chromatic.suspense.pending",
781
+ "ssr.error_boundary_captured": "chromatic.error.snapshot",
782
+ "ssr.progressive_hydration_started": "chromatic.hydration.progressive",
783
+ "ssr.selective_hydration_completed": "chromatic.hydration.selective",
784
+ "transition.element_started": "chromatic.transition.element.start",
785
+ "transition.element_finished": "chromatic.transition.element.finish",
786
+ "transition.document_started": "chromatic.transition.document.start",
787
+ "transition.animation_asserted": "chromatic.animation.assert",
788
+ "form.status_pending": "chromatic.form.pending",
789
+ "form.optimistic_applied": "chromatic.form.optimistic",
790
+ "form.progressive_enhanced": "chromatic.form.enhanced",
791
+ "form.action_resolved": "chromatic.form.resolved"
792
+ }
793
+ };
794
+ function providerEventName(target, neutral) {
795
+ return dialect[target][neutral] ?? neutral;
796
+ }
797
+
798
+ // src/semantics/rsc-harness.ts
799
+ function startRscHarness(input) {
800
+ if (input.componentId.length === 0) {
801
+ throw new Error("startRscHarness: componentId must not be empty");
802
+ }
803
+ return {
804
+ target: input.target,
805
+ componentId: input.componentId,
806
+ state: "idle",
807
+ chunks: [],
808
+ suspenseFallback: input.suspenseFallback ?? null,
809
+ history: [],
810
+ error: null
811
+ };
812
+ }
813
+ function beginRscRender(session) {
814
+ if (session.state !== "idle") {
815
+ throw new Error(`beginRscRender: session is ${session.state}, not idle`);
816
+ }
817
+ session.state = "rendering";
818
+ return emit(session, "rsc.render_started", { componentId: session.componentId });
819
+ }
820
+ function enterSuspenseBoundary(session, fallback = session.suspenseFallback ?? '<template data-suspense="pending"></template>') {
821
+ if (session.state !== "rendering") {
822
+ throw new Error(`enterSuspenseBoundary: session is ${session.state}, not rendering`);
823
+ }
824
+ session.state = "suspended";
825
+ session.suspenseFallback = fallback;
826
+ return emit(session, "rsc.suspense_boundary", { fallback });
827
+ }
828
+ function streamHtmlChunk(session, chunk) {
829
+ if (session.state !== "rendering" && session.state !== "suspended" && session.state !== "streaming") {
830
+ throw new Error(`streamHtmlChunk: session is ${session.state}, cannot stream`);
831
+ }
832
+ if (chunk.length === 0) {
833
+ throw new Error("streamHtmlChunk: chunk must not be empty");
834
+ }
835
+ session.state = "streaming";
836
+ session.chunks.push(chunk);
837
+ return emit(session, "rsc.html_chunk_streamed", {
838
+ chunk,
839
+ chunkIndex: session.chunks.length - 1,
840
+ bytes: chunk.length
841
+ });
842
+ }
843
+ function completeRscRender(session) {
844
+ if (session.state !== "rendering" && session.state !== "suspended" && session.state !== "streaming") {
845
+ throw new Error(`completeRscRender: session is ${session.state}, cannot complete`);
846
+ }
847
+ session.state = "completed";
848
+ return emit(session, "rsc.render_completed", {
849
+ chunkCount: session.chunks.length,
850
+ html: session.chunks.join("")
851
+ });
852
+ }
853
+ function failRscRender(session, error) {
854
+ if (session.state === "completed") {
855
+ throw new Error("failRscRender: completed session cannot fail");
856
+ }
857
+ session.state = "errored";
858
+ session.error = typeof error === "string" ? error : error.message;
859
+ return emit(session, "ssr.error_boundary_captured", {
860
+ componentId: session.componentId,
861
+ error: session.error
862
+ });
863
+ }
864
+ function emit(session, neutralEvent, metadata) {
865
+ const step = {
866
+ neutralEvent,
867
+ providerEvent: providerEventName(session.target, neutralEvent),
868
+ state: session.state,
869
+ amountCents: 0,
870
+ metadata: { target: session.target, ...metadata }
871
+ };
872
+ session.history.push(step);
873
+ return step;
874
+ }
875
+
876
+ // src/semantics/streaming-ssr.ts
877
+ function startStreamingSsr(input) {
878
+ if (input.routeId.length === 0) {
879
+ throw new Error("startStreamingSsr: routeId must not be empty");
880
+ }
881
+ return {
882
+ target: input.target,
883
+ routeId: input.routeId,
884
+ state: "idle",
885
+ pendingBoundaries: /* @__PURE__ */ new Set(),
886
+ hydratedBoundaries: /* @__PURE__ */ new Set(),
887
+ errors: [],
888
+ history: []
889
+ };
890
+ }
891
+ function markSuspensePending(session, boundaryId) {
892
+ assertBoundaryId(boundaryId);
893
+ session.state = "suspense-pending";
894
+ session.pendingBoundaries.add(boundaryId);
895
+ return emit2(session, "ssr.suspense_pending", { boundaryId, pendingCount: session.pendingBoundaries.size });
896
+ }
897
+ function captureErrorBoundary(session, input) {
898
+ assertBoundaryId(input.boundaryId);
899
+ const message = typeof input.error === "string" ? input.error : input.error.message;
900
+ session.state = "error-captured";
901
+ session.errors.push({ boundaryId: input.boundaryId, message });
902
+ if (input.recoverable === false) {
903
+ session.pendingBoundaries.delete(input.boundaryId);
904
+ }
905
+ return emit2(session, "ssr.error_boundary_captured", {
906
+ boundaryId: input.boundaryId,
907
+ message,
908
+ recoverable: input.recoverable ?? true
909
+ });
910
+ }
911
+ function startProgressiveHydration(session, boundaryId) {
912
+ assertBoundaryId(boundaryId);
913
+ if (!session.pendingBoundaries.has(boundaryId)) {
914
+ throw new Error(`startProgressiveHydration: ${boundaryId} is not pending`);
915
+ }
916
+ session.state = "progressive-hydrating";
917
+ return emit2(session, "ssr.progressive_hydration_started", { boundaryId });
918
+ }
919
+ function completeSelectiveHydration(session, boundaryId) {
920
+ assertBoundaryId(boundaryId);
921
+ if (!session.pendingBoundaries.has(boundaryId)) {
922
+ throw new Error(`completeSelectiveHydration: ${boundaryId} is not pending`);
923
+ }
924
+ session.pendingBoundaries.delete(boundaryId);
925
+ session.hydratedBoundaries.add(boundaryId);
926
+ session.state = "selective-hydrated";
927
+ return emit2(session, "ssr.selective_hydration_completed", {
928
+ boundaryId,
929
+ hydratedCount: session.hydratedBoundaries.size,
930
+ remainingPending: session.pendingBoundaries.size
931
+ });
932
+ }
933
+ function assertBoundaryId(boundaryId) {
934
+ if (boundaryId.length === 0) {
935
+ throw new Error("boundaryId must not be empty");
936
+ }
937
+ }
938
+ function emit2(session, neutralEvent, metadata) {
939
+ const step = {
940
+ neutralEvent,
941
+ providerEvent: providerEventName(session.target, neutralEvent),
942
+ state: session.state,
943
+ amountCents: 0,
944
+ metadata: { target: session.target, routeId: session.routeId, ...metadata }
945
+ };
946
+ session.history.push(step);
947
+ return step;
948
+ }
949
+
950
+ // src/semantics/view-transitions.ts
951
+ function startViewTransitionSession(input) {
952
+ if (input.transitionId.length === 0) {
953
+ throw new Error("startViewTransitionSession: transitionId must not be empty");
954
+ }
955
+ return {
956
+ target: input.target,
957
+ transitionId: input.transitionId,
958
+ state: "idle",
959
+ activeElements: /* @__PURE__ */ new Set(),
960
+ documentTransition: null,
961
+ assertions: [],
962
+ history: []
963
+ };
964
+ }
965
+ function startElementTransition(session, input) {
966
+ if (input.elementId.length === 0) {
967
+ throw new Error("startElementTransition: elementId must not be empty");
968
+ }
969
+ session.state = "element-transitioning";
970
+ session.activeElements.add(input.elementId);
971
+ return emit3(session, "transition.element_started", input);
972
+ }
973
+ function finishElementTransition(session, elementId) {
974
+ if (!session.activeElements.has(elementId)) {
975
+ throw new Error(`finishElementTransition: ${elementId} is not active`);
976
+ }
977
+ session.activeElements.delete(elementId);
978
+ session.state = session.activeElements.size === 0 ? "finished" : "element-transitioning";
979
+ return emit3(session, "transition.element_finished", {
980
+ elementId,
981
+ remaining: session.activeElements.size
982
+ });
983
+ }
984
+ function startDocumentTransition(session, input) {
985
+ if (input.name.length === 0) {
986
+ throw new Error("startDocumentTransition: name must not be empty");
987
+ }
988
+ session.state = "document-transitioning";
989
+ session.documentTransition = input.name;
990
+ return emit3(session, "transition.document_started", input);
991
+ }
992
+ function assertAnimation(session, input) {
993
+ if (input.durationMs < 0) {
994
+ throw new Error("assertAnimation: durationMs must be >= 0");
995
+ }
996
+ session.state = "asserted";
997
+ session.assertions.push(input.assertionId);
998
+ return emit3(session, "transition.animation_asserted", {
999
+ assertionId: input.assertionId,
1000
+ durationMs: input.durationMs,
1001
+ easing: input.easing ?? "linear"
1002
+ });
1003
+ }
1004
+ function emit3(session, neutralEvent, metadata) {
1005
+ const step = {
1006
+ neutralEvent,
1007
+ providerEvent: providerEventName(session.target, neutralEvent),
1008
+ state: session.state,
1009
+ amountCents: 0,
1010
+ metadata: { target: session.target, transitionId: session.transitionId, ...metadata }
1011
+ };
1012
+ session.history.push(step);
1013
+ return step;
1014
+ }
1015
+
1016
+ // src/semantics/form-action-advanced.ts
1017
+ function startFormActionSession(input) {
1018
+ if (input.formId.length === 0) {
1019
+ throw new Error("startFormActionSession: formId must not be empty");
1020
+ }
1021
+ return {
1022
+ target: input.target,
1023
+ formId: input.formId,
1024
+ state: "idle",
1025
+ form: { ...input.initial },
1026
+ optimisticPatches: [],
1027
+ enhanced: false,
1028
+ history: [],
1029
+ error: null
1030
+ };
1031
+ }
1032
+ function markFormStatusPending(session, submitter) {
1033
+ if (session.state === "pending") {
1034
+ throw new Error("markFormStatusPending: form is already pending");
1035
+ }
1036
+ session.state = "pending";
1037
+ return emit4(session, "form.status_pending", { submitter });
1038
+ }
1039
+ function applyOptimisticUpdate(session, patch) {
1040
+ if (session.state !== "pending" && session.state !== "optimistic") {
1041
+ throw new Error(`applyOptimisticUpdate: session is ${session.state}, not pending`);
1042
+ }
1043
+ session.state = "optimistic";
1044
+ session.optimisticPatches.push(patch);
1045
+ session.form = { ...session.form, ...patch };
1046
+ return emit4(session, "form.optimistic_applied", {
1047
+ patchKeys: Object.keys(patch).join(","),
1048
+ patchCount: session.optimisticPatches.length
1049
+ });
1050
+ }
1051
+ function enableProgressiveEnhancement(session, input) {
1052
+ if (input.actionUrl.length === 0) {
1053
+ throw new Error("enableProgressiveEnhancement: actionUrl must not be empty");
1054
+ }
1055
+ session.enhanced = true;
1056
+ session.state = "enhanced";
1057
+ return emit4(session, "form.progressive_enhanced", {
1058
+ method: input.method ?? "post",
1059
+ actionUrl: input.actionUrl
1060
+ });
1061
+ }
1062
+ function resolveFormAction(session, result) {
1063
+ if (session.state === "idle") {
1064
+ throw new Error("resolveFormAction: action was not submitted");
1065
+ }
1066
+ session.state = "resolved";
1067
+ session.form = { ...session.form, ...result };
1068
+ return emit4(session, "form.action_resolved", {
1069
+ resultKeys: Object.keys(result).join(","),
1070
+ enhanced: session.enhanced
1071
+ });
1072
+ }
1073
+ function rejectFormAction(session, error) {
1074
+ if (session.state === "resolved") {
1075
+ throw new Error("rejectFormAction: resolved action cannot reject");
1076
+ }
1077
+ session.state = "rejected";
1078
+ session.error = typeof error === "string" ? error : error.message;
1079
+ return emit4(session, "form.action_resolved", {
1080
+ rejected: true,
1081
+ error: session.error
1082
+ });
1083
+ }
1084
+ function emit4(session, neutralEvent, metadata) {
1085
+ const step = {
1086
+ neutralEvent,
1087
+ providerEvent: providerEventName(session.target, neutralEvent),
1088
+ state: session.state,
1089
+ amountCents: 0,
1090
+ metadata: { target: session.target, formId: session.formId, ...metadata }
1091
+ };
1092
+ session.history.push(step);
1093
+ return step;
1094
+ }
1095
+
1096
+ // src/semantics/fidelity.ts
1097
+ var COMPONENT_AXIS_TO_EVENTS = {
1098
+ "rsc-harness": [
1099
+ "rsc.render_started",
1100
+ "rsc.suspense_boundary",
1101
+ "rsc.html_chunk_streamed",
1102
+ "rsc.render_completed"
1103
+ ],
1104
+ "streaming-ssr": [
1105
+ "ssr.suspense_pending",
1106
+ "ssr.error_boundary_captured",
1107
+ "ssr.progressive_hydration_started",
1108
+ "ssr.selective_hydration_completed"
1109
+ ],
1110
+ "view-transitions": [
1111
+ "transition.element_started",
1112
+ "transition.element_finished",
1113
+ "transition.document_started",
1114
+ "transition.animation_asserted"
1115
+ ],
1116
+ "form-action-advanced": [
1117
+ "form.status_pending",
1118
+ "form.optimistic_applied",
1119
+ "form.progressive_enhanced",
1120
+ "form.action_resolved"
1121
+ ]
1122
+ };
1123
+ function collectFidelityCoverage(providers = ["storybook8", "playwright-ct", "chromatic"]) {
1124
+ const axes = Object.keys(COMPONENT_AXIS_TO_EVENTS);
1125
+ const rows = [];
1126
+ for (const provider of providers) {
1127
+ for (const axis of axes) {
1128
+ const neutralEvents = COMPONENT_AXIS_TO_EVENTS[axis];
1129
+ rows.push({
1130
+ provider,
1131
+ axis,
1132
+ neutralEvents,
1133
+ providerEvents: neutralEvents.map((event) => providerEventName(provider, event))
1134
+ });
1135
+ }
1136
+ }
1137
+ return { providers, axes, rows };
1138
+ }
1139
+
1140
+ // src/real-driver.ts
1141
+ var TARGET_KEY_ENV = {
1142
+ storybook8: "STORYBOOK_URL",
1143
+ "playwright-ct": "PLAYWRIGHT_CT_URL",
1144
+ chromatic: "CHROMATIC_TOKEN"
1145
+ };
1146
+ function resolveMode(provider, env = process.env) {
1147
+ const rawMode = env.KIWA_MODE?.toLowerCase();
1148
+ if (rawMode !== void 0 && rawMode !== "real" && rawMode !== "mock") {
1149
+ return { provider, mode: "mock", reason: "invalid-mode" };
1150
+ }
1151
+ if (rawMode !== "real") {
1152
+ return { provider, mode: "mock", reason: "default-mock" };
1153
+ }
1154
+ const keyValue = env[TARGET_KEY_ENV[provider]];
1155
+ if (typeof keyValue !== "string" || keyValue.length === 0) {
1156
+ return { provider, mode: "mock", reason: "missing-key" };
1157
+ }
1158
+ return { provider, mode: "real", reason: "kiwa-mode-real" };
1159
+ }
1160
+ function resolveAllModes(env = process.env) {
1161
+ const providers = ["storybook8", "playwright-ct", "chromatic"];
1162
+ return providers.map((provider) => resolveMode(provider, env));
1163
+ }
1164
+ function assertMode(provider, expected, env = process.env) {
1165
+ const resolved = resolveMode(provider, env);
1166
+ if (resolved.mode !== expected) {
1167
+ throw new Error(
1168
+ `expected ${provider} in ${expected} mode but resolved ${resolved.mode} (${resolved.reason})`
1169
+ );
1170
+ }
1171
+ }
736
1172
  export {
1173
+ COMPONENT_AXIS_TO_EVENTS,
737
1174
  addHandler,
738
1175
  appendChild,
1176
+ applyOptimisticUpdate,
1177
+ assertAnimation,
1178
+ assertMode,
1179
+ beginRscRender,
739
1180
  buildButton,
740
1181
  buildCard,
741
1182
  buildForm,
742
1183
  buildInput,
743
1184
  buildModal,
1185
+ captureErrorBoundary,
1186
+ collectFidelityCoverage,
1187
+ completeRscRender,
1188
+ completeSelectiveHydration,
744
1189
  componentFixtures,
745
1190
  createCanvas,
746
1191
  createChromaticVisualMock,
747
1192
  createNode,
748
1193
  createPlaywrightCTMock,
749
1194
  createStoryRegistry,
1195
+ enableProgressiveEnhancement,
1196
+ enterSuspenseBoundary,
1197
+ failRscRender,
750
1198
  findByRole,
751
1199
  findByText,
1200
+ finishElementTransition,
752
1201
  fireEvent,
753
1202
  hashMarkup,
1203
+ markFormStatusPending,
1204
+ markSuspensePending,
1205
+ providerEventName,
754
1206
  query,
755
- renderMarkup
1207
+ rejectFormAction,
1208
+ renderMarkup,
1209
+ resolveAllModes,
1210
+ resolveFormAction,
1211
+ resolveMode,
1212
+ startDocumentTransition,
1213
+ startElementTransition,
1214
+ startFormActionSession,
1215
+ startProgressiveHydration,
1216
+ startRscHarness,
1217
+ startStreamingSsr,
1218
+ startViewTransitionSession,
1219
+ streamHtmlChunk
756
1220
  };
757
1221
  //# sourceMappingURL=index.js.map