@solidjs/web 2.0.0-beta.2 → 2.0.0-beta.21

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 (52) hide show
  1. package/README.md +27 -4
  2. package/dist/dev.cjs +790 -223
  3. package/dist/dev.js +764 -217
  4. package/dist/server.cjs +742 -186
  5. package/dist/server.js +714 -183
  6. package/dist/web.cjs +779 -196
  7. package/dist/web.js +753 -190
  8. package/package.json +193 -38
  9. package/serialization/dist/serialization.cjs +83 -0
  10. package/serialization/dist/serialization.js +75 -0
  11. package/serialization/package.json +20 -0
  12. package/serialization/types/index.d.ts +139 -0
  13. package/serialization/types-cjs/index.d.cts +139 -0
  14. package/serialization/types-cjs/package.json +3 -0
  15. package/server-functions/dist/client.cjs +448 -0
  16. package/server-functions/dist/client.js +435 -0
  17. package/server-functions/dist/server.cjs +632 -0
  18. package/server-functions/dist/server.js +615 -0
  19. package/server-functions/package.json +30 -0
  20. package/storage/package.json +8 -3
  21. package/storage/types/index.d.ts +26 -0
  22. package/storage/types-cjs/index.d.cts +28 -0
  23. package/storage/types-cjs/package.json +3 -0
  24. package/types/client.d.ts +64 -21
  25. package/types/core.d.ts +3 -3
  26. package/types/index.d.ts +156 -24
  27. package/types/jsx-properties.d.ts +93 -0
  28. package/types/jsx.d.ts +4135 -1
  29. package/types/response.d.ts +93 -0
  30. package/types/serializer.d.ts +139 -0
  31. package/types/server-functions/client.d.ts +137 -0
  32. package/types/server-functions/server.d.ts +307 -0
  33. package/types/server-functions/shared.d.ts +342 -0
  34. package/types/server-mock.d.ts +89 -0
  35. package/types/server.d.ts +123 -28
  36. package/types-cjs/client.d.cts +131 -0
  37. package/types-cjs/core.d.cts +3 -0
  38. package/types-cjs/index.d.cts +178 -0
  39. package/types-cjs/jsx-properties.d.cts +93 -0
  40. package/types-cjs/jsx.d.cts +4135 -0
  41. package/types-cjs/package.json +3 -0
  42. package/types-cjs/response.d.cts +93 -0
  43. package/types-cjs/serializer.d.cts +139 -0
  44. package/types-cjs/server-functions/client.d.cts +137 -0
  45. package/types-cjs/server-functions/server.d.cts +307 -0
  46. package/types-cjs/server-functions/shared.d.cts +342 -0
  47. package/types-cjs/server-mock.d.cts +161 -0
  48. package/types-cjs/server.d.cts +251 -0
  49. package/storage/types/src/client.d.ts +0 -1
  50. package/storage/types/src/index.d.ts +0 -46
  51. package/storage/types/src/server-mock.d.ts +0 -72
  52. package/storage/types/storage/src/index.d.ts +0 -2
@@ -0,0 +1,28 @@
1
+ import type { RequestEvent } from "@solidjs/web";
2
+ /**
3
+ * Establishes the request-event scope for a server request: everything
4
+ * `cb` runs (across `await`s, via AsyncLocalStorage) sees `init` from
5
+ * `getRequestEvent()`. Call it at the top of the server's request handling,
6
+ * wrapping SSR and server-function dispatch; the server-functions runtime
7
+ * also picks the scope up automatically as its default event provider.
8
+ *
9
+ * Lives on its own subpath because it imports `node:async_hooks` — keep it
10
+ * out of environments without that module.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { provideRequestEvent } from "@solidjs/web/storage";
15
+ *
16
+ * async function handler(request: Request) {
17
+ * return provideRequestEvent({ request, locals: {} }, () =>
18
+ * renderToStringAsync(() => <App />)
19
+ * );
20
+ * }
21
+ * ```
22
+ *
23
+ * @param init the event for this request — frameworks pass their richer
24
+ * event shapes
25
+ * @param cb runs synchronously; its return value is passed through
26
+ * @throws on the client, where there is no request to scope
27
+ */
28
+ export declare function provideRequestEvent<T extends RequestEvent, U>(init: T, cb: () => U): U;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
package/types/client.d.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { JSX } from "./jsx.js";
2
- export const Properties: Set<string>;
2
+ export const DOMWithState: Record<string, Record<string, 1 | 2>>;
3
3
  export const ChildProperties: Set<string>;
4
4
  export const DelegatedEvents: Set<string>;
5
5
  export const DOMElements: Set<string>;
6
6
  export const SVGElements: Set<string>;
7
- export const SVGNamespace: Record<string, string>;
7
+ export const MathMLElements: Set<string>;
8
+ export const VoidElements: Set<string>;
9
+ export const RawTextElements: Set<string>;
10
+ export const Namespaces: Record<string, string>;
8
11
 
9
12
  type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;
10
13
  export function render(
@@ -13,42 +16,60 @@ export function render(
13
16
  init?: JSX.Element,
14
17
  options?: { owner?: unknown }
15
18
  ): () => void;
16
- export function template(html: string, isImportNode?: boolean, isSVG?: boolean, isMathML?: boolean): () => Element;
17
- export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void, init?: T): void;
19
+ /**
20
+ * @param flag
21
+ * - `undefined` — clone the template as-is (uses `cloneNode`).
22
+ * - `1` — use `document.importNode` instead of `cloneNode`.
23
+ * - `2` — the template html is wrapped; the outer tag is stripped at clone time.
24
+ */
25
+ export function template(html: string, flag?: 1 | 2): () => Element;
26
+ export function scope<T extends () => any>(fn: T): T;
27
+ export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void): void;
18
28
  export function memo<T>(fn: () => T, equal: boolean): () => T;
19
29
  export function untrack<T>(fn: () => T): T;
20
30
  export function insert<T>(
21
31
  parent: MountableElement,
22
32
  accessor: (() => T) | T,
23
33
  marker?: Node | null,
24
- init?: JSX.Element
34
+ init?: JSX.Element,
35
+ options?: {
36
+ /**
37
+ * Live accessor for the slot's logical host in the source tree (portals).
38
+ * Each top-level node the slot manages is tagged with a `_$host` getter
39
+ * backed by this accessor so delegated events retarget correctly.
40
+ */
41
+ host?: () => Node | null;
42
+ /** Defer the insert effect to the queue instead of running it inline. */
43
+ schedule?: boolean;
44
+ }
25
45
  ): JSX.Element;
26
46
  export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element;
27
- export function delegateEvents(eventNames: string[], d?: Document): void;
28
- export function clearDelegatedEvents(d?: Document): void;
29
- export function spread<T>(
30
- node: Element,
31
- accessor: T,
32
- isSVG?: Boolean,
33
- skipChildren?: Boolean
47
+ export function delegateEvents(eventNames: string[]): void;
48
+ export function registerDelegatedRoot(root: MountableElement): void;
49
+ export function unregisterDelegatedRoot(root: MountableElement): void;
50
+ export function registerDelegatedContainer(
51
+ container: MountableElement,
52
+ owner?: MountableElement
53
+ ): void;
54
+ export function unregisterDelegatedContainer(
55
+ container: MountableElement,
56
+ owner?: MountableElement
34
57
  ): void;
58
+ export function getDelegatedRoot(node: MountableElement): MountableElement | undefined;
59
+ export function spread<T>(node: Element, accessor: T, skipChildren?: Boolean): void;
35
60
  export function assign(
36
61
  node: Element,
37
62
  props: any,
38
- isSVG?: Boolean,
39
63
  skipChildren?: Boolean,
40
64
  prevProps?: any,
41
65
  skipRef?: Boolean
42
66
  ): void;
43
67
  export function setAttribute(node: Element, name: string, value: string): void;
44
68
  export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
45
- type ClassList =
46
- | Record<string, boolean>
47
- | Array<string | number | boolean | null | undefined | Record<string, boolean>>;
48
- export function className(node: Element, value: string | ClassList, isSvg?: boolean, prev?: string | ClassList): void;
69
+ export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
49
70
  export function setProperty(node: Element, name: string, value: any): void;
50
71
  export function setStyleProperty(node: Element, name: string, value: any): void;
51
- export function addEventListener(
72
+ export function addEvent(
52
73
  node: Element,
53
74
  name: string,
54
75
  handler: EventListener | EventListenerObject | (EventListenerObject & AddEventListenerOptions),
@@ -62,8 +83,14 @@ export function style(
62
83
  export function getOwner(): unknown;
63
84
  export function mergeProps(...sources: unknown[]): unknown;
64
85
  export function dynamicProperty(props: unknown, key: string): unknown;
65
- export function applyRef(r: ((element: Element) => void) | ((element: Element) => void)[], element: Element): void;
66
- export function ref(fn: () => ((element: Element) => void) | ((element: Element) => void)[], element: Element): void;
86
+ export function applyRef(
87
+ r: ((element: Element) => void) | ((element: Element) => void)[],
88
+ element: Element
89
+ ): void;
90
+ export function ref(
91
+ fn: () => ((element: Element) => void) | ((element: Element) => void)[],
92
+ element: Element
93
+ ): void;
67
94
 
68
95
  export function hydrate(
69
96
  fn: () => JSX.Element,
@@ -76,8 +103,24 @@ export function getNextMatch(start: Node, elementName: string): Element;
76
103
  export function getNextMarker(start: Node): [Node, Array<Node>];
77
104
  export function useAssets(fn: () => JSX.Element): void;
78
105
  export function getAssets(): string;
106
+ export type AssetDescriptor =
107
+ | { type: "style"; href: string; attrs?: Record<string, string> }
108
+ | { type: "inline-style"; id: string; content?: string; attrs?: Record<string, string> }
109
+ | { type: "module"; href: string }
110
+ | ExclusiveAssetDescriptor<any>;
111
+ export interface ExclusiveAssetDescriptor<T> {
112
+ policy: "exclusive";
113
+ key: string;
114
+ value: T;
115
+ get(): T;
116
+ set(value: T): void;
117
+ }
118
+ export function acquireAsset(descriptor: AssetDescriptor): () => void;
79
119
  export function HydrationScript(props?: { nonce?: string; eventNames?: string[] }): JSX.Element;
80
- export function generateHydrationScript(options?: { nonce?: string; eventNames?: string[] }): string;
120
+ export function generateHydrationScript(options?: {
121
+ nonce?: string;
122
+ eventNames?: string[];
123
+ }): string;
81
124
  export function Assets(props: { children?: JSX.Element }): JSX.Element;
82
125
  export interface RequestEvent {
83
126
  request: Request;
package/types/core.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrRunInScope } from "solid-js";
2
- export declare const effect: (fn: any, effectFn: any, initial: any) => void;
3
- export declare const memo: (fn: any, transparent: any) => any;
1
+ export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope } from "solid-js";
2
+ export declare const effect: (fn: any, effectFn: any, options: any) => void;
3
+ export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
package/types/index.d.ts CHANGED
@@ -1,46 +1,178 @@
1
- import { hydrate as hydrateCore, render as renderCore } from "./client.js";
2
- import { JSX, ComponentProps, ValidComponent } from "solid-js";
1
+ import { hydrate as hydrateCore } from "./client.js";
2
+ import { Component } from "solid-js";
3
+ import type { JSX } from "./jsx.js";
3
4
  export * from "./client.js";
4
- export declare function render(...args: Parameters<typeof renderCore>): ReturnType<typeof renderCore>;
5
- export { For, Show, Switch, Match, Errored, Loading, NoHydration, Hydration, merge as mergeProps } from "solid-js";
6
5
  export * from "./server-mock.js";
6
+ export * from "./response.js";
7
+ export type { JSX } from "./jsx.js";
8
+ export { For, Show, Switch, Match, Errored, Loading, Repeat, Reveal, NoHydration, Hydration } from "solid-js";
9
+ import { merge } from "solid-js";
10
+ /**
11
+ * Compiler-emitted prop-spread helper. The JSX transform (in
12
+ * `dom-expressions`) emits `mergeProps(...)` calls when compiling prop
13
+ * spreads on components — it is *not* a user-facing API. Application code
14
+ * should import `merge` from `solid-js` directly.
15
+ *
16
+ * @internal
17
+ */
18
+ export declare const mergeProps: typeof merge;
19
+ /**
20
+ * Build-time constant indicating whether code is running on the server. This
21
+ * client entry sets it to `false`; the matching server entry (`@solidjs/web`
22
+ * resolved through the `solid` server export condition) sets it to `true`.
23
+ *
24
+ * Bundlers can dead-code-eliminate branches gated on `isServer`, so guarding
25
+ * browser-only code with `if (!isServer) {…}` keeps it out of the SSR bundle
26
+ * entirely.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { isServer } from "@solidjs/web";
31
+ *
32
+ * if (!isServer) {
33
+ * // Browser-only: tree-shaken out of the SSR bundle.
34
+ * window.addEventListener("resize", onResize);
35
+ * }
36
+ * ```
37
+ */
7
38
  export declare const isServer: boolean;
39
+ /**
40
+ * Build-time constant indicating whether code is running in a dev build.
41
+ * Replaced statically (`_SOLID_DEV_`) by the bundler integration, so guards
42
+ * like `if (isDev) {…}` are stripped from production builds.
43
+ *
44
+ * Use this to gate dev-only diagnostics, warnings, or expensive invariants
45
+ * that should never ship to production.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { isDev } from "@solidjs/web";
50
+ *
51
+ * if (isDev) {
52
+ * console.warn("debug-only path");
53
+ * }
54
+ * ```
55
+ */
8
56
  export declare const isDev: boolean;
57
+ type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;
58
+ export type IntrinsicElement = Extract<keyof JSX.IntrinsicElements, string>;
59
+ export type ValidComponent = IntrinsicElement | Component<any> | (string & {});
60
+ export type ComponentProps<T extends ValidComponent> = T extends Component<infer P> ? P : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : Record<string, unknown>;
61
+ export type DynamicProps<T extends ValidComponent, P = ComponentProps<T>> = {
62
+ [K in keyof P]: P[K];
63
+ } & {
64
+ component: T | null | undefined | false;
65
+ };
66
+ /**
67
+ * Renders a component tree into a DOM element. Returns a dispose function
68
+ * that tears the tree down and cleans up reactive scopes when called.
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * import { render } from "@solidjs/web";
73
+ *
74
+ * const dispose = render(() => <App />, document.getElementById("root")!);
75
+ *
76
+ * // Later, to unmount:
77
+ * dispose();
78
+ * ```
79
+ *
80
+ * @remarks
81
+ * The top-level insert is queued via `insertOptions: { schedule: true }` so
82
+ * its initial DOM attach goes through the effect queue rather than executing
83
+ * inline. This lets the mount participate in transitions: if an uncaught
84
+ * async read surfaces during the initial render (no `Loading` ancestor
85
+ * absorbs it), the mount is held by the transition and attaches atomically
86
+ * once all pending settles. On the no-async happy path the tail `flush()`
87
+ * drains the queued callback so the attach is synchronous by the time
88
+ * `render()` returns. The dev enforcement window scopes
89
+ * `ASYNC_OUTSIDE_LOADING_BOUNDARY` to the initial mount only.
90
+ */
91
+ export declare function render(code: () => JSX.Element, element: MountableElement, init?: unknown, options?: {
92
+ renderId?: string;
93
+ }): () => void;
94
+ /**
95
+ * Resumes a server-rendered tree on the client, attaching event listeners
96
+ * and reactive bindings without reconstructing the DOM. Returns a `dispose`
97
+ * function that tears down reactive scopes (DOM nodes are left in place).
98
+ *
99
+ * Use this when the page HTML was produced by `renderToString`,
100
+ * `renderToStringAsync`, or `renderToStream`. For client-only apps, use
101
+ * `render` instead.
102
+ *
103
+ * Pass `options.renderId` to hydrate one of multiple roots emitted by a
104
+ * server render that used the same id.
105
+ *
106
+ * @example
107
+ * ```tsx
108
+ * import { hydrate } from "@solidjs/web";
109
+ *
110
+ * hydrate(() => <App />, document.getElementById("root")!);
111
+ * ```
112
+ */
9
113
  export declare const hydrate: typeof hydrateCore;
10
114
  /**
11
- * Renders components somewhere else in the DOM
115
+ * Renders its children into a different part of the DOM (modal roots,
116
+ * tooltips, layers that need to escape an `overflow: hidden` ancestor).
12
117
  *
13
- * Useful for inserting modals and tooltips outside of an cropping layout. If no mount point is given, the portal is inserted in document.body; it is wrapped in a `<div>` unless the target is document.head or `isSVG` is true. setting `useShadow` to true places the element in a shadow root to isolate styles.
118
+ * If `mount` is omitted, the portal attaches to `document.body`. The portal
119
+ * still participates in the parent's reactive scope and disposes when the
120
+ * parent does.
121
+ *
122
+ * Portals are client-only islands: the server renders nothing for them, and
123
+ * under hydration the children render fresh once hydration settles. Async
124
+ * read inside a portal therefore starts on the client — data that should be
125
+ * fetched on the server belongs above the portal (hoist the read, not the
126
+ * render), and async UI inside one wants its own `<Loading>` boundary.
127
+ *
128
+ * @example
129
+ * ```tsx
130
+ * <Portal mount={document.getElementById("modal-root")!}>
131
+ * <Dialog />
132
+ * </Portal>
133
+ * ```
14
134
  *
15
135
  * @description https://docs.solidjs.com/reference/components/portal
16
136
  */
17
- export declare function Portal<T extends boolean = false, S extends boolean = false>(props: {
137
+ export declare function Portal(props: {
18
138
  mount?: Element;
19
139
  children: JSX.Element;
20
- }): Text;
21
- export type DynamicProps<T extends ValidComponent, P = ComponentProps<T>> = {
22
- [K in keyof P]: P[K];
23
- } & {
24
- component: T | undefined;
25
- };
140
+ }): JSX.Element;
26
141
  /**
27
- * Renders an arbitrary component or element with the given props
142
+ * Returns a stable `Component` whose identity is driven by a reactive (and
143
+ * optionally async) `source`. The returned component can be used anywhere a
144
+ * normal component is used; children and props flow through JSX as usual.
145
+ *
146
+ * `source` may return a component, a native tag name (`'input'`, `'textarea'`,
147
+ * etc.), `undefined`, or a `Promise` of any of the above. A pending promise
148
+ * propagates as `NotReadyError` through the surrounding reactive scope, so
149
+ * async swaps compose with `<Loading>` boundaries the same way as `lazy`.
28
150
  *
29
- * This is a lower level version of the `Dynamic` component, useful for
30
- * performance optimizations in libraries. Do not use this unless you know
31
- * what you are doing.
32
- * ```typescript
33
- * const element = () => multiline() ? 'textarea' : 'input';
34
- * createDynamic(element, { value: value() });
151
+ * @example
152
+ * ```tsx
153
+ * // `source` can return either a custom Component or a native tag
154
+ * // name — they're interchangeable, and the returned reference is a
155
+ * // stable Component you can use anywhere a normal one would go.
156
+ * const Field = dynamic(() => multiline() ? RichTextEditor : "input");
157
+ * return <Field value={value()} onInput={onInput} />;
35
158
  * ```
159
+ *
36
160
  * @description https://docs.solidjs.com/reference/components/dynamic
37
161
  */
38
- export declare function createDynamic<T extends ValidComponent>(component: () => T | undefined, props: ComponentProps<T>): JSX.Element;
162
+ export declare function dynamic<T extends ValidComponent>(source: () => T | Promise<T> | null | undefined | false): Component<ComponentProps<T>>;
39
163
  /**
40
- * Renders an arbitrary custom or native component and passes the other props
41
- * ```typescript
42
- * <Dynamic component={multiline() ? 'textarea' : 'input'} value={value()} />
164
+ * Renders an arbitrary custom or native component and forwards the other
165
+ * props. JSX form of `dynamic()` — same primitive, picked at the JSX site.
166
+ *
167
+ * @example
168
+ * ```tsx
169
+ * <Dynamic
170
+ * component={multiline() ? RichTextEditor : "input"}
171
+ * value={value()}
172
+ * onInput={onInput}
173
+ * />
43
174
  * ```
175
+ *
44
176
  * @description https://docs.solidjs.com/reference/components/dynamic
45
177
  */
46
178
  export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>): JSX.Element;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Shared type-level helpers used to derive `prop:*` attribute typings from
3
+ * DOM element interfaces (e.g. `HTMLInputElement`, `HTMLButtonElement`).
4
+ *
5
+ * The wrapping of each value (`FunctionMaybe<T>` in `jsx-h.d.ts` vs. the
6
+ * raw value in `jsx.d.ts`) is applied by each consumer when composing its
7
+ * own `Properties<T>` mapped type. That way this file stays identical in
8
+ * both reactive and non-reactive contexts and only needs to exist once.
9
+ *
10
+ * originally from
11
+ * @url https://github.com/potahtml/pota
12
+ */
13
+
14
+ /** Base-class properties shared by all elements — skipped from `prop:*`. */
15
+ export type SkipPropsFrom = HTMLUnknownElement & HTMLElement & Element & Node;
16
+
17
+ /**
18
+ * Value types allowed on a `prop:*`. Primitives plus the writable
19
+ * non-primitive DOM-object props worth exposing:
20
+ *
21
+ * - `HTMLMediaElement.srcObject`
22
+ * - `HTMLButtonElement.popoverTargetElement` / `commandForElement` (and the same via
23
+ * `PopoverTargetAttributes` mixin on `HTMLInputElement`)
24
+ */
25
+ export type PropValue =
26
+ | string
27
+ | number
28
+ | boolean
29
+ | null
30
+ | MediaStream
31
+ | MediaSource
32
+ | Blob
33
+ | File
34
+ | Date
35
+ | Element;
36
+
37
+ /**
38
+ * Ergonomics widening for emitted `prop:*` value types:
39
+ *
40
+ * - general `string` → `string | number` (HTML coerces numbers)
41
+ * - string literal unions (`'on' | 'off'`) stay exact, so users still get autocomplete /
42
+ * narrowing
43
+ * - other types pass through unchanged
44
+ */
45
+ type WidenString<V> = string extends V ? string | number : V;
46
+ export type WidenPropValue<V> = [V] extends [string] ? WidenString<V> : V;
47
+
48
+ /**
49
+ * Structurally identical → `Y`; distinct → `N`. Used by `IsReadonlyKey` to detect
50
+ * readonly keys by comparing `Pick<T, K>` with `Readonly<Pick<T, K>>`.
51
+ */
52
+ export type IfEquals<A, B, Y = unknown, N = never> =
53
+ (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
54
+
55
+ /**
56
+ * True when `K` is readonly on `T`. Singleton-constant properties (e.g.
57
+ * `tagName: "INPUT"`, `nodeType: 1`) are always `readonly` in `lib.dom.d.ts`, so this
58
+ * single check covers both readonly and singleton-literal cases.
59
+ */
60
+ export type IsReadonlyKey<T, K extends keyof T> = IfEquals<
61
+ Pick<T, K>,
62
+ Readonly<Pick<T, K>>,
63
+ true,
64
+ false
65
+ >;
66
+
67
+ /**
68
+ * Resolves to the `prop:K` string literal when `K` is a writable, element-specific
69
+ * property suitable for a `prop:*` attribute; otherwise resolves to `never` so the
70
+ * key is filtered out of the resulting mapped type.
71
+ *
72
+ * Filters out:
73
+ *
74
+ * - base-class keys (via `SkipPropsFrom`)
75
+ * - aria-* keys (already typed via `AriaAttributes`)
76
+ * - readonly keys
77
+ * - keys whose value types fall outside `PropValue`
78
+ * - the generic `string` index signature (e.g. `HTMLFormElement[name: string]: any`),
79
+ * which would otherwise shadow every key with an `any`-typed `prop:*`
80
+ */
81
+ export type PropKey<T, K extends keyof T> = K extends keyof SkipPropsFrom
82
+ ? never
83
+ : K extends string
84
+ ? string extends K
85
+ ? never
86
+ : K extends `aria${string}`
87
+ ? never
88
+ : T[K] extends PropValue
89
+ ? IsReadonlyKey<T, K> extends true
90
+ ? never
91
+ : `prop:${K}`
92
+ : never
93
+ : never;