@colixsystems/widget-sdk 0.113.0 → 0.114.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/README.md +4 -3
- package/dist/contract.cjs +36 -1
- package/dist/contract.js +36 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1 -0
- package/dist/index.native.js +1 -0
- package/dist/interaction.js +16 -0
- package/dist/interaction.native.js +10 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
17
17
|
|
|
18
18
|
| Group | Hook (signature) | Returns | Reads / scope |
|
|
19
19
|
| ----- | ---------------- | ------- | ------------- |
|
|
20
|
-
| **CORE** | `useTheme()` | `{ colors, elevation, spacing, spacingScale, radii, typography, components, widgetStyles }` | `ctx.workspace.theme` — no scope. `elevation` is the shared depth scale (`none / sm / md / lg / xl`) you spread into a style; `colors` includes the accent's quiet tiers (`primarySoft` / `onPrimarySoft` / `primaryStrong`) and `loader`, the spinner colour for your own loading state. `components` is HOST-OWNED (the theme's per-component style tokens); the host has already folded it into your `props.style`, so read `useWidgetStyle()` and ignore this slice. |
|
|
20
|
+
| **CORE** | `useTheme()` | `{ colors, elevation, interaction, spacing, spacingScale, radii, typography, components, widgetStyles }` | `ctx.workspace.theme` — no scope. `elevation` is the shared depth scale (`none / sm / md / lg / xl`) you spread into a style; `colors` includes the accent's quiet tiers (`primarySoft` / `onPrimarySoft` / `primaryStrong`) and `loader`, the spinner colour for your own loading state. `components` is HOST-OWNED (the theme's per-component style tokens); the host has already folded it into your `props.style`, so read `useWidgetStyle()` and ignore this slice. |
|
|
21
21
|
| **CORE** | `useWorkspaceCurrency()` | `{ currency, formatMoney }` | `ctx.workspace.currency` — no scope. The currency this workspace charges its app users in, resolved at RENDER time. Render every price as `formatMoney(minorUnits)` and never write a currency symbol or code into a widget: the owner can change it after the widget ships, and a baked label then contradicts the charge. |
|
|
22
22
|
| **CORE** | `useWidgetStyle()` | `{ [styleField]: value }` | `ctx.props.style` — no scope. The author-set per-widget style values declared in `manifest.styleSchema`; apply each onto whatever element you choose. |
|
|
23
23
|
| **CORE** | `useUser()` | `{ id, email, displayName, roles, groupIds }` | `ctx.user` (host-built context, **camelCase** — not a wire payload; `id` null when anonymous) — no scope |
|
|
@@ -1094,15 +1094,16 @@ A widget that works but looks unfinished is only half done. `useTheme()` is the
|
|
|
1094
1094
|
- **Contain and elevate.** Wrap a logical unit in a surface: `colors.surface` + padding + `radii.lg` + `...theme.elevation.sm`. Give it the elevation **or** a `colors.border` hairline, not both — and prefer the elevation, because a hairline-only card reads as a wireframe. `theme.elevation` is a token table you spread into a style (`...theme.elevation.md`), covering `none / sm / md / lg / xl`; never hand-write `shadowOpacity` / `shadowRadius` / `boxShadow`. Use the status roles (`danger / success / warning / info`) for state.
|
|
1095
1095
|
- **Tint the supporting cast.** `colors.primarySoft` is a tint of the workspace accent over the surface and `colors.onPrimarySoft` is guaranteed readable on it (WCAG AA, on light and dark themes alike). Use the pair for chips, secondary buttons, progress tracks, icon badges and selected rows. One saturated accent moment surrounded by several pale echoes of the same hue is what reads as designed — a row of grey-outlined buttons reads as a form. Never hand-mix a tint with `rgba(...)` or a translucent overlay.
|
|
1096
1096
|
- **Spend one gradient.** `<Gradient colors={[theme.colors.primary, theme.colors.primaryStrong]} angle={160} style={…}>` is a `View` that paints a gradient behind its children, so it replaces the `View` you'd otherwise give a flat `backgroundColor`. `angle` is CSS degrees (0 = to top, 90 = to right, default 180); text on it uses `colors.onPrimary`. Exactly **one** per widget — on the focal element — and never behind body text. Both hosts render it identically (web paints CSS, native uses `expo-linear-gradient`), so there is no per-platform branching to write; don't import `expo-linear-gradient` yourself and don't write a `backgroundImage` string.
|
|
1097
|
+
- **Answer the touch.** Every tappable card, row and list entry lifts while the pointer is over it (web) or it is pressed (touch). One declaration does both: give the Pressable a style FUNCTION and spread `pressableLift` — `<Pressable onPress={open} style={(state) => [styles.card, ...pressableLift(state)]}>`. The lift is a -2px nudge plus one elevation step from `theme.interaction`, with the web transition built in. Never hand-write hover logic or your own pressed shadows, and never fake feedback with `opacity` — a dimmed surface reads as disabling itself.
|
|
1097
1098
|
- **Size to your container — measure it, don't stretch into it.** The same widget sits in a full-width desktop section (~1400px), a half-width grid cell (~700px) and a phone (~360px), so layout built only from `flex: 1` stretches to fill whatever it is handed — a month calendar ends up with 200px day cells and swallows the page. Measure your own width with `onLayout={(e) => setWidth(e.nativeEvent.layout.width)}` on the root `View` (a React Native primitive, so it behaves identically on both hosts), render nothing size-dependent while `width === 0`, and compute every threshold from the measured value: a widget gets no declared breakpoint prop, but it can always measure. **Cap a repeating cell** rather than giving a grid `flex: 1` — `const cell = Math.max(32, Math.min(Math.floor((usable - gap * (columns - 1)) / columns), 64));`, with a calendar day cell topping out at 56–72px on `aspectRatio: 1`, and the grid given its exact computed width plus `alignSelf: 'center'` when the cap leaves slack. **Split two co-equal surfaces above ~720px measured width** (`flexDirection: width >= 720 ? 'row' : 'column'`, each half `{ flex: 1, minWidth: 0 }`) — a picker beside the form it feeds on a wide canvas, stacked in reading order below it. Never hardcode a width, never put `flex: 1` / `height: '100%'` on a content widget's root, and don't read the screen with `Dimensions` — the screen is not the widget.
|
|
1098
1099
|
- **Compose forms — pair fields into rows, don't stack one per row.** Put short, related fields side by side (first + last name, city + postal code, expiry + CVC): a row of `{ flexDirection: 'row', flexWrap: 'wrap', gap: theme.spacing.md }` with each field cell `{ flexGrow: 1, flexBasis: 160 }` splits the width on a wide card and wraps to stacked on a narrow phone — the right recipe for field pairs because it needs no measurement (a fixed-width column overflows a phone; when a layout needs a real column count instead of wrapping, measure your width as above). Keep wide fields (email, address, notes) full-width, cap it at two–three per row, group a long form into labelled sections, and label every input above it (not placeholder-only). Give a `multiline` field BOTH a floor and a ceiling (`{ minHeight: 150, maxHeight: 260 }`) so a long value scrolls inside the box instead of growing past its card, and keep the Save / Cancel row in normal flow below the fields, never positioned over them.
|
|
1099
|
-
- **Respond to touch.** Give every `Pressable`
|
|
1100
|
+
- **Respond to touch.** Give every `Pressable` the lift via the function-style `style={(state) => [base, ...pressableLift(state)]}` — see "Answer the touch" above. Never dim with `opacity`, which reads as the surface disabling itself.
|
|
1100
1101
|
- **Drag and drop — show what is being dragged.** A drag where the item stays put reads as broken. Three things change the moment a drag starts: the **drag proxy** (the item lifts and follows the finger — `...theme.elevation.lg`, `{ scale: 1.03 }`, `opacity: 0.9`; for a tall or full-width item drag a compact `primarySoft` pill with its icon + one line of label instead), the **source placeholder** (the vacated slot keeps its height as a quiet `colors.surfaceMuted` block so the list doesn't collapse), and the **drop target** (one slot at a time highlighted with `primarySoft` or a 2px `colors.primary` border). Always animate the release — settle into the new slot, or `Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false })` back to the origin on cancel. Build it with `Animated` + `PanResponder` from `react-native` (the only mechanism that behaves identically on both hosts) — never HTML5 drag events (`draggable` / `onDragStart` / `dataTransfer` are web-only, and `document` / `window` are banned) — and start the drag from a `GripVertical` grip handle whenever the row is also tappable or sits in a `ScrollView`.
|
|
1101
1102
|
- **Use icons for clarity.** Pair a `lucide-react-native` icon with its label at a consistent size, coloured from the theme. The label never repeats the icon as a character — with a `Plus` icon the button says "Add item", never "+ Add item" (that renders a doubled plus).
|
|
1102
1103
|
- **Use imagery deliberately.** Render pictures with the `Image` primitive (`source` takes a URL or `{ uri }`); resolve workspace assets via `useAsset()`. Give every image a sized, `radii`-clipped container so it never renders as a raw rectangle, and never hardcode a credentialed image URL — expose an `image`-type property instead. The frame is your decision, never the picture's: size it for the role (a 40–56 square avatar, a 72–96 square row thumbnail, a `16 / 9` card cover, a 160–240 tall band) and let `resizeMode="cover"` crop the photo into it — photos arrive at every size and ratio, so one left to its own proportions breaks the layout. Keep `contain` for art whose whole subject must stay visible (a logo, a diagram), inside a fixed frame.
|
|
1103
1104
|
- **Design the empty, loading, and error states.** A blank box on a fresh install reads as broken — show a short helper line when a list is empty, a calm loading line, and a single human sentence in `colors.danger` on error.
|
|
1104
1105
|
|
|
1105
|
-
**Honest ceilings:** the styling surface is React Native style objects, not full CSS. Gradients come from the `<Gradient>` primitive (not a CSS `linear-gradient` string), depth comes from `theme.elevation` (not arbitrary `box-shadow` stacks), and there are no custom CSS keyframe animations or `transition` strings, no `filter` / `backdrop-filter` / `clip-path` / `mask` / blend modes, and no opacity-faked tints (that's what `primarySoft` is for). Aim for clean, confident, professional polish within those bounds — lifted surfaces, generous corners, one accent moment.
|
|
1106
|
+
**Honest ceilings:** the styling surface is React Native style objects, not full CSS. Gradients come from the `<Gradient>` primitive (not a CSS `linear-gradient` string), depth comes from `theme.elevation` (not arbitrary `box-shadow` stacks), and there are no custom CSS keyframe animations or `transition` strings (the hover/press lift comes built into `pressableLift` — never write your own), no `filter` / `backdrop-filter` / `clip-path` / `mask` / blend modes, and no opacity-faked tints (that's what `primarySoft` is for). Aim for clean, confident, professional polish within those bounds — lifted surfaces, generous corners, one accent moment.
|
|
1106
1107
|
|
|
1107
1108
|
## Managing app users from a widget
|
|
1108
1109
|
|
package/dist/contract.cjs
CHANGED
|
@@ -45,6 +45,24 @@ const ELEVATION = Object.freeze({
|
|
|
45
45
|
}),
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
// sc-6531 (REQ-AI-AGENT-DESIGN-LIFT): the ONE cross-platform interaction
|
|
49
|
+
// vocabulary. A tappable surface answers the pointer over it (web) and the
|
|
50
|
+
// press on it (touch) by LIFTING. `raised` is one elevation step up plus a
|
|
51
|
+
// nudge — the scale above stays the only shadow vocabulary — and is what a
|
|
52
|
+
// widget's own surface wears via `pressableLift`; `translateY` alone is the
|
|
53
|
+
// surface-agnostic nudge a tappable layout REGION gets on both hosts.
|
|
54
|
+
const INTERACTION = Object.freeze({
|
|
55
|
+
lift: Object.freeze({
|
|
56
|
+
translateY: -2,
|
|
57
|
+
raised: Object.freeze({
|
|
58
|
+
transform: Object.freeze([Object.freeze({ translateY: -2 })]),
|
|
59
|
+
...ELEVATION.lg,
|
|
60
|
+
}),
|
|
61
|
+
// Web-only smoothing; a touch state change is instant.
|
|
62
|
+
transitionMs: 150,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
48
66
|
const DEFAULT_THEME_TOKENS = Object.freeze({
|
|
49
67
|
colors: Object.freeze({
|
|
50
68
|
primary: "#3b82f6",
|
|
@@ -76,6 +94,7 @@ const DEFAULT_THEME_TOKENS = Object.freeze({
|
|
|
76
94
|
info: "#0284c7",
|
|
77
95
|
}),
|
|
78
96
|
elevation: ELEVATION,
|
|
97
|
+
interaction: INTERACTION,
|
|
79
98
|
spacing: Object.freeze({ xs: 4, sm: 8, md: 16, lg: 24, xl: 32 }),
|
|
80
99
|
// REQ-THEME-LOOK: multiplies every layout spacing value at render. 1 is
|
|
81
100
|
// unchanged, so a theme that never sets it renders exactly as before.
|
|
@@ -1740,6 +1759,17 @@ const PRIMITIVES = [
|
|
|
1740
1759
|
rnComponent: "expo-linear-gradient",
|
|
1741
1760
|
docsUrl: "https://docs.expo.dev/versions/latest/sdk/linear-gradient/",
|
|
1742
1761
|
},
|
|
1762
|
+
// sc-6531 (REQ-AI-AGENT-DESIGN-LIFT) — the hover/press lift affordance. A
|
|
1763
|
+
// FUNCTION primitive, not a component: it resolves the interaction tokens per
|
|
1764
|
+
// input, so one declaration answers the pointer on web and the press on
|
|
1765
|
+
// native.
|
|
1766
|
+
{
|
|
1767
|
+
name: "pressableLift",
|
|
1768
|
+
description:
|
|
1769
|
+
"Interaction feedback for a tappable surface. Call it inside a Pressable's style FUNCTION and spread the result: `<Pressable style={(state) => [styles.card, ...pressableLift(state)]}>`. While the pointer hovers the surface (web) or it is pressed (touch), the surface lifts: a -2px nudge plus one elevation step, from `themeTokens.interaction`, with the web transition built in. Give it to EVERY tappable card, row and list entry; never hand-write hover logic, `transition` strings, or your own pressed shadows.",
|
|
1770
|
+
rnComponent: null,
|
|
1771
|
+
docsUrl: null,
|
|
1772
|
+
},
|
|
1743
1773
|
];
|
|
1744
1774
|
|
|
1745
1775
|
const CATEGORIES = [
|
|
@@ -3433,7 +3463,12 @@ const CONTRACT = deepFreeze({
|
|
|
3433
3463
|
// docs/design/req-widget-sdk-cross-platform-primitives.md, which was
|
|
3434
3464
|
// about vetting a full camera surface (permissions, multi-step UX, frame
|
|
3435
3465
|
// processing) as a widget import — none of which this adds.
|
|
3436
|
-
|
|
3466
|
+
// 1.87.0: additive (sc-6531) — `themeTokens.interaction`: the interaction-
|
|
3467
|
+
// state vocabulary (`lift.raised`, `lift.translateY`, `lift.transitionMs`),
|
|
3468
|
+
// plus the `pressableLift` primitive that resolves it per input — hover on
|
|
3469
|
+
// web, press on native. Tappable layout containers and widget surfaces lift
|
|
3470
|
+
// from this ONE table.
|
|
3471
|
+
version: "1.87.0",
|
|
3437
3472
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3438
3473
|
hooks: HOOKS,
|
|
3439
3474
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -45,6 +45,24 @@ const ELEVATION = Object.freeze({
|
|
|
45
45
|
}),
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
// sc-6531 (REQ-AI-AGENT-DESIGN-LIFT): the ONE cross-platform interaction
|
|
49
|
+
// vocabulary. A tappable surface answers the pointer over it (web) and the
|
|
50
|
+
// press on it (touch) by LIFTING. `raised` is one elevation step up plus a
|
|
51
|
+
// nudge — the scale above stays the only shadow vocabulary — and is what a
|
|
52
|
+
// widget's own surface wears via `pressableLift`; `translateY` alone is the
|
|
53
|
+
// surface-agnostic nudge a tappable layout REGION gets on both hosts.
|
|
54
|
+
const INTERACTION = Object.freeze({
|
|
55
|
+
lift: Object.freeze({
|
|
56
|
+
translateY: -2,
|
|
57
|
+
raised: Object.freeze({
|
|
58
|
+
transform: Object.freeze([Object.freeze({ translateY: -2 })]),
|
|
59
|
+
...ELEVATION.lg,
|
|
60
|
+
}),
|
|
61
|
+
// Web-only smoothing; a touch state change is instant.
|
|
62
|
+
transitionMs: 150,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
48
66
|
const DEFAULT_THEME_TOKENS = Object.freeze({
|
|
49
67
|
colors: Object.freeze({
|
|
50
68
|
primary: "#3b82f6",
|
|
@@ -76,6 +94,7 @@ const DEFAULT_THEME_TOKENS = Object.freeze({
|
|
|
76
94
|
info: "#0284c7",
|
|
77
95
|
}),
|
|
78
96
|
elevation: ELEVATION,
|
|
97
|
+
interaction: INTERACTION,
|
|
79
98
|
spacing: Object.freeze({ xs: 4, sm: 8, md: 16, lg: 24, xl: 32 }),
|
|
80
99
|
// REQ-THEME-LOOK: multiplies every layout spacing value at render. 1 is
|
|
81
100
|
// unchanged, so a theme that never sets it renders exactly as before.
|
|
@@ -1740,6 +1759,17 @@ const PRIMITIVES = [
|
|
|
1740
1759
|
rnComponent: "expo-linear-gradient",
|
|
1741
1760
|
docsUrl: "https://docs.expo.dev/versions/latest/sdk/linear-gradient/",
|
|
1742
1761
|
},
|
|
1762
|
+
// sc-6531 (REQ-AI-AGENT-DESIGN-LIFT) — the hover/press lift affordance. A
|
|
1763
|
+
// FUNCTION primitive, not a component: it resolves the interaction tokens per
|
|
1764
|
+
// input, so one declaration answers the pointer on web and the press on
|
|
1765
|
+
// native.
|
|
1766
|
+
{
|
|
1767
|
+
name: "pressableLift",
|
|
1768
|
+
description:
|
|
1769
|
+
"Interaction feedback for a tappable surface. Call it inside a Pressable's style FUNCTION and spread the result: `<Pressable style={(state) => [styles.card, ...pressableLift(state)]}>`. While the pointer hovers the surface (web) or it is pressed (touch), the surface lifts: a -2px nudge plus one elevation step, from `themeTokens.interaction`, with the web transition built in. Give it to EVERY tappable card, row and list entry; never hand-write hover logic, `transition` strings, or your own pressed shadows.",
|
|
1770
|
+
rnComponent: null,
|
|
1771
|
+
docsUrl: null,
|
|
1772
|
+
},
|
|
1743
1773
|
];
|
|
1744
1774
|
|
|
1745
1775
|
const CATEGORIES = [
|
|
@@ -3433,7 +3463,12 @@ const CONTRACT = deepFreeze({
|
|
|
3433
3463
|
// docs/design/req-widget-sdk-cross-platform-primitives.md, which was
|
|
3434
3464
|
// about vetting a full camera surface (permissions, multi-step UX, frame
|
|
3435
3465
|
// processing) as a widget import — none of which this adds.
|
|
3436
|
-
|
|
3466
|
+
// 1.87.0: additive (sc-6531) — `themeTokens.interaction`: the interaction-
|
|
3467
|
+
// state vocabulary (`lift.raised`, `lift.translateY`, `lift.transitionMs`),
|
|
3468
|
+
// plus the `pressableLift` primitive that resolves it per input — hover on
|
|
3469
|
+
// web, press on native. Tappable layout containers and widget surfaces lift
|
|
3470
|
+
// from this ONE table.
|
|
3471
|
+
version: "1.87.0",
|
|
3437
3472
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3438
3473
|
hooks: HOOKS,
|
|
3439
3474
|
primitives: PRIMITIVES,
|
package/dist/index.d.ts
CHANGED
|
@@ -2312,3 +2312,15 @@ export const CONTRACT: AiWidgetContract;
|
|
|
2312
2312
|
|
|
2313
2313
|
export function isHookAllowed(name: string): boolean;
|
|
2314
2314
|
export function requiredContextKeys(): string[];
|
|
2315
|
+
|
|
2316
|
+
/**
|
|
2317
|
+
* sc-6531 (REQ-AI-AGENT-DESIGN-LIFT) — interaction feedback for a tappable
|
|
2318
|
+
* surface. Call inside a Pressable's style function and spread the result:
|
|
2319
|
+
* `<Pressable style={(state) => [styles.card, ...pressableLift(state)]}>`.
|
|
2320
|
+
* Web raises the surface while hovered or pressed (with the transition built
|
|
2321
|
+
* in); native raises it while pressed.
|
|
2322
|
+
*/
|
|
2323
|
+
export function pressableLift(state?: {
|
|
2324
|
+
hovered?: boolean;
|
|
2325
|
+
pressed?: boolean;
|
|
2326
|
+
}): Array<Record<string, unknown> | null>;
|
package/dist/index.js
CHANGED
package/dist/index.native.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// sc-6531 (REQ-AI-AGENT-DESIGN-LIFT) — the web half of the lift affordance.
|
|
2
|
+
// react-native-web's Pressable hands its style function `{ hovered, pressed }`;
|
|
3
|
+
// either one raises the surface, and the smoothing rides the RESTING style so
|
|
4
|
+
// the lift animates in both directions. The touch half is interaction.native.js
|
|
5
|
+
// — no hover exists there, and a pressed state change is instant.
|
|
6
|
+
import { CONTRACT } from "./contract.js";
|
|
7
|
+
|
|
8
|
+
const LIFT = CONTRACT.themeTokens.interaction.lift;
|
|
9
|
+
const SMOOTHING = Object.freeze({
|
|
10
|
+
transitionProperty: "transform, box-shadow",
|
|
11
|
+
transitionDuration: `${LIFT.transitionMs}ms`,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export function pressableLift(state = {}) {
|
|
15
|
+
return [SMOOTHING, state.hovered || state.pressed ? LIFT.raised : null];
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// sc-6531 (REQ-AI-AGENT-DESIGN-LIFT) — the touch half of the lift affordance:
|
|
2
|
+
// there is no hover, the press state raises the surface, and the change is
|
|
3
|
+
// instant (RN styles carry no transitions). The pointer half is interaction.js.
|
|
4
|
+
import { CONTRACT } from "./contract.js";
|
|
5
|
+
|
|
6
|
+
const LIFT = CONTRACT.themeTokens.interaction.lift;
|
|
7
|
+
|
|
8
|
+
export function pressableLift(state = {}) {
|
|
9
|
+
return [state.pressed ? LIFT.raised : null];
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.114.0",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
],
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "node scripts/build.js",
|
|
51
|
-
"test": "node --test src/__tests__/contract.test.js src/__tests__/vetted-imports-audit.test.js src/__tests__/hooks-users.test.js src/__tests__/hooks-groups.test.js src/__tests__/hooks-invites.test.js src/__tests__/hooks-schema.test.js src/__tests__/hooks-assets-by-tag.test.js src/__tests__/hooks-filestore-upload.test.js src/__tests__/hooks-filestore-file.test.js src/__tests__/hooks-mutation.test.js src/__tests__/hooks-payments.test.js src/__tests__/hooks-record-permissions.test.js src/__tests__/hooks-geolocation.test.js src/__tests__/hooks-section-empty.test.js src/__tests__/hooks-widget-event.test.js src/__tests__/hooks-widget-input.test.js src/__tests__/hooks-identification.test.js src/__tests__/hooks-subscription.test.js src/__tests__/hooks-volatile-query-key.test.js src/__tests__/linter-users-scope.test.js src/__tests__/linter-comments.test.js src/__tests__/linter-translation-api.test.js src/__tests__/linter-page-url.test.js src/__tests__/linter-image-height.test.js src/__tests__/linter-hardcoded-design.test.js src/__tests__/linter-measured-padding.test.js src/__tests__/linter-payment-error.test.js src/__tests__/linter-platform.test.js src/__tests__/linter-react-import.test.js src/__tests__/lucide-icon-names.test.js src/__tests__/lucideIconName.test.js src/__tests__/manifest-actions.test.js src/__tests__/widget-translations.test.js src/__tests__/hooks-translate.test.js src/__tests__/devserver.test.js src/__tests__/host-externals.test.js src/__tests__/datetimepicker.test.js src/__tests__/property-schema-resolve.test.js src/__tests__/corner-radius.test.js src/__tests__/theme-components-parity.test.js src/__tests__/navigation-parity.test.js src/__tests__/theme-depth-tokens.test.js src/__tests__/toast-host.test.js src/__tests__/hooks-domain-error-mapping.test.js src/__tests__/linter-datastore-error.test.js src/__tests__/linter-write-gating.test.js src/__tests__/hooks-speech-to-text.test.js src/__tests__/hooks-camera.test.js src/__tests__/hooks-bound-columns.test.js src/__tests__/hooks-stable-query.test.js src/__tests__/hooks-can-write.test.js src/__tests__/widget-route.test.js"
|
|
51
|
+
"test": "node --test src/__tests__/contract.test.js src/__tests__/vetted-imports-audit.test.js src/__tests__/hooks-users.test.js src/__tests__/hooks-groups.test.js src/__tests__/hooks-invites.test.js src/__tests__/hooks-schema.test.js src/__tests__/hooks-assets-by-tag.test.js src/__tests__/hooks-filestore-upload.test.js src/__tests__/hooks-filestore-file.test.js src/__tests__/hooks-mutation.test.js src/__tests__/hooks-payments.test.js src/__tests__/hooks-record-permissions.test.js src/__tests__/hooks-geolocation.test.js src/__tests__/hooks-section-empty.test.js src/__tests__/hooks-widget-event.test.js src/__tests__/hooks-widget-input.test.js src/__tests__/hooks-identification.test.js src/__tests__/hooks-subscription.test.js src/__tests__/hooks-volatile-query-key.test.js src/__tests__/linter-users-scope.test.js src/__tests__/linter-comments.test.js src/__tests__/linter-translation-api.test.js src/__tests__/linter-page-url.test.js src/__tests__/linter-image-height.test.js src/__tests__/linter-hardcoded-design.test.js src/__tests__/linter-measured-padding.test.js src/__tests__/linter-payment-error.test.js src/__tests__/linter-platform.test.js src/__tests__/linter-react-import.test.js src/__tests__/lucide-icon-names.test.js src/__tests__/lucideIconName.test.js src/__tests__/manifest-actions.test.js src/__tests__/widget-translations.test.js src/__tests__/hooks-translate.test.js src/__tests__/devserver.test.js src/__tests__/host-externals.test.js src/__tests__/datetimepicker.test.js src/__tests__/property-schema-resolve.test.js src/__tests__/corner-radius.test.js src/__tests__/theme-components-parity.test.js src/__tests__/navigation-parity.test.js src/__tests__/theme-depth-tokens.test.js src/__tests__/interaction-lift.test.js src/__tests__/toast-host.test.js src/__tests__/hooks-domain-error-mapping.test.js src/__tests__/linter-datastore-error.test.js src/__tests__/linter-write-gating.test.js src/__tests__/hooks-speech-to-text.test.js src/__tests__/hooks-camera.test.js src/__tests__/hooks-bound-columns.test.js src/__tests__/hooks-stable-query.test.js src/__tests__/hooks-can-write.test.js src/__tests__/widget-route.test.js"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=18"
|