@comms-id/abn-react 0.2.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/LICENSE +15 -0
- package/README.md +62 -0
- package/dist/abn-autocomplete.d.ts +21 -0
- package/dist/abn-autocomplete.js +96 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/use-abn.d.ts +23 -0
- package/dist/use-abn.js +85 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Copyright (c) 2026 COMMS.ID PTY LTD. Proprietary software; not open source.
|
|
2
|
+
|
|
3
|
+
Subject to the Comms.ID policy agreement, you may install, copy, bundle, run and
|
|
4
|
+
deliver this client or component within your applications, adapt the supplied
|
|
5
|
+
component source, and use the designated local functions and fixtures offline.
|
|
6
|
+
Hosted operations are provided by the Comms.ID service.
|
|
7
|
+
|
|
8
|
+
You may keep replies for your own customers and transactions, and your registered
|
|
9
|
+
application may relay requests for its own users.
|
|
10
|
+
|
|
11
|
+
You may not republish these packages as a standalone offering, resell access, offer
|
|
12
|
+
a substitute lookup service, or harvest replies to reconstruct or redistribute a
|
|
13
|
+
source dataset.
|
|
14
|
+
|
|
15
|
+
Preserve this notice and any applicable third-party notices.
|
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @comms-id/abn-react
|
|
2
|
+
|
|
3
|
+
A React hook and a finished component for Comms.ID ABN search. The user types a business name, or an ABN or ACN. Accessible, no layout shift, with the data attribution always shown. React 18 or later; it depends on `@comms-id/abn` and nothing else.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { createAbnBrowserClient } from "@comms-id/abn/browser";
|
|
7
|
+
import { AbnAutocomplete } from "@comms-id/abn-react";
|
|
8
|
+
|
|
9
|
+
const client = createAbnBrowserClient({ relayUrl: "/api/comms-id/abn" });
|
|
10
|
+
|
|
11
|
+
export const Supplier = () => (
|
|
12
|
+
<AbnAutocomplete
|
|
13
|
+
client={client}
|
|
14
|
+
label="Business name or ABN"
|
|
15
|
+
onSelect={(business) => console.log(business.name, business.formattedAbn, business.status)}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`relayUrl` is your own server route, set up with `@comms-id/relay`: your server signs the call as your registered app, so the browser holds no credential.
|
|
21
|
+
|
|
22
|
+
## What the field does
|
|
23
|
+
|
|
24
|
+
- **A name:** after the user pauses, it suggests businesses (name, ABN, state and postcode). Choosing one looks it up and calls `onSelect`.
|
|
25
|
+
- **An ABN or ACN:** a whole number (9 or 11 digits, spaces and hyphens allowed) is checked in the browser first. An invalid number sends nothing, and the field says why ("This is not a valid ABN: the check digits do not match."). A valid number is looked up.
|
|
26
|
+
- **No match is not an outage.** "No business found." and "Business search is unavailable" are different messages; an outage offers "Try again".
|
|
27
|
+
|
|
28
|
+
`onSelect(business, timing)` gets one `AbnBusiness` whichever source answered the lookup: `abn`, `formattedAbn`, `acn`, `name`, `status`, `entityType`, `gstRegistered`, `state`, `postcode`, `source` (`live` or `snapshot`), `asAt` and `attribution`.
|
|
29
|
+
|
|
30
|
+
## Your own markup: `useAbn`
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
const abn = useAbn({ client, onSelect });
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<input {...abn.getInputProps()} />
|
|
37
|
+
<ul {...abn.getListboxProps("Matches")} hidden={!abn.state.open}>
|
|
38
|
+
{abn.state.suggestions.map((s, i) => (
|
|
39
|
+
<li key={`${s.abn}-${i}`} {...abn.getOptionProps(i)}>{describeSuggestion(s).primary}</li>
|
|
40
|
+
))}
|
|
41
|
+
</ul>
|
|
42
|
+
<p {...abn.getStatusProps()}>{abn.state.announcement}</p>
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The prop getters carry the WAI-ARIA combobox contract: roles, `aria-expanded`, `aria-controls`, `aria-activedescendant`, key handling (arrows, Enter, Escape, Tab) and a polite live region. Show the attribution yourself when you use the hook: `abn.state.attribution`.
|
|
48
|
+
|
|
49
|
+
## The component
|
|
50
|
+
|
|
51
|
+
Props: `client` and `label` (required), `onSelect`, `minChars` (3), `debounceMs` (200), `limit` (8), `prefer` (`{ state, postcode }`), `idPrefix`, `name`, `placeholder`, `disabled`, `required`, `className`, `listLabel`, `showTiming` (true), `renderOption`.
|
|
52
|
+
|
|
53
|
+
Style it with the custom properties `--comms-id-text`, `--comms-id-surface`, `--comms-id-border`, `--comms-id-active`, `--comms-id-radius` or the `comms-id-abn__*` class names. It needs no stylesheet.
|
|
54
|
+
|
|
55
|
+
- The list floats under the input; opening it moves nothing. One note line has a reserved height and does not wrap: the result is a summary (name, ABN, status, GST), not a card, so the page never moves. Use `onSelect` for the whole record.
|
|
56
|
+
- It renders on the server with the same structure, and works under StrictMode.
|
|
57
|
+
|
|
58
|
+
## Data attribution
|
|
59
|
+
|
|
60
|
+
The component always shows the attribution: the one the reply carried, or the standard text when a reply has none. Do not hide it.
|
|
61
|
+
|
|
62
|
+
The package is private until the publishing path exists (#515). `LICENSE` is the consumer client licence.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AbnSuggestion } from "@comms-id/abn/controller";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { type UseAbnOptions } from "./use-abn.js";
|
|
4
|
+
export interface AbnAutocompleteProps extends UseAbnOptions {
|
|
5
|
+
readonly className?: string;
|
|
6
|
+
readonly disabled?: boolean;
|
|
7
|
+
/** The visible label of the field. Required: a field without a label is not accessible. */
|
|
8
|
+
readonly label: string;
|
|
9
|
+
/** The accessible name of the list of suggestions. */
|
|
10
|
+
readonly listLabel?: string;
|
|
11
|
+
readonly name?: string;
|
|
12
|
+
readonly placeholder?: string;
|
|
13
|
+
/** Your own content for one suggestion. */
|
|
14
|
+
readonly renderOption?: (suggestion: AbnSuggestion, state: {
|
|
15
|
+
readonly active: boolean;
|
|
16
|
+
}) => ReactNode;
|
|
17
|
+
readonly required?: boolean;
|
|
18
|
+
/** Show how long the lookup took. True by default. */
|
|
19
|
+
readonly showTiming?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare const AbnAutocomplete: (props: AbnAutocompleteProps) => ReactNode;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// <AbnAutocomplete>: the ABN field as one component. The user types a business name, or an ABN
|
|
3
|
+
// or ACN. It is accessible (WAI-ARIA combobox), it never shifts the page (the list floats, every
|
|
4
|
+
// other line has a fixed place), and it always shows the data attribution. Style it with the
|
|
5
|
+
// --comms-id-* variables or the comms-id-abn class names; it needs no stylesheet.
|
|
6
|
+
import { describeBusiness, describeSuggestion } from "@comms-id/abn/controller";
|
|
7
|
+
import { useAbn } from "./use-abn.js";
|
|
8
|
+
const HIDDEN_FROM_SIGHT = {
|
|
9
|
+
position: "absolute",
|
|
10
|
+
width: 1,
|
|
11
|
+
height: 1,
|
|
12
|
+
margin: -1,
|
|
13
|
+
padding: 0,
|
|
14
|
+
overflow: "hidden",
|
|
15
|
+
clip: "rect(0 0 0 0)",
|
|
16
|
+
whiteSpace: "nowrap",
|
|
17
|
+
border: 0,
|
|
18
|
+
};
|
|
19
|
+
const ROOT = { position: "relative", color: "var(--comms-id-text, inherit)", font: "inherit" };
|
|
20
|
+
const LABEL = { display: "block", marginBottom: 4 };
|
|
21
|
+
const FIELD = { position: "relative" };
|
|
22
|
+
const INPUT = {
|
|
23
|
+
boxSizing: "border-box",
|
|
24
|
+
width: "100%",
|
|
25
|
+
padding: "0.5rem 0.75rem",
|
|
26
|
+
font: "inherit",
|
|
27
|
+
color: "inherit",
|
|
28
|
+
background: "var(--comms-id-surface, #fff)",
|
|
29
|
+
border: "1px solid var(--comms-id-border, #767676)",
|
|
30
|
+
borderRadius: "var(--comms-id-radius, 4px)",
|
|
31
|
+
};
|
|
32
|
+
// The list floats under the input, so opening and closing it moves nothing else on the page.
|
|
33
|
+
const LIST = {
|
|
34
|
+
position: "absolute",
|
|
35
|
+
top: "100%",
|
|
36
|
+
left: 0,
|
|
37
|
+
right: 0,
|
|
38
|
+
zIndex: 1000,
|
|
39
|
+
maxHeight: "16rem",
|
|
40
|
+
overflowY: "auto",
|
|
41
|
+
margin: "4px 0 0",
|
|
42
|
+
padding: 0,
|
|
43
|
+
listStyle: "none",
|
|
44
|
+
background: "var(--comms-id-surface, #fff)",
|
|
45
|
+
color: "var(--comms-id-text, #111)",
|
|
46
|
+
border: "1px solid var(--comms-id-border, #767676)",
|
|
47
|
+
borderRadius: "var(--comms-id-radius, 4px)",
|
|
48
|
+
boxShadow: "0 4px 12px rgb(0 0 0 / 15%)",
|
|
49
|
+
};
|
|
50
|
+
const OPTION = { padding: "0.5rem 0.75rem", cursor: "pointer" };
|
|
51
|
+
// One line with a fixed height: a found business, a message, or an error never moves what follows.
|
|
52
|
+
const NOTE = {
|
|
53
|
+
minHeight: "1.6em",
|
|
54
|
+
margin: "4px 0 0",
|
|
55
|
+
fontSize: "0.85em",
|
|
56
|
+
lineHeight: "1.5em",
|
|
57
|
+
overflow: "hidden",
|
|
58
|
+
textOverflow: "ellipsis",
|
|
59
|
+
whiteSpace: "nowrap",
|
|
60
|
+
};
|
|
61
|
+
const ATTRIBUTION_TEXT = { margin: "2px 0 0", fontSize: "0.75em", lineHeight: 1.4, opacity: 0.8 };
|
|
62
|
+
const FOOTER = {
|
|
63
|
+
...ATTRIBUTION_TEXT,
|
|
64
|
+
padding: "0.5rem 0.75rem",
|
|
65
|
+
borderTop: "1px solid var(--comms-id-border, #767676)",
|
|
66
|
+
};
|
|
67
|
+
const defaultOption = (suggestion) => {
|
|
68
|
+
const text = describeSuggestion(suggestion);
|
|
69
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "comms-id-abn__name", children: text.primary }), _jsx("span", { className: "comms-id-abn__secondary", style: { display: "block", fontSize: "0.85em", opacity: 0.75 }, children: text.secondary })] }));
|
|
70
|
+
};
|
|
71
|
+
const noteFor = (state, showTiming, retry) => {
|
|
72
|
+
if (state.status === "error" && state.error !== undefined) {
|
|
73
|
+
return (_jsxs(_Fragment, { children: [state.error.message, " ", state.error.retryable ? (_jsx("button", { className: "comms-id-abn__retry", onClick: retry, type: "button", children: "Try again" })) : null] }));
|
|
74
|
+
}
|
|
75
|
+
if (state.status === "invalid" && state.invalid !== undefined) {
|
|
76
|
+
return state.invalid.message;
|
|
77
|
+
}
|
|
78
|
+
if (state.status === "empty") {
|
|
79
|
+
return "No business found.";
|
|
80
|
+
}
|
|
81
|
+
if (state.status === "resolved" && state.resolved !== undefined) {
|
|
82
|
+
const found = describeBusiness(state.resolved.business);
|
|
83
|
+
return showTiming ? `${found} (${state.resolved.timing.ms} ms)` : found;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
export const AbnAutocomplete = (props) => {
|
|
88
|
+
const { label, name, placeholder, disabled, required, className, listLabel, showTiming, renderOption, ...options } = props;
|
|
89
|
+
const abn = useAbn(options);
|
|
90
|
+
const { state } = abn;
|
|
91
|
+
const note = noteFor(state, showTiming !== false, abn.retry);
|
|
92
|
+
return (_jsxs("div", { className: className === undefined ? "comms-id-abn" : `comms-id-abn ${className}`, style: ROOT, children: [_jsx("label", { className: "comms-id-abn__label", htmlFor: abn.ids.input, style: LABEL, children: label }), _jsxs("div", { style: FIELD, children: [_jsx("input", { ...abn.getInputProps(), className: "comms-id-abn__input", disabled: disabled, name: name, placeholder: placeholder, required: required, style: INPUT }), _jsxs("ul", { ...abn.getListboxProps(listLabel ?? "Business suggestions"), className: "comms-id-abn__list", hidden: !state.open, style: LIST, children: [state.suggestions.map((suggestion, index) => {
|
|
93
|
+
const active = index === state.activeIndex;
|
|
94
|
+
return (_jsx("li", { ...abn.getOptionProps(index), className: "comms-id-abn__option", style: { ...OPTION, background: active ? "var(--comms-id-active, #e8f0fe)" : "transparent" }, children: renderOption === undefined ? defaultOption(suggestion) : renderOption(suggestion, { active }) }, `${suggestion.abn}-${suggestion.nameType}-${suggestion.name}`));
|
|
95
|
+
}), _jsx("li", { "aria-hidden": "true", className: "comms-id-abn__list-attribution", style: FOOTER, children: state.attribution })] })] }), _jsx("span", { ...abn.getStatusProps(), style: HIDDEN_FROM_SIGHT, children: state.announcement }), _jsx("p", { "aria-hidden": "true", className: "comms-id-abn__note", style: NOTE, children: note }), _jsx("p", { className: "comms-id-abn__attribution", style: ATTRIBUTION_TEXT, children: state.attribution })] }));
|
|
96
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { AbnBusiness, AbnState, AbnStatus, AbnSuggestion } from "@comms-id/abn/controller";
|
|
2
|
+
export { ATTRIBUTION, describeBusiness, describeSuggestion } from "@comms-id/abn/controller";
|
|
3
|
+
export type { AbnAutocompleteProps } from "./abn-autocomplete.js";
|
|
4
|
+
export { AbnAutocomplete } from "./abn-autocomplete.js";
|
|
5
|
+
export type { UseAbnOptions, UseAbnResult } from "./use-abn.js";
|
|
6
|
+
export { useAbn } from "./use-abn.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AbnControllerOptions, type AbnIds, type AbnState } from "@comms-id/abn/controller";
|
|
2
|
+
export interface UseAbnOptions extends AbnControllerOptions {
|
|
3
|
+
/** Prefix of the element ids. A generated one by default; set it to name the input yourself. */
|
|
4
|
+
readonly idPrefix?: string;
|
|
5
|
+
}
|
|
6
|
+
type Props = Record<string, unknown>;
|
|
7
|
+
export interface UseAbnResult {
|
|
8
|
+
close: () => void;
|
|
9
|
+
/** Props for the text input: role, aria-*, value and the handlers. */
|
|
10
|
+
getInputProps: () => Props;
|
|
11
|
+
/** Props for the list element (role listbox). It must always be rendered; hide it while closed. */
|
|
12
|
+
getListboxProps: (label: string) => Props;
|
|
13
|
+
/** Props for the option at `index` (role option). */
|
|
14
|
+
getOptionProps: (index: number) => Props;
|
|
15
|
+
/** Props for the polite live region that says what just happened. */
|
|
16
|
+
getStatusProps: () => Props;
|
|
17
|
+
readonly ids: AbnIds;
|
|
18
|
+
retry: () => void;
|
|
19
|
+
select: (index?: number) => void;
|
|
20
|
+
readonly state: AbnState;
|
|
21
|
+
}
|
|
22
|
+
export declare const useAbn: (options: UseAbnOptions) => UseAbnResult;
|
|
23
|
+
export {};
|
package/dist/use-abn.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// The headless hook: the ABN controller as React state, plus prop getters that carry the
|
|
2
|
+
// WAI-ARIA combobox contract. Use it with your own markup, or use <AbnAutocomplete>.
|
|
3
|
+
import { createAbnController, handleKey, IDLE_STATE, inputAttributes, listboxAttributes, optionAttributes, statusAttributes, } from "@comms-id/abn/controller";
|
|
4
|
+
import { useEffect, useId, useRef, useState, useSyncExternalStore, } from "react";
|
|
5
|
+
const EMPTY = { getState: () => IDLE_STATE, subscribe: () => () => undefined };
|
|
6
|
+
// React's names for three attributes the framework-neutral helpers write in HTML case.
|
|
7
|
+
const REACT_NAMES = {
|
|
8
|
+
autocomplete: "autoComplete",
|
|
9
|
+
autocapitalize: "autoCapitalize",
|
|
10
|
+
spellcheck: "spellCheck",
|
|
11
|
+
};
|
|
12
|
+
const toProps = (attributes) => Object.fromEntries(Object.entries(attributes)
|
|
13
|
+
.filter(([, value]) => value !== undefined)
|
|
14
|
+
.map(([name, value]) => [REACT_NAMES[name] ?? name, value]));
|
|
15
|
+
const COLONS = /:/g;
|
|
16
|
+
export const useAbn = (options) => {
|
|
17
|
+
const generated = useId().replace(COLONS, "");
|
|
18
|
+
const prefix = options.idPrefix ?? `comms-id-abn-${generated}`;
|
|
19
|
+
const ids = { input: `${prefix}-input`, listbox: `${prefix}-list`, status: `${prefix}-status` };
|
|
20
|
+
// The latest options, read at call time, so a new client or callback never recreates the controller.
|
|
21
|
+
const latest = useRef(options);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
latest.current = options;
|
|
24
|
+
});
|
|
25
|
+
const [controller, setController] = useState(undefined);
|
|
26
|
+
const { minChars, debounceMs, limit } = options;
|
|
27
|
+
const preferState = options.prefer?.state;
|
|
28
|
+
const preferPostcode = options.prefer?.postcode;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const created = createAbnController({
|
|
31
|
+
client: {
|
|
32
|
+
suggest: (input, callOptions) => latest.current.client.suggest(input, callOptions),
|
|
33
|
+
lookup: (input, callOptions) => latest.current.client.lookup(input, callOptions),
|
|
34
|
+
},
|
|
35
|
+
...(minChars === undefined ? {} : { minChars }),
|
|
36
|
+
...(debounceMs === undefined ? {} : { debounceMs }),
|
|
37
|
+
...(limit === undefined ? {} : { limit }),
|
|
38
|
+
...(preferState === undefined && preferPostcode === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: {
|
|
41
|
+
prefer: {
|
|
42
|
+
...(preferState === undefined ? {} : { state: preferState }),
|
|
43
|
+
...(preferPostcode === undefined ? {} : { postcode: preferPostcode }),
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
...(latest.current.now === undefined ? {} : { now: () => latest.current.now?.() ?? 0 }),
|
|
47
|
+
onSelect: (business, timing) => latest.current.onSelect?.(business, timing),
|
|
48
|
+
});
|
|
49
|
+
setController(created);
|
|
50
|
+
return () => {
|
|
51
|
+
created.dispose();
|
|
52
|
+
setController(undefined);
|
|
53
|
+
};
|
|
54
|
+
}, [minChars, debounceMs, limit, preferState, preferPostcode]);
|
|
55
|
+
const store = controller ?? EMPTY;
|
|
56
|
+
const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);
|
|
57
|
+
return {
|
|
58
|
+
state,
|
|
59
|
+
ids,
|
|
60
|
+
getInputProps: () => ({
|
|
61
|
+
...toProps(inputAttributes(state, ids)),
|
|
62
|
+
value: state.query,
|
|
63
|
+
onChange: (event) => controller?.setQuery(event.target.value),
|
|
64
|
+
onKeyDown: (event) => {
|
|
65
|
+
if (controller !== undefined && handleKey(controller, event.key)) {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
onFocus: () => controller?.open(),
|
|
70
|
+
onBlur: () => controller?.close(),
|
|
71
|
+
}),
|
|
72
|
+
getListboxProps: (label) => toProps(listboxAttributes(ids, label)),
|
|
73
|
+
getOptionProps: (index) => ({
|
|
74
|
+
...toProps(optionAttributes(state, ids, index)),
|
|
75
|
+
// The input keeps focus while the pointer chooses an option.
|
|
76
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
77
|
+
onMouseMove: () => controller?.setActive(index),
|
|
78
|
+
onClick: () => controller?.select(index),
|
|
79
|
+
}),
|
|
80
|
+
getStatusProps: () => toProps(statusAttributes(ids)),
|
|
81
|
+
select: (index) => controller?.select(index),
|
|
82
|
+
retry: () => controller?.retry(),
|
|
83
|
+
close: () => controller?.close(),
|
|
84
|
+
};
|
|
85
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@comms-id/abn-react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "React hook and component for Comms.ID ABN search: a name or an ABN or ACN, accessible, no layout shift, with the data attribution",
|
|
5
|
+
"homepage": "https://comms.id",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/comms-id/platform/issues"
|
|
8
|
+
},
|
|
9
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
10
|
+
"author": "COMMS.ID PTY LTD",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/comms-id/platform.git",
|
|
14
|
+
"directory": "packages/abn-react"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public",
|
|
33
|
+
"registry": "https://registry.npmjs.org"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@comms-id/abn": "0.2.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "24.19.0",
|
|
43
|
+
"@types/react": "19.3.0",
|
|
44
|
+
"@types/react-dom": "19.3.0",
|
|
45
|
+
"happy-dom": "20.14.5",
|
|
46
|
+
"react": "19.3.0",
|
|
47
|
+
"react-dom": "19.3.0",
|
|
48
|
+
"typescript": "7.0.2",
|
|
49
|
+
"vitest": "5.0.2"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -p tsconfig.build.json",
|
|
53
|
+
"lint": "biome check .",
|
|
54
|
+
"check-types": "tsc -p tsconfig.json --noEmit",
|
|
55
|
+
"test": "vitest run"
|
|
56
|
+
}
|
|
57
|
+
}
|