uidex 0.7.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.
Files changed (51) hide show
  1. package/dist/cli/cli.cjs +1024 -1041
  2. package/dist/cli/cli.cjs.map +1 -1
  3. package/dist/headless/index.cjs +4 -448
  4. package/dist/headless/index.cjs.map +1 -1
  5. package/dist/headless/index.d.cts +41 -11
  6. package/dist/headless/index.d.ts +41 -11
  7. package/dist/headless/index.js +4 -450
  8. package/dist/headless/index.js.map +1 -1
  9. package/dist/index.cjs +147 -3252
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +43 -316
  12. package/dist/index.d.ts +43 -316
  13. package/dist/index.js +133 -3254
  14. package/dist/index.js.map +1 -1
  15. package/dist/playwright/index.cjs +175 -0
  16. package/dist/playwright/index.cjs.map +1 -1
  17. package/dist/playwright/index.d.cts +2 -0
  18. package/dist/playwright/index.d.ts +2 -0
  19. package/dist/playwright/index.js +167 -0
  20. package/dist/playwright/index.js.map +1 -1
  21. package/dist/playwright/states-reporter.cjs +123 -0
  22. package/dist/playwright/states-reporter.cjs.map +1 -0
  23. package/dist/playwright/states-reporter.d.cts +46 -0
  24. package/dist/playwright/states-reporter.d.ts +46 -0
  25. package/dist/playwright/states-reporter.js +88 -0
  26. package/dist/playwright/states-reporter.js.map +1 -0
  27. package/dist/playwright/states.cjs +118 -0
  28. package/dist/playwright/states.cjs.map +1 -0
  29. package/dist/playwright/states.d.cts +120 -0
  30. package/dist/playwright/states.d.ts +120 -0
  31. package/dist/playwright/states.js +88 -0
  32. package/dist/playwright/states.js.map +1 -0
  33. package/dist/react/index.cjs +163 -3255
  34. package/dist/react/index.cjs.map +1 -1
  35. package/dist/react/index.d.cts +62 -275
  36. package/dist/react/index.d.ts +62 -275
  37. package/dist/react/index.js +151 -3268
  38. package/dist/react/index.js.map +1 -1
  39. package/dist/scan/index.cjs +1194 -322
  40. package/dist/scan/index.cjs.map +1 -1
  41. package/dist/scan/index.d.cts +274 -8
  42. package/dist/scan/index.d.ts +274 -8
  43. package/dist/scan/index.js +1185 -322
  44. package/dist/scan/index.js.map +1 -1
  45. package/package.json +27 -31
  46. package/dist/cloud/index.cjs +0 -682
  47. package/dist/cloud/index.cjs.map +0 -1
  48. package/dist/cloud/index.d.cts +0 -270
  49. package/dist/cloud/index.d.ts +0 -270
  50. package/dist/cloud/index.js +0 -645
  51. package/dist/cloud/index.js.map +0 -1
@@ -1,7 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, ReactElement } from 'react';
3
3
  import { StoreApi } from 'zustand/vanilla';
4
- import { ReportPayload, ReportResult, IngestConfig, ReportListResponse, PinRecord, ArchiveReason } from '@uidex/api-client';
5
4
  import { TemplateResult } from 'lit-html';
6
5
 
7
6
  declare const ENTITY_KINDS: readonly ["route", "page", "feature", "widget", "region", "element", "primitive", "flow"];
@@ -16,6 +15,32 @@ interface EntityRef {
16
15
  kind: EntityKind;
17
16
  id: string;
18
17
  }
18
+ /**
19
+ * The four kinds every captured route is expected to render — the state-matrix
20
+ * axis. A capture that only shoots the happy path leaves most of the value on
21
+ * the table, so a missing core kind must be captured, waived (the page CANNOT
22
+ * render it), or acknowledged in the MISSING_KINDS baseline.
23
+ */
24
+ declare const CORE_STATE_KINDS: readonly ["loading", "empty", "populated", "error"];
25
+ type CoreStateKind = (typeof CORE_STATE_KINDS)[number];
26
+ /** `variant` is any state outside the matrix (a dialog, wizard step, multi-currency…). */
27
+ type StateKind = CoreStateKind | "variant";
28
+ /**
29
+ * A declared render state of an entity — the "story"-like unit: a named
30
+ * condition the page/feature/widget can render in (loading / empty / error /
31
+ * multi-currency / …). Enumerated on the entity (not derived from syntax), it
32
+ * is the contract the visual-states capture must satisfy and the anchor for
33
+ * per-state acceptance criteria. Fixtures + capture mechanics stay in the spec;
34
+ * the registry holds only the name (+ optional kind / per-state acceptance).
35
+ */
36
+ interface StateDecl {
37
+ name: string;
38
+ /** Canonical kind for the state-matrix axis (defaults to `variant`). */
39
+ kind?: StateKind;
40
+ /** Acceptance criteria that only this state can evidence. */
41
+ acceptance?: string[];
42
+ description?: string;
43
+ }
19
44
  interface Metadata {
20
45
  name?: string;
21
46
  description?: string;
@@ -25,6 +50,21 @@ interface Metadata {
25
50
  flows?: readonly string[];
26
51
  features?: string[];
27
52
  widgets?: string[];
53
+ /** Declared render states (see StateDecl); the capture-completeness gate. */
54
+ states?: StateDecl[];
55
+ /**
56
+ * `false` opts this page/feature/widget out of visual-states capture forever
57
+ * (a redirect, a static notice, an internal target). The page-coverage gate
58
+ * treats an opted-out route as accounted-for. Reason belongs in `description`.
59
+ */
60
+ capture?: boolean;
61
+ /**
62
+ * Per-core-kind waivers: a core kind this page's route legitimately CANNOT
63
+ * render (a create form has no `empty`), mapped to a substantive reason. The
64
+ * state-matrix gate treats a waived kind as accounted-for. A waiver is a
65
+ * permanent design statement — unlike MISSING_KINDS backlog, which is debt.
66
+ */
67
+ waivers?: Partial<Record<CoreStateKind, string>>;
28
68
  }
29
69
  interface EntityWithMetaBase {
30
70
  id: string;
@@ -106,123 +146,6 @@ interface Registry {
106
146
  onReportsChange(cb: () => void): () => void;
107
147
  }
108
148
 
109
- type ConsoleLevel = "warn" | "error";
110
- interface ConsoleEntry {
111
- level: ConsoleLevel;
112
- message: string;
113
- timestamp: string;
114
- }
115
- interface ConsoleCaptureOptions {
116
- limit?: number;
117
- target?: Console;
118
- now?: () => Date;
119
- }
120
- interface ConsoleCapture {
121
- start(): void;
122
- stop(): void;
123
- readonly isActive: boolean;
124
- entries(): readonly ConsoleEntry[];
125
- clear(): void;
126
- }
127
-
128
- interface NetworkEntry {
129
- method: string;
130
- url: string;
131
- status: number;
132
- timestamp: string;
133
- error?: string;
134
- }
135
- interface NetworkCaptureOptions {
136
- limit?: number;
137
- target?: {
138
- fetch?: typeof fetch;
139
- };
140
- now?: () => Date;
141
- }
142
- interface NetworkCapture {
143
- start(): void;
144
- stop(): void;
145
- readonly isActive: boolean;
146
- entries(): readonly NetworkEntry[];
147
- clear(): void;
148
- }
149
-
150
- interface UserIdentity {
151
- id: string;
152
- name?: string;
153
- avatar?: string;
154
- }
155
-
156
- type RealtimePresenceUser = {
157
- userId: string;
158
- name: string;
159
- avatar: string | null;
160
- };
161
- type RealtimeChannelState = "connecting" | "connected" | "disconnected";
162
- interface RealtimeConnectOpts {
163
- user: UserIdentity;
164
- route: string;
165
- }
166
- interface RealtimeChannel {
167
- readonly state: RealtimeChannelState;
168
- connect(): void;
169
- disconnect(): void;
170
- joinRoute(route: string): void;
171
- onPresence(cb: (users: RealtimePresenceUser[]) => void): () => void;
172
- onPin(cb: (pin: PinRecord) => void): () => void;
173
- /** Optional so pre-existing channel stubs keep satisfying the interface. */
174
- onPinArchived?(cb: (reportId: string) => void): () => void;
175
- }
176
- interface CloudAdapter<TPayload = ReportPayload, TResult = ReportResult, TIntegrations = {
177
- getConfig(): Promise<IngestConfig>;
178
- getCachedConfig(): IngestConfig | null;
179
- }> {
180
- readonly reports: {
181
- submit(payload: TPayload): Promise<TResult>;
182
- list?(opts?: {
183
- page?: number;
184
- limit?: number;
185
- }): Promise<ReportListResponse>;
186
- };
187
- readonly integrations: TIntegrations;
188
- readonly realtime: {
189
- connect(opts: RealtimeConnectOpts): RealtimeChannel;
190
- };
191
- readonly pins: {
192
- list(params: {
193
- route?: string;
194
- entities?: string;
195
- }): Promise<PinRecord[]>;
196
- /**
197
- * Pin records travel without their screenshot (kept out of `pins.list`
198
- * so multi-MB frames don't stall the shared socket); the report detail
199
- * view fetches it lazily through here. Optional for host-provided
200
- * adapters that inline screenshots on the record instead.
201
- */
202
- screenshot?(reportId: string): Promise<string | null>;
203
- close(reportId: string, reason?: ArchiveReason): Promise<void>;
204
- };
205
- /** Closes the adapter's shared socket. Any later RPC call revives it. */
206
- dispose?(): void;
207
- }
208
-
209
- interface IngestOptions {
210
- captureConsole?: boolean;
211
- captureNetwork?: boolean;
212
- consoleLimit?: number;
213
- networkLimit?: number;
214
- consoleTarget?: ConsoleCaptureOptions["target"];
215
- networkTarget?: NetworkCaptureOptions["target"];
216
- now?: () => Date;
217
- }
218
- interface Ingest {
219
- start(): void;
220
- stop(): void;
221
- readonly isActive: boolean;
222
- readonly console: ConsoleCapture | null;
223
- readonly network: NetworkCapture | null;
224
- }
225
-
226
149
  type ThemePreference = "light" | "dark" | "auto";
227
150
  type ResolvedTheme = "light" | "dark";
228
151
  interface ViewStackEntry {
@@ -237,11 +160,6 @@ interface SessionSnapshot {
237
160
  mode: SurfaceMode;
238
161
  theme: ThemePreference;
239
162
  resolvedTheme: ResolvedTheme;
240
- /**
241
- * Identity for the local user. Set once at session creation; gates realtime
242
- * features (cursor labels, presence) and auto-populates report attribution.
243
- */
244
- user: UserIdentity | null;
245
163
  }
246
164
  type SessionState = SessionSnapshot;
247
165
 
@@ -319,7 +237,7 @@ interface Router {
319
237
  isFavorite(ref: EntityRef): boolean;
320
238
  }
321
239
 
322
- type ViewSurface = ListSurface | DetailSurface | FormSurface;
240
+ type ViewSurface = ListSurface | DetailSurface;
323
241
  interface ListSurface {
324
242
  kind: "list";
325
243
  id: string;
@@ -464,102 +382,6 @@ interface MetadataEntry {
464
382
  label: string;
465
383
  value: string;
466
384
  }
467
- interface FormSurface {
468
- kind: "form";
469
- id: string;
470
- fields: readonly FormField[];
471
- submit: FormSubmit;
472
- initial?: Record<string, FormValue>;
473
- /**
474
- * Optional Standard Schema (Zod v4, Valibot, ArkType, etc.) run against the
475
- * collected form values before `submit.onSubmit`. On failure, issues are
476
- * routed to the matching `<FieldError>` and submission is skipped.
477
- */
478
- schema?: StandardSchemaV1<Record<string, unknown>>;
479
- /**
480
- * Promise resolving to a base64 screenshot data URL. When provided, the
481
- * form renderer shows a thumbnail preview above the fields.
482
- */
483
- screenshotPreview?: Promise<string | null>;
484
- }
485
- /**
486
- * Minimal StandardSchemaV1 surface — kept inline so the SDK doesn't need a
487
- * type-level dep on any specific validator package.
488
- */
489
- interface StandardSchemaV1<Input = unknown, Output = Input> {
490
- readonly "~standard": {
491
- readonly version: 1;
492
- readonly vendor: string;
493
- readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
494
- readonly types?: {
495
- readonly input: Input;
496
- readonly output: Output;
497
- };
498
- };
499
- }
500
- type StandardSchemaResult<Output> = {
501
- readonly value: Output;
502
- readonly issues?: undefined;
503
- } | {
504
- readonly issues: ReadonlyArray<StandardSchemaIssue>;
505
- };
506
- interface StandardSchemaIssue {
507
- readonly message: string;
508
- readonly path?: ReadonlyArray<PropertyKey | {
509
- readonly key: PropertyKey;
510
- }>;
511
- }
512
- interface FormSubmit {
513
- label: string;
514
- onSubmit: (values: Record<string, FormValue>) => FormSubmitResult | void | Promise<FormSubmitResult | void>;
515
- }
516
- type FormSubmitResult = {
517
- status: "success";
518
- message?: string;
519
- link?: {
520
- url: string;
521
- label?: string;
522
- };
523
- resetFields?: readonly string[];
524
- } | {
525
- status: "error";
526
- message: string;
527
- /** Per-field error messages keyed by field `name`. */
528
- fieldErrors?: Record<string, string>;
529
- };
530
- type FormValue = string | boolean;
531
- interface FormFieldBase {
532
- name: string;
533
- label: string;
534
- }
535
- type FormField = (FormFieldBase & {
536
- kind: "select";
537
- options: readonly {
538
- value: string;
539
- label: string;
540
- }[];
541
- required?: boolean;
542
- value?: string;
543
- }) | (FormFieldBase & {
544
- kind: "text";
545
- placeholder?: string;
546
- required?: boolean;
547
- value?: string;
548
- }) | (FormFieldBase & {
549
- kind: "email";
550
- placeholder?: string;
551
- required?: boolean;
552
- value?: string;
553
- }) | (FormFieldBase & {
554
- kind: "textarea";
555
- placeholder?: string;
556
- required?: boolean;
557
- rows?: number;
558
- value?: string;
559
- }) | (FormFieldBase & {
560
- kind: "checkbox";
561
- value?: boolean;
562
- });
563
385
 
564
386
  interface ViewPalette {
565
387
  label: string;
@@ -583,13 +405,6 @@ interface HighlightController {
583
405
  interface ViewContext {
584
406
  ref: EntityRef | null;
585
407
  registry: Registry;
586
- cloud: CloudAdapter<unknown, unknown, unknown> | null;
587
- /**
588
- * Local user identity from `createUidex`'s `user` option, or `null` when
589
- * unconfigured. Views that auto-populate attribution (e.g. report
590
- * `reporterName`) read this here instead of touching the session store.
591
- */
592
- user: UserIdentity | null;
593
408
  views: Router;
594
409
  /** Push a view onto the stack. Unregistered ids are a no-op. */
595
410
  push: (target: ViewPushTarget) => void;
@@ -605,11 +420,6 @@ interface ViewContext {
605
420
  highlight: HighlightController;
606
421
  /** Snapshot of the current view stack (top-most last). */
607
422
  getStack: () => readonly ViewStackEntry[];
608
- pushEscapeLayer(handler: EscapeHandler): Cleanup;
609
- /** Called after a report submission succeeds, to refresh pins etc. */
610
- onAfterSubmit?: () => void;
611
- /** Return the current route pattern. Falls back to `location.pathname`. */
612
- getRoute?: () => string;
613
423
  }
614
424
  interface ShellHint {
615
425
  key: string;
@@ -669,7 +479,6 @@ interface PaletteShortcut {
669
479
  /** Require Shift. Default: false. */
670
480
  shift?: boolean;
671
481
  }
672
- type EscapeHandler = () => boolean;
673
482
 
674
483
  interface CreateUidexOptions {
675
484
  theme?: ThemePreference;
@@ -678,38 +487,13 @@ interface CreateUidexOptions {
678
487
  initialCorner?: Corner;
679
488
  /** Shown as a label at the start of the menu bar. */
680
489
  appTitle?: string;
681
- cloud?: CloudAdapter | null;
682
490
  dev?: boolean;
683
491
  /** Register bundled default views. Defaults to `true`. */
684
492
  defaultViews?: boolean;
685
493
  /** Additional views to register (after defaults). */
686
494
  views?: readonly View[];
687
- /**
688
- * Override ingest behaviour. When `cloud` is non-null, ingest auto-enables
689
- * (both console and network capture). Pass `null` to disable entirely, or
690
- * an options object to override either channel.
691
- */
692
- ingest?: IngestOptions | null;
693
495
  /** Keyboard shortcut for the command palette. Defaults to Cmd+K / Ctrl+K. */
694
496
  shortcut?: PaletteShortcut;
695
- /**
696
- * Identity for the local user. Required to opt into realtime collaborative
697
- * features (peer cursors, presence). When omitted, realtime stays
698
- * disconnected and report attribution falls back to whatever the user
699
- * types in the report form.
700
- */
701
- user?: UserIdentity;
702
- /** Git context for pin filtering. Typically mirrors `cloud({ git })`. */
703
- git?: {
704
- branch?: string;
705
- commit?: string;
706
- };
707
- /**
708
- * Return the current route pattern (e.g. `/users/:id`). Used for
709
- * route-based pin matching and report attribution. When omitted, the SDK
710
- * falls back to `location.pathname`.
711
- */
712
- getRoute?: () => string;
713
497
  }
714
498
  interface Uidex {
715
499
  mount(target?: Element): void;
@@ -717,24 +501,19 @@ interface Uidex {
717
501
  readonly registry: Registry;
718
502
  readonly session: SessionStore;
719
503
  readonly views: Router;
720
- readonly cloud: CloudAdapter | null;
721
- readonly ingest: Ingest | null;
722
504
  readonly shadowRoot: ShadowRoot | null;
723
505
  }
724
506
 
725
507
  interface UidexProviderProps {
726
508
  /**
727
509
  * Pre-built uidex instance (e.g. the `uidex` export from `uidex.gen.ts`).
728
- * When provided, `projectKey`, `cloud`, `user`, and `config` are ignored.
510
+ * When provided, `config` is ignored.
729
511
  */
730
512
  instance?: Uidex;
731
- projectKey?: string;
732
- cloud?: CloudAdapter | null;
733
- user?: UserIdentity;
734
- config?: Omit<CreateUidexOptions, "cloud" | "user">;
513
+ config?: CreateUidexOptions;
735
514
  children?: ReactNode;
736
515
  }
737
- declare function UidexProvider({ instance: externalInstance, projectKey, cloud, user, config, children, }: UidexProviderProps): react_jsx_runtime.JSX.Element;
516
+ declare function UidexProvider({ instance: externalInstance, config, children, }: UidexProviderProps): react_jsx_runtime.JSX.Element;
738
517
 
739
518
  declare class UidexContextError extends Error {
740
519
  constructor(hookName: string);
@@ -765,16 +544,24 @@ interface KindChipProps {
765
544
  declare function KindChip({ kind, label, withKindName, className, }: KindChipProps): ReactElement;
766
545
 
767
546
  interface UidexDevtoolsProps {
547
+ /**
548
+ * Populates the entity registry (typically `loadRegistry` from `uidex.gen.ts`).
549
+ * Must be idempotent: it is called once per created instance, and under React
550
+ * StrictMode the effect runs setup→cleanup→setup, so it may be invoked against
551
+ * a short-lived instance that is discarded before mount.
552
+ */
768
553
  loadRegistry: (target: Registry) => Registry;
769
- projectKey?: string;
770
- endpoint?: string;
771
- user?: UserIdentity;
772
- git?: {
773
- branch?: string;
774
- commit?: string;
775
- };
776
- config?: Omit<CreateUidexOptions, "cloud" | "user" | "git">;
554
+ /**
555
+ * Optional deferred hydration — populate the registry with data that need not
556
+ * be present at mount (typically the thin element/region source locations from
557
+ * `uidex.gen.locs.ts`, dynamic-imported so they land in a separate chunk). Run
558
+ * on an idle callback after mount, so the initial devtools chunk stays small
559
+ * and the copy-source-path metadata streams in shortly after. Idempotent, like
560
+ * `loadRegistry`.
561
+ */
562
+ loadDeferred?: (target: Registry) => void | Promise<void>;
563
+ config?: CreateUidexOptions;
777
564
  }
778
- declare function UidexDevtools({ loadRegistry, projectKey, endpoint, user, git, config, }: UidexDevtoolsProps): react_jsx_runtime.JSX.Element | null;
565
+ declare function UidexDevtools({ loadRegistry, loadDeferred, config, }: UidexDevtoolsProps): react_jsx_runtime.JSX.Element | null;
779
566
 
780
567
  export { KindChip, type KindChipProps, type ReactViewDef, UidexContextError, UidexDevtools, type UidexDevtoolsProps, UidexMount, type UidexMountProps, UidexProvider, type UidexProviderProps, createReactView, useUidex };