@stealthscale/component-messaging 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stealth Scale B.V.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,392 @@
1
+ # @stealthscale/component-messaging
2
+
3
+ A conversation in this package is a transcript that follows the latest message, turns with a sender
4
+ and a bubble per message, the reactions and the attachments under them, and the composer the reader
5
+ writes the next message in. An application's compiler reads the recipes from the preset under
6
+ `./theme`.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @stealthscale/component-messaging
12
+ ```
13
+
14
+ The package peers on `react`, `react-dom`, `@stealthscale/hooks`, `@stealthscale/theme`,
15
+ `@stealthscale/component-disclosure` and `@stealthscale/component-primitives`. It depends on
16
+ `@stealthscale/component-actions` and on `use-stick-to-bottom` 1.1.6, which scrolls the transcript.
17
+
18
+ | Export | What it renders |
19
+ | -------------- | --------------------------------------------------------------------------------- |
20
+ | `Conversation` | The transcript: a log that follows the latest message, and a control to return |
21
+ | `Message` | One turn of a conversation: the sender, a bubble per message and a status row |
22
+ | `Reactions` | A row of reaction toggles under a message, and a picker that adds one |
23
+ | `Attachment` | A file attached to a message or waiting to be sent, alone or in a list |
24
+ | `Composer` | The box a reader writes a message in, with mentions, attachments, send and stop |
25
+ | `groupTurns` | Nothing: it groups a transcript into days, and each day into turns, for rendering |
26
+
27
+ ## Conversation
28
+
29
+ `Conversation` renders a transcript that opens at its end and follows each message that arrives or
30
+ grows, until the reader scrolls up. A control then brings the reader back to the latest message.
31
+
32
+ ```tsx
33
+ import { ArrowDownIcon } from "lucide-react";
34
+
35
+ import { Divider } from "@stealthscale/component-layout";
36
+ import { Conversation, groupTurns } from "@stealthscale/component-messaging";
37
+
38
+ const conversation = Conversation.useConversation();
39
+
40
+ <Conversation.Root conversation={conversation} label="Conversation with Ada" maxHeight="xs">
41
+ <Conversation.Content>
42
+ {groupTurns(messages, { self: "me", timeZone }).map((day) => (
43
+ <Fragment key={day.key}>
44
+ <Divider
45
+ label={<Timestamp options={{ dateStyle: "medium", timeZone }} value={day.day} />}
46
+ />
47
+ {day.turns.map(renderTurn)}
48
+ </Fragment>
49
+ ))}
50
+ {typing ? <Conversation.Typing>Ada is typing</Conversation.Typing> : null}
51
+ </Conversation.Content>
52
+ <Conversation.JumpTrigger label="Jump to the latest message">
53
+ <ArrowDownIcon />
54
+ </Conversation.JumpTrigger>
55
+ </Conversation.Root>;
56
+ ```
57
+
58
+ | Part | Element | What it renders |
59
+ | ------------- | ------- | ------------------------------------------------------------------------- |
60
+ | `Root` | `div` | The primitives scroll area's root, with the vertical bar after its parts |
61
+ | `Content` | `div` | The scroll area's viewport as the `log`, around the content of turns |
62
+ | `JumpTrigger` | `div` | The actions `Button` over the transcript's bottom edge, away from the end |
63
+ | `Typing` | `div` | Three pulsing dots and the caller's words |
64
+
65
+ - `useConversation()` runs the scroll engine and returns `atEnd`, `scrollToEnd()` and
66
+ `scrollToMessage(id)`. Pass the result to `Root` as `conversation` to scroll the transcript from
67
+ outside it, such as from the composer's send. A root without `conversation` runs its own engine.
68
+ - The engine is use-stick-to-bottom. While the view is within 70px of the end, a message that
69
+ arrives or grows scrolls the view with it. A wheel, a key or a drag that scrolls up stops the
70
+ follow. Under reduced motion every scroll is instant.
71
+ - `scrollToEnd()` scrolls to the end and follows again. `scrollToMessage(id)` puts the element with
72
+ that `id` at the top of the view and stops the follow, for a jump to the first unread message.
73
+ - `Root` takes the scroll area's props. A transcript scrolls only in a bounded height: `maxHeight`,
74
+ from `xs` at 20rem to `lg` at 32rem, or a parent of a definite height. `inset` pads the turns.
75
+ - `JumpTrigger` renders nothing while the view is at the end. A press moves focus to the log first,
76
+ so focus remains in the conversation when the control goes. The caller passes the glyph.
77
+ - `Typing`'s dots pulse at the theme's 1.2s ambient pace, 200ms apart, and rest under reduced
78
+ motion. Under forced colors they fill with `CanvasText`. Render it inside `Content`, after the
79
+ last turn, while someone types.
80
+ - Render a day as the layout `Divider` with the date as its `label`. A labelled line has no
81
+ `separator` role, so a screen reader reads the date.
82
+
83
+ ### Accessibility
84
+
85
+ - The viewport is the `log`, named by `label`, "Messages" by default. A log is a polite live region,
86
+ so a screen reader reads each turn once as it is added, and `aria-relevant="additions"` leaves out
87
+ text that changes inside a turn.
88
+ - Pass `aria-busy` on a turn while its words still arrive, and on `Content` while older messages
89
+ load. A screen reader then reads the finished turn once.
90
+ - While the transcript overflows, the log is in the tab order and the arrow keys, Page Up, Page
91
+ Down, Home and End scroll it.
92
+ - Write the turns oldest first. A screen reader reads them in the order they are written.
93
+
94
+ ## groupTurns
95
+
96
+ `groupTurns(messages, { self, timeZone, window })` returns the days of a transcript, each with its
97
+ turns.
98
+
99
+ | Returns | Fields |
100
+ | --------- | ----------------------------------------------------------------------------------- |
101
+ | `TurnDay` | `day`, midnight at the start of the day, `key`, and `turns` |
102
+ | `Turn` | `author`, `first`, `last`, `messages` oldest first, and `own` for the `self` author |
103
+
104
+ - A turn is the run of messages one author sent within `window` of the one before, 5 minutes by
105
+ default. A new day always starts a new turn.
106
+ - A message reads `author`, `id` and `sentAt`: a `Date`, epoch milliseconds or a string `Date`
107
+ parses. A message without a readable `sentAt` joins the turn and the day before it.
108
+ - A day starts at midnight in `timeZone`, an IANA zone, or in the runtime's zone, the reader's own
109
+ calendar in a browser. Pass the zone the times are formatted in, and format the day's label in it
110
+ too, so a label names the day its messages were sent. Where a daylight saving change skips
111
+ midnight, the day starts at its first instant.
112
+
113
+ ## Message
114
+
115
+ A turn is the run of messages one person sent together. It renders the sender's avatar, a header
116
+ with the name and the time, a bubble per message, and a footer with the delivery status and the
117
+ actions.
118
+
119
+ ```tsx
120
+ import { Message } from "@stealthscale/component-messaging";
121
+
122
+ <Message.Root>
123
+ <Message.Avatar>
124
+ <Avatar.Root aria-hidden name="Ada Okafor" size="sm">
125
+ <Avatar.Fallback />
126
+ </Avatar.Root>
127
+ </Message.Avatar>
128
+ <Message.Content>
129
+ <Message.Header>
130
+ <Strong>Ada Okafor</Strong>
131
+ <Timestamp options={{ timeStyle: "short" }} value={sentAt} />
132
+ </Message.Header>
133
+ <Message.Bubble>The invoice from Northwind is in.</Message.Bubble>
134
+ <Message.Bubble>Can you approve it before five?</Message.Bubble>
135
+ </Message.Content>
136
+ </Message.Root>;
137
+ <Message.Root align="end" aria-label="You" look="solid" palette="primary">
138
+ <Message.Content>
139
+ <Message.Bubble>Approved. I'll send it to finance.</Message.Bubble>
140
+ <Message.Footer>
141
+ <Message.Status status="read">
142
+ <CheckCheckIcon aria-hidden />
143
+ Read
144
+ </Message.Status>
145
+ </Message.Footer>
146
+ </Message.Content>
147
+ </Message.Root>;
148
+ ```
149
+
150
+ | Part | Element | What it renders |
151
+ | --------- | --------- | ---------------------------------------------------------------------- |
152
+ | `Root` | `article` | The turn. It takes the variants |
153
+ | `Avatar` | `div` | The box around the sender's avatar, at the top of the turn |
154
+ | `Content` | `div` | The column of the header, the bubbles and the footer |
155
+ | `Header` | `div` | The row above the bubbles, with the sender's name and the time |
156
+ | `Bubble` | `div` | One message. `failed` paints it in the error palette |
157
+ | `Footer` | `div` | The row under the bubbles, with the status and the actions |
158
+ | `Status` | `span` | How far a message has got, from `status`, and the caller's glyph |
159
+ | `Actions` | `div` | The turn's controls, transparent until a pointer or focus reaches them |
160
+
161
+ | Axis | Values | Default |
162
+ | --------- | ---------------------------------------------------------------------------------- | --------- |
163
+ | `align` | `start`, `end` | `start` |
164
+ | `look` | `solid`, `subtle`, `surface`, `outline`, `plain` | `subtle` |
165
+ | `palette` | `primary`, `secondary`, `accent`, `neutral`, `info`, `success`, `warning`, `error` | `neutral` |
166
+ | `size` | `sm`, `md`, `lg` | `md` |
167
+ | `reveal` | `hover`, `always` | `hover` |
168
+
169
+ - `align="end"` puts a turn at the end of the line, for the reader's own messages. The row runs the
170
+ other way, so the avatar is at the end, and the column aligns the bubbles to the end.
171
+ - A bubble is at most 80% of the column wide. `look` paints every bubble of the turn in one of the
172
+ flat looks, which do not repaint under a pointer. `plain` has no fill and no padding and gives the
173
+ bubble the whole column, for an answer that reads as a document or a comment in a thread.
174
+ - `palette` colors the bubbles. Give the reader's own turns `primary` and every other sender
175
+ `neutral`, so the two sides differ by more than their place.
176
+ - `size` sets the text style of the bubbles, the header and the footer, the padding inside a bubble,
177
+ and the gap between the avatar and the column.
178
+ - `Bubble failed` writes `data-failed` and paints the bubble in the error palette. Pair it with
179
+ `Status status="failed"` and a control that sends the message again.
180
+ - `Status status` writes `data-status`: `sending`, `sent`, `delivered`, `read` or `failed`. The
181
+ status of a read message is in the primary palette's ink, and a failed one in the error ink.
182
+ - `reveal="hover"` makes the actions transparent until a pointer is over the turn or focus is inside
183
+ it. A coarse pointer cannot hover, so under one the actions are opaque at rest. `reveal="always"`
184
+ shows them at rest, for the last turn of a conversation.
185
+ - Under forced colors a bubble has a `CanvasText` hairline outline, except in the plain look.
186
+ - A mention in a message is the typography `Mark` with `palette="primary"`, and `warning` for a
187
+ mention of the reader.
188
+
189
+ ### Accessibility
190
+
191
+ - Each turn is an `article`, so a screen reader's article command moves from turn to turn.
192
+ - Name a turn whose header does not show its sender with `aria-label`, such as "You" on the reader's
193
+ own turns.
194
+ - Set `aria-hidden` on an avatar beside the same name in words, so a screen reader reads the name
195
+ once.
196
+ - A screen reader does not read a glyph's shape or color. Hide the status glyph with `aria-hidden`
197
+ and keep the words, visually hidden where the glyph is enough to see.
198
+ - The actions are in the tab order under both `reveal` values. Name each icon button.
199
+
200
+ ## Reactions
201
+
202
+ `Reactions` renders a toggle per reaction under a message, with the number of people who reacted,
203
+ and a picker that adds a reaction.
204
+
205
+ ```tsx
206
+ import { SmilePlusIcon } from "lucide-react";
207
+
208
+ import { Reactions } from "@stealthscale/component-messaging";
209
+
210
+ <Reactions.Root label="Reactions">
211
+ <Reactions.Item count={3} label="Thumbs up, 3 people, including you" onClick={toggle} pressed>
212
+ 👍
213
+ </Reactions.Item>
214
+ <Reactions.Picker icon={<SmilePlusIcon />} label="Add a reaction" onSelect={add}>
215
+ <Reactions.Choice label="Party popper" value="party">
216
+ 🎉
217
+ </Reactions.Choice>
218
+ </Reactions.Picker>
219
+ </Reactions.Root>;
220
+ ```
221
+
222
+ | Part | Element | What it renders |
223
+ | -------- | ---------- | --------------------------------------------------------------------------------- |
224
+ | `Root` | `fieldset` | The row, named by `label`, "Reactions" by default |
225
+ | `Item` | `button` | The actions `Button`: the glyph and the count, pressed while the reader took part |
226
+ | `Picker` | `div` | The disclosure `Popover`: its trigger and its portalled panel of choices |
227
+ | `Choice` | `button` | One reaction in the panel, named by `label`, reported by `value` |
228
+
229
+ - The caller keeps each reaction's state and toggles `pressed` and `count` from `onClick`. A pressed
230
+ reaction takes the primary palette, and `aria-pressed` states it.
231
+ - `Item` renders `count` after the glyph whenever it is stated, in tabular figures.
232
+ - `Picker` calls `onSelect` with the chosen value and closes, and the popover returns focus to its
233
+ trigger. `choicesLabel` names the panel, "Choose a reaction" by default.
234
+
235
+ ### Accessibility
236
+
237
+ - The row is a `fieldset`, so a screen reader announces the reactions as one set.
238
+ - The glyph and the count are `aria-hidden`, and `label` names the reaction in their place. State
239
+ the reaction, the count and the reader's part in it: "Thumbs up, 3 people, including you".
240
+
241
+ ## Attachment
242
+
243
+ `Attachment` renders a file with a media square, its name over a line of detail, and its actions.
244
+ The square shows the type's icon, a thumbnail or a spinner. `Attachment.Group` lists the files of a
245
+ message.
246
+
247
+ ```tsx
248
+ import { FileSpreadsheetIcon } from "lucide-react";
249
+
250
+ import { Attachment } from "@stealthscale/component-messaging";
251
+
252
+ <Attachment.Group aria-label="Attachments" size="sm">
253
+ <Attachment.Root>
254
+ <Attachment.Media>
255
+ <FileSpreadsheetIcon aria-hidden />
256
+ </Attachment.Media>
257
+ <Attachment.Content>
258
+ <Attachment.Title>payouts-september.csv</Attachment.Title>
259
+ <Attachment.Description>CSV, 18 kB</Attachment.Description>
260
+ </Attachment.Content>
261
+ <Attachment.Actions>{download}</Attachment.Actions>
262
+ </Attachment.Root>
263
+ </Attachment.Group>;
264
+ ```
265
+
266
+ | Part | Element | What it renders |
267
+ | ------------- | ------------- | ------------------------------------------------------------- |
268
+ | `Group` | `ul` | The list. Its `size` and `orientation` are the files' default |
269
+ | `Root` | `div` or `li` | One file, an `li` inside a group |
270
+ | `Media` | `div` | The square with the icon, the spinner or a cropped thumbnail |
271
+ | `Content` | `div` | The column of the title and the description |
272
+ | `Title` | `span` | The file's name, on one line |
273
+ | `Description` | `span` | The detail, such as the type and the size, on one line |
274
+ | `Actions` | `div` | The file's controls, over the media's corner on a tile |
275
+
276
+ | Axis | Values | Default |
277
+ | ------------- | ------------------------ | ------------ |
278
+ | `orientation` | `horizontal`, `vertical` | `horizontal` |
279
+ | `size` | `xs`, `sm`, `md` | `md` |
280
+
281
+ - `horizontal` renders a row, and a group stacks rows. `vertical` renders a tile with the media
282
+ across its top, and a group wraps tiles.
283
+ - `state` on `Root` writes `data-state`. `idle` marks a file not yet sent with a dashed edge.
284
+ `uploading` and `processing` mark a file that transfers or that the server checks. `error` takes
285
+ the error edge and inks, and `done` is the default.
286
+ - The title and the description end in an ellipsis, so a long file name never widens the list.
287
+
288
+ ### Accessibility
289
+
290
+ - A group is a list, so a screen reader announces the number of files. Name it with `aria-label`.
291
+ - State the file's state in the description. A screen reader does not read an edge or an ink.
292
+ - Give a thumbnail `alt=""` beside a title with the file's name, so the name is read once.
293
+
294
+ ## Composer
295
+
296
+ `Composer` renders the box a reader writes a message in. Above the text it shows the message it
297
+ replies to or edits and the files waiting to be sent. The text grows with its content, and a row of
298
+ controls under it attaches files and sends.
299
+
300
+ ```tsx
301
+ import { ArrowUpIcon, PaperclipIcon, SquareIcon } from "lucide-react";
302
+
303
+ import { Composer } from "@stealthscale/component-messaging";
304
+
305
+ <Composer.Root busy={answering} onAttach={attach} onStop={stop} onSubmit={send}>
306
+ <Composer.Input label="Message" placeholder="Write a message" />
307
+ <Composer.Toolbar>
308
+ <Composer.AttachTrigger label="Attach files">
309
+ <PaperclipIcon />
310
+ </Composer.AttachTrigger>
311
+ <Composer.Submit label="Send" stopIcon={<SquareIcon />} stopLabel="Stop the answer">
312
+ <ArrowUpIcon />
313
+ </Composer.Submit>
314
+ </Composer.Toolbar>
315
+ </Composer.Root>;
316
+ ```
317
+
318
+ | Part | Element | What it renders |
319
+ | --------------- | ---------- | ---------------------------------------------------------------------- |
320
+ | `Root` | `form` | The box, whose edge, focus ring and states come from the textarea |
321
+ | `Context` | `div` | The strip above the text for the message the composer replies to |
322
+ | `Attachments` | `div` | The row above the text for an `Attachment.Group` of files to send |
323
+ | `Input` | `textarea` | The text, and the list of mention suggestions while one is typed |
324
+ | `Toolbar` | `div` | The row of controls under the text, with the submit control at its end |
325
+ | `AttachTrigger` | `button` | The actions `Button` that opens the file picker, and a hidden input |
326
+ | `Submit` | `button` | The actions `Button` that sends, or stops a running answer |
327
+
328
+ | Axis | Values | Default |
329
+ | --------- | ------------------------------ | --------- |
330
+ | `variant` | `outline`, `subtle`, `flushed` | `outline` |
331
+ | `size` | `sm`, `md`, `lg` | `md` |
332
+
333
+ - `Root` keeps the text, controlled through `value` and `onValueChange` or from `defaultValue`.
334
+ `onSubmit` receives the text trimmed, the root clears a text it keeps, and focus returns to the
335
+ textarea. Whitespace alone is not a message, and files alone are, where `attached` is true.
336
+ - `busy` stops every send. While `busy` and with `onStop`, `Submit` is the stop control on the same
337
+ element, named by `stopLabel`, so focus remains on it across the swap. With nothing to send,
338
+ `Submit` is `aria-disabled` and keeps focus.
339
+ - `Input` grows line by line through `field-sizing: content` up to `maxRows`, 8 by default, and then
340
+ scrolls. It takes a `ref`, so a control outside the composer can focus it.
341
+ - `onAttach` receives the files a reader picks, drops on the box or pastes. The box takes the focus
342
+ edge while files are dragged over it. `AttachTrigger accept` limits the files the picker offers.
343
+ - `triggers` on `Input`, such as `["@"]`, opens a mention. `onQueryChange` reports the query at the
344
+ caret, the caller passes the matching `suggestions`, and `onMention` reports the one inserted. The
345
+ text keeps the name the reader sees, such as `@Ada Okafor`, and a space after it. A trigger opens
346
+ a query only at the start of the text or after whitespace, so an email address is not a mention.
347
+
348
+ | Key | What it does |
349
+ | --------------------- | ------------------------------------------------------------------- |
350
+ | Enter | Sends, under `submitOn="enter"`, the default |
351
+ | Shift+Enter | Starts a new line, under `submitOn="enter"` |
352
+ | Ctrl+Enter, Cmd+Enter | Sends, under `submitOn="modEnter"`, where Enter starts a new line |
353
+ | Escape | Calls `onCancelContext`, which dismisses a reply or leaves an edit |
354
+ | ArrowUp | Calls `onEditLast` from an empty textarea, to edit the last message |
355
+ | ArrowDown, ArrowUp | Move the highlight in an open list of suggestions |
356
+ | Enter, Tab | Insert the highlighted suggestion |
357
+ | Escape | Closes the list of suggestions, and nothing else |
358
+
359
+ - A key pressed while an input method composes text belongs to the input method, so Enter never
360
+ sends a candidate a reader of Japanese or Chinese is choosing.
361
+
362
+ ### Accessibility
363
+
364
+ - The textarea is named by `label`, "Message" by default. A placeholder disappears with the first
365
+ character, so the textarea takes its name from `label` alone.
366
+ - With `triggers` the textarea states `aria-autocomplete="list"`. While suggestions are open,
367
+ `aria-controls` points at the list and `aria-activedescendant` at the highlighted suggestion.
368
+ Focus remains in the textarea.
369
+ - The file input is `hidden`, so neither a screen reader nor Tab finds a second, unnamed control.
370
+
371
+ ## Types
372
+
373
+ | Type | Props of |
374
+ | ---------------- | ----------------------------------------------------------------------------------------------------------------- |
375
+ | `Conversation.*` | `RootProps`, `ContentProps`, `JumpTriggerProps`, `TypingProps`, `ConversationApi` |
376
+ | `Message.*` | `RootProps`, `BubbleProps`, `StatusProps`, `MessageStatus`, and the parts |
377
+ | `Reactions.*` | `RootProps`, `ItemProps`, `PickerProps`, `ChoiceProps` |
378
+ | `Attachment.*` | `RootProps`, `GroupProps`, `AttachmentState`, and the parts |
379
+ | `Composer.*` | `RootProps`, `InputProps`, `SubmitProps`, `AttachTriggerProps`, `SubmitKey`, `Query`, `Suggestion`, and the parts |
380
+ | `groupTurns` | `GroupTurnsOptions`, `Turn`, `TurnDay`, `TurnMessage` |
381
+
382
+ ## Not offered
383
+
384
+ - An emoji picker. `Reactions.Picker` offers the caller's choices.
385
+ - A component for a mention inside a sent message, a reasoning panel, a tool call card, a comment
386
+ thread or a list of conversations. The catalogue composes each from the library's parts: the
387
+ typography `Mark`, the disclosure `Details`, the content `JsonTreeView` and `Markdown`, and the
388
+ collections `Listbox`.
389
+
390
+ ## Licence
391
+
392
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1 @@
1
+ {"$schema":"http://cyclonedx.org/schema/bom-1.7.schema.json","bomFormat":"CycloneDX","specVersion":"1.7","version":1,"metadata":{"lifecycles":[{"phase":"build"}],"tools":{"components":[{"type":"application","name":"rolldown","version":"1.2.11"},{"type":"application","name":"vite","version":"8.3.1"},{"type":"library","name":"core","group":"@arethetypeswrong","version":"0.18.5","bom-ref":"pkg:npm/%40arethetypeswrong/core@0.18.5","author":"Andrew Branch","description":"The API behind arethetypeswrong.github.io","hashes":[{"alg":"SHA-512","content":"f72b63cc6c318e6f54cfb23d6af7dbb79be542deae93db914444b3263aabce797a58abc8e9dc184e8faf277534d96aeafe64778aa97f3f386f1e129d2480230d"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40arethetypeswrong/core@0.18.5","externalReferences":[{"url":"https://github.com/arethetypeswrong/arethetypeswrong.github.io.git#packages/core","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=20"}]},{"type":"library","name":"component-disclosure","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/component-disclosure@0.1.0","description":"Draws what is shown and hidden on the reader's say-so.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/component-disclosure@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#components/disclosure","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/components/disclosure#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"component-primitives","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/component-primitives@0.1.0","description":"Decides what is drawn and where, and draws nothing itself.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/component-primitives@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#components/primitives","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/components/primitives#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"hooks","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/hooks@0.1.0","description":"The React hooks a component uses to read the page it draws into: how the page is read, what it measures, and what a value was on the previous render.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/hooks@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#foundations/hooks","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/foundations/hooks#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"theme","group":"@stealthscale","version":"0.3.0","bom-ref":"pkg:npm/%40stealthscale/theme@0.3.0","description":"Publishes the foundation every recipe is written against, the runtime every component binds its recipe with, and the vocabulary a theme is written in.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/theme@0.3.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#foundations/theme","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/foundations/theme#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-i18n","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-i18n@0.1.0","description":"Configures a package or an application with words: the catalogue plugin, and the words in scope for its specifications.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-i18n@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-i18n","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-i18n#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-react","group":"@stealthscale","version":"0.8.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-react@0.8.0","description":"Adds the JSX transform, the React lint rules, a test document and an MDX compiler to a package that renders.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-react@0.8.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-react#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-specimen","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-specimen@0.1.0","description":"Configures an application that shows a catalogue, and the workspace root the specimens sit under.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-specimen@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-specimen","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-specimen#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-theme","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-theme@0.1.0","description":"Adds the runtime generator to a design-system package and the stylesheet compiler to an application.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-theme@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-theme","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-theme#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-typescript","group":"@stealthscale","version":"0.3.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-typescript@0.3.0","description":"Sets the TypeScript options every stealthscale package compiles with.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-typescript@0.3.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-typescript","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-typescript#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config","group":"@stealthscale","version":"0.6.0","bom-ref":"pkg:npm/%40stealthscale/vite-config@0.6.0","description":"Configures a package from five Vite+ tiers and a named layer for every block.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config@0.6.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"react","group":"@testing-library","version":"16.3.3","bom-ref":"pkg:npm/%40testing-library/react@16.3.3","author":"Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)","description":"Simple and complete React DOM testing utilities that encourage good testing practices.","hashes":[{"alg":"SHA-512","content":"528d7ddcd8106cf333ea5aeb86d45040570c0ba45efc42cb15b6ee54bdf4583959c65a597fdfe53074c05713d12f0d6ad62bbd389991d5cd812e210d46cb5d06"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40testing-library/react@16.3.3","externalReferences":[{"url":"https://github.com/testing-library/react-testing-library/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"https://github.com/testing-library/react-testing-library","type":"vcs","comment":"as detected from PackageJson property \"repository.url\""},{"url":"https://github.com/testing-library/react-testing-library#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=18"}]},{"type":"library","name":"react-dom","group":"@types","version":"19.3.0","bom-ref":"pkg:npm/%40types/react-dom@19.3.0","description":"TypeScript definitions for react-dom","hashes":[{"alg":"SHA-512","content":"648edb538da66575ca1e7fea34b130d88adb888354ed7e7ebdf81d8b10479023696165e32a07d0fcffaec866f90a338b07d51c9d37a0debca542d5768729b8e1"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40types/react-dom@19.3.0","externalReferences":[{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped.git#types/react-dom","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"react","group":"@types","version":"19.3.0","bom-ref":"pkg:npm/%40types/react@19.3.0","description":"TypeScript definitions for react","hashes":[{"alg":"SHA-512","content":"374ac50ae1fd628c46f7f9bad65f4c7e924a7e62ce554d1e9bb8a9233e9346049292f45e2c1f6f2fce4607ecabf01b3995eaa9be0f7a252c05e194b5b38be242"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40types/react@19.3.0","externalReferences":[{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped.git#types/react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"happy-dom","version":"20.14.5","bom-ref":"pkg:npm/happy-dom@20.14.5","author":"David Ortner","description":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.","hashes":[{"alg":"SHA-512","content":"c7f47392958ee346d38c8a13df4890b6deb814b2e61ff89171408dd97fff6cbc7b1f789f91d7cf5f2aacae8fce62dab32008622cc300ee69a2391f42dcd38a8d"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/happy-dom@20.14.5","externalReferences":[{"url":"https://github.com/capricorn86/happy-dom","type":"vcs","comment":"as detected from PackageJson property \"repository\""},{"url":"https://github.com/capricorn86/happy-dom","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=20.0.0"}]},{"type":"library","name":"lucide-react","version":"1.48.0","bom-ref":"pkg:npm/lucide-react@1.48.0","author":"Eric Fennis","description":"A Lucide icon library package for React applications.","hashes":[{"alg":"SHA-512","content":"474088298fdf5e20bacbdc510400e0b0af955b6a7fa5c4c938c84b65fd53fae1bebc952fc94474da718e8262080bb30f92234ae0ec7c4039dba85f2b25858f65"}],"licenses":[{"license":{"id":"ISC"}}],"purl":"pkg:npm/lucide-react@1.48.0","externalReferences":[{"url":"https://github.com/lucide-icons/lucide/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"https://github.com/lucide-icons/lucide.git#packages/lucide-react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://lucide.dev","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"oxc-transform-react","version":"0.151.0","bom-ref":"pkg:npm/oxc-transform-react@0.151.0","author":"Boshen and oxc contributors","description":"Oxc React Compiler Node API","hashes":[{"alg":"SHA-512","content":"60ed7f62cdc5202fee060ca37b1bc6b084d05085c318d9b1272eafa906ddf1663b263ccc0f5faf095879f65cdaf7f3f4dbf4d135a517c94eceaf6c70c7f29b77"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/oxc-transform-react@0.151.0","externalReferences":[{"url":"https://github.com/oxc-project/oxc/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/oxc-project/oxc.git#napi/transform-react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://oxc.rs","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":"^20.19.0 || >=22.12.0"}]},{"type":"library","name":"react-dom","version":"19.3.0","bom-ref":"pkg:npm/react-dom@19.3.0","description":"React package for working with the DOM.","hashes":[{"alg":"SHA-512","content":"24393c76089fe753a31680c4ef4f8e4fd202c98afeebd1e59a284dc29d7e36c7db9dadede6c22209af59264b43990e3fd5bfeb9f6ea1200476b985c332be6bd1"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/react-dom@19.3.0","externalReferences":[{"url":"https://github.com/react/react/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"https://github.com/react/react.git#packages/react-dom","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://react.dev/","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"react","version":"19.3.0","bom-ref":"pkg:npm/react@19.3.0","description":"React is a JavaScript library for building user interfaces.","hashes":[{"alg":"SHA-512","content":"13c2d471bb415addb46db9766281dfc7864305dc554df38ab42667f5c0d227897afe7ba8029729201723e3bb76c19a155fc836647b8c1db892860091d53e52a2"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/react@19.3.0","externalReferences":[{"url":"https://github.com/react/react/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"https://github.com/react/react.git#packages/react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://react.dev/","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=0.10.0"}]},{"type":"library","name":"typescript","version":"7.0.2","bom-ref":"pkg:npm/typescript@7.0.2","author":"Microsoft Corp.","description":"TypeScript is a language for application scale JavaScript development","hashes":[{"alg":"SHA-512","content":"f0561abbdea8dcd28e85b8ca8bfa8dbc6fd6e63873c646dd9b9b23f406d9ff94f9b16aa7de12602df1b1dbbb11299593bf2cc23fc74b4414dfe6d05349155434"}],"licenses":[{"license":{"id":"Apache-2.0"}}],"purl":"pkg:npm/typescript@7.0.2","externalReferences":[{"url":"https://github.com/microsoft/TypeScript/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"https://github.com/microsoft/TypeScript.git","type":"vcs","comment":"as detected from PackageJson property \"repository.url\""},{"url":"https://www.typescriptlang.org/","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=16.20.0"}]},{"type":"library","name":"vitest","version":"5.0.1","bom-ref":"pkg:npm/vitest@5.0.1","author":"Anthony Fu <anthonyfu117@hotmail.com>","description":"Next generation testing framework powered by Vite","hashes":[{"alg":"SHA-512","content":"880f799506ca124bebb5391d0209d66d77db8a95a28967bf8439763f9b4c8ba585c03efa1b43715c01a9fccf4439c62ea5e18946beb0a6931cec2a00e354d08e"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/vitest@5.0.1","externalReferences":[{"url":"https://github.com/vitest-dev/vitest/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"git+https://github.com/vitest-dev/vitest.git#packages/vitest","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://vitest.dev","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":"^22.12.0 || ^24.0.0 || >=26.0.0"}]}]},"component":{"type":"library","name":"component-messaging","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/component-messaging@0.1.0","description":"Renders conversations: turns of messages with a status and actions, a transcript that follows the latest message, reactions, attachments, mentions and a composer.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/component-messaging@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#components/messaging","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/components/messaging#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}],"evidence":{"licenses":[{"license":{"name":"file: LICENSE","text":{"content":"TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgMjAyNiBTdGVhbHRoIFNjYWxlIEIuVi4KClBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkKb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgIlNvZnR3YXJlIiksIHRvIGRlYWwKaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cwp0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsCmNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcwpmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOgoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW4gYWxsCmNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==","contentType":"text/plain","encoding":"base64"}}}]}},"supplier":{"name":"Stealth Scale B.V.","url":["https://stealthscale.io"]}},"components":[],"dependencies":[{"ref":"pkg:npm/%40stealthscale/component-messaging@0.1.0"}]}