@starklab/stark-mcp 0.1.0 → 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/package.json +10 -4
- package/src/adopt/adoptScanReport.js +124 -0
- package/src/adopt/catalog.js +26 -6
- package/src/adopt/foreignDiscoveryResolver.js +276 -0
- package/src/adopt/foreignPropSchemaResolver.js +134 -0
- package/src/adopt/foreignScanReport.js +210 -0
- package/src/adopt/foreignScoringResolver.js +192 -0
- package/src/adopt/foreignSystemConfig.js +356 -0
- package/src/adopt/installedPackageDiscoveryResolver.js +602 -0
- package/src/adopt/installedPackagePropSchemaResolver.js +279 -0
- package/src/adopt/installedPackageScoringResolver.js +153 -0
- package/src/adopt/installedSystemAutoDetector.js +51 -0
- package/src/adopt/installedSystemScan.js +101 -0
- package/src/adopt/jsxOpportunityHelpers.js +99 -0
- package/src/adopt/moduleGraph.js +39 -8
- package/src/adopt/opportunityResolver.js +255 -0
- package/src/adopt/opportunitySignaturesNative.js +47 -0
- package/src/adopt/usageRulesResolver.js +298 -0
- package/src/adopt/vecnaMaterializer.js +165 -0
- package/src/adopt/vecnaVerifier.js +127 -0
- package/src/cli.js +407 -1
- package/src/adopt/__fixtures__/dominion-fixture-app/src/pages/Home.jsx +0 -21
- package/src/adopt/__fixtures__/dominion-fixture-app/src/pages/Menu.jsx +0 -13
- package/src/adopt/__fixtures__/dominion-fixture-app/src/pages/Profile.jsx +0 -11
- package/src/adopt/__fixtures__/dominion-fixture-app/src/theme.css +0 -34
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/AppButton.jsx +0 -8
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/BrandButton.jsx +0 -9
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/CardBase.jsx +0 -9
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/FeatureCard.jsx +0 -7
- package/src/adopt/__fixtures__/dominion-fixture-app/src/wrappers/SectionCard.jsx +0 -12
- package/src/adopt/dominionFixture.test.js +0 -165
- package/src/adopt/propApiResolver.test.js +0 -229
- package/src/adopt/referenceResolver.test.js +0 -213
- package/src/adopt/rnTailwindResolver.test.js +0 -263
- package/src/adopt/rnTokenAliasResolver.test.js +0 -260
- package/src/adopt/tailwindResolver.test.js +0 -178
- package/src/adopt/targetDiscovery.test.js +0 -227
- package/src/adopt/tokenAliasResolver.test.js +0 -319
- package/src/adopt/wrapperResolver.test.js +0 -324
- package/src/data.test.js +0 -231
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-only registry of foreign design systems `stark-cli scan-foreign`
|
|
3
|
+
* knows how to detect. Phase 1 ships one entry — shadcn/ui, the only
|
|
4
|
+
* system that scored 4/4 in the B1 spike (ADOPTION_APP_PLAN.md §10
|
|
5
|
+
* decision #25) because its copy-paste distribution puts real component
|
|
6
|
+
* source (and therefore real prop types) inside the repo, with no
|
|
7
|
+
* node_modules boundary to cross. Structured so Phase 2's installed-
|
|
8
|
+
* package adapter (MUI, most likely) adds a second entry of a different
|
|
9
|
+
* shape rather than a new code path — the same reasoning catalog.json
|
|
10
|
+
* already gives Stark's own components, applied here to foreign ones.
|
|
11
|
+
*/
|
|
12
|
+
export const FOREIGN_SYSTEMS = {
|
|
13
|
+
shadcn: {
|
|
14
|
+
id: 'shadcn',
|
|
15
|
+
label: 'shadcn/ui',
|
|
16
|
+
distribution: 'copy-paste', // vs Phase 2's 'installed-package'
|
|
17
|
+
// Same display-only hint as every installed-package entry below (see the
|
|
18
|
+
// comment above `mui`). shadcn generates React DOM components wrapping
|
|
19
|
+
// Radix web primitives, so 'web' is the only honest value — it was
|
|
20
|
+
// omitted rather than deliberated, which made shadcn the one row
|
|
21
|
+
// listForeignSystems() emitted as `platform: undefined`.
|
|
22
|
+
platform: 'web',
|
|
23
|
+
packageDetect: {
|
|
24
|
+
// shadcn is never itself an npm dependency — components.json (written
|
|
25
|
+
// by `npx shadcn init`) is the primary signal; @radix-ui/* deps (what
|
|
26
|
+
// shadcn's generated components wrap) are corroborating evidence only.
|
|
27
|
+
configFiles: ['components.json'],
|
|
28
|
+
depFallback: [/^@radix-ui\//],
|
|
29
|
+
},
|
|
30
|
+
componentDir: { parentName: 'components', dirName: 'ui' },
|
|
31
|
+
tokenSource: { markerVars: ['--background', '--primary', '--radius'] },
|
|
32
|
+
},
|
|
33
|
+
// Phase 2's 'installed-package' shape — real component/prop types live in
|
|
34
|
+
// node_modules/<npmPackage>'s .d.ts, not in repo source (see
|
|
35
|
+
// installedPackageDiscoveryResolver.js). This registry entry is a
|
|
36
|
+
// convenience label only, never a gate: cli.js's scan-foreign accepts any
|
|
37
|
+
// raw npm package name too, registered here or not. "platform" is a
|
|
38
|
+
// hint for the tested-systems list/auto-detect output only — it never
|
|
39
|
+
// restricts which --platform value actually runs against the package.
|
|
40
|
+
//
|
|
41
|
+
// An entry declares its packages one of two ways, never both:
|
|
42
|
+
// `npmPackage: 'x'` for a system published as one entry package (every
|
|
43
|
+
// entry below but `atlaskit`), or `npmPackages: ['a', 'b']` for one
|
|
44
|
+
// distributed as many sibling packages with no umbrella barrel. Read them
|
|
45
|
+
// through systemPackages() rather than either field directly; that helper
|
|
46
|
+
// is what every consumer (auto-detect, the tested-systems list, cli.js's
|
|
47
|
+
// scan dispatch) normalises through.
|
|
48
|
+
//
|
|
49
|
+
// Having an umbrella is the thing to check before reaching for
|
|
50
|
+
// `npmPackages` — a system that has one should be registered by it. Adobe
|
|
51
|
+
// React Spectrum looked like the multi-package case and is not: live
|
|
52
|
+
// probing on 2026-08-27 found `@adobe/react-spectrum` enumerating all 101
|
|
53
|
+
// components, so it would be registered single-string. What that probe did
|
|
54
|
+
// establish is that the two import styles are mutually blind — the
|
|
55
|
+
// umbrella scored 3/101 against umbrella-style imports and 0/101 against
|
|
56
|
+
// `@react-spectrum/button`-style ones, and the siblings scored the reverse.
|
|
57
|
+
// Listing an umbrella *and* its siblings together would therefore
|
|
58
|
+
// double-count the shared catalog (101 + 6 + 3 for 101 real components) and
|
|
59
|
+
// deflate the percentage, so `npmPackages` is for systems with no umbrella
|
|
60
|
+
// to choose, never a way to cover both import styles at once.
|
|
61
|
+
mui: {
|
|
62
|
+
id: 'mui',
|
|
63
|
+
label: 'MUI',
|
|
64
|
+
distribution: 'installed-package',
|
|
65
|
+
npmPackage: '@mui/material',
|
|
66
|
+
platform: 'web',
|
|
67
|
+
},
|
|
68
|
+
// The following twelve entries were live-verified against real npm installs
|
|
69
|
+
// 2026-08-25 (ADOPTION_APP_PLAN.md §10 decision #25, "broader 12-system
|
|
70
|
+
// sweep") — five needed real adapter fixes (Radix, Mantine, NativeBase,
|
|
71
|
+
// Chakra UI v3, @atlaskit/button), the rest came back clean. Restyle is
|
|
72
|
+
// registered despite its `used: 0` coverage gap — since root-caused as
|
|
73
|
+
// KNOWN_DISTRIBUTION_PATTERNS #7 (runtime-factory-synthesized), a
|
|
74
|
+
// documented architectural non-goal rather than a bug — instead of being
|
|
75
|
+
// left out: the point of this list is "what's been tested," not "what's
|
|
76
|
+
// guaranteed clean."
|
|
77
|
+
chakra: {
|
|
78
|
+
id: 'chakra',
|
|
79
|
+
label: 'Chakra UI',
|
|
80
|
+
distribution: 'installed-package',
|
|
81
|
+
npmPackage: '@chakra-ui/react',
|
|
82
|
+
platform: 'web',
|
|
83
|
+
},
|
|
84
|
+
carbon: {
|
|
85
|
+
id: 'carbon',
|
|
86
|
+
label: 'Carbon Design System',
|
|
87
|
+
distribution: 'installed-package',
|
|
88
|
+
npmPackage: '@carbon/react',
|
|
89
|
+
platform: 'web',
|
|
90
|
+
},
|
|
91
|
+
antd: {
|
|
92
|
+
id: 'antd',
|
|
93
|
+
label: 'Ant Design',
|
|
94
|
+
distribution: 'installed-package',
|
|
95
|
+
npmPackage: 'antd',
|
|
96
|
+
platform: 'web',
|
|
97
|
+
},
|
|
98
|
+
// The one genuinely umbrella-less system registered here, and the reason
|
|
99
|
+
// `npmPackages` exists. Atlaskit publishes ~100 independent `@atlaskit/*`
|
|
100
|
+
// packages and no barrel over them: `@atlaskit/core` is a 404 and bare
|
|
101
|
+
// `atlaskit` is npm's 0.0.1-security placeholder (both checked 2026-08-27).
|
|
102
|
+
// Until then this entry named a single sibling and its label said so —
|
|
103
|
+
// "Atlassian (Atlaskit Button)" — which reported Atlaskit as a
|
|
104
|
+
// four-component system. Live-verified against a real four-package install
|
|
105
|
+
// the same day: the old single-sibling form said 1/4 used (25%), the list
|
|
106
|
+
// below says 3/13 (23.1%) across the four, which is the honest denominator.
|
|
107
|
+
//
|
|
108
|
+
// The list is a curated core rather than all ~100 — every name below was
|
|
109
|
+
// confirmed to resolve on npm before being added. Siblings a repo has not
|
|
110
|
+
// installed are not an error: they land in the scan's `unresolved` array
|
|
111
|
+
// and stay out of the coverage denominator, since a component that isn't
|
|
112
|
+
// installed is not one the repo declined to use.
|
|
113
|
+
atlaskit: {
|
|
114
|
+
id: 'atlaskit',
|
|
115
|
+
label: 'Atlassian Atlaskit',
|
|
116
|
+
distribution: 'installed-package',
|
|
117
|
+
npmPackages: [
|
|
118
|
+
'@atlaskit/button',
|
|
119
|
+
'@atlaskit/textfield',
|
|
120
|
+
'@atlaskit/checkbox',
|
|
121
|
+
'@atlaskit/radio',
|
|
122
|
+
'@atlaskit/select',
|
|
123
|
+
'@atlaskit/form',
|
|
124
|
+
'@atlaskit/toggle',
|
|
125
|
+
'@atlaskit/modal-dialog',
|
|
126
|
+
'@atlaskit/drawer',
|
|
127
|
+
'@atlaskit/banner',
|
|
128
|
+
'@atlaskit/flag',
|
|
129
|
+
'@atlaskit/tooltip',
|
|
130
|
+
'@atlaskit/lozenge',
|
|
131
|
+
'@atlaskit/badge',
|
|
132
|
+
'@atlaskit/tag',
|
|
133
|
+
'@atlaskit/avatar',
|
|
134
|
+
'@atlaskit/tabs',
|
|
135
|
+
'@atlaskit/menu',
|
|
136
|
+
'@atlaskit/dropdown-menu',
|
|
137
|
+
'@atlaskit/breadcrumbs',
|
|
138
|
+
'@atlaskit/spinner',
|
|
139
|
+
'@atlaskit/progress-bar',
|
|
140
|
+
'@atlaskit/calendar',
|
|
141
|
+
'@atlaskit/datetime-picker',
|
|
142
|
+
'@atlaskit/inline-edit',
|
|
143
|
+
],
|
|
144
|
+
platform: 'web',
|
|
145
|
+
},
|
|
146
|
+
radix: {
|
|
147
|
+
id: 'radix',
|
|
148
|
+
label: 'Radix Primitives',
|
|
149
|
+
distribution: 'installed-package',
|
|
150
|
+
npmPackage: 'radix-ui',
|
|
151
|
+
platform: 'web',
|
|
152
|
+
},
|
|
153
|
+
mantine: {
|
|
154
|
+
id: 'mantine',
|
|
155
|
+
label: 'Mantine',
|
|
156
|
+
distribution: 'installed-package',
|
|
157
|
+
npmPackage: '@mantine/core',
|
|
158
|
+
platform: 'web',
|
|
159
|
+
},
|
|
160
|
+
fluentui: {
|
|
161
|
+
id: 'fluentui',
|
|
162
|
+
label: 'Fluent UI',
|
|
163
|
+
distribution: 'installed-package',
|
|
164
|
+
npmPackage: '@fluentui/react-components',
|
|
165
|
+
platform: 'web',
|
|
166
|
+
},
|
|
167
|
+
nativebase: {
|
|
168
|
+
id: 'nativebase',
|
|
169
|
+
label: 'NativeBase',
|
|
170
|
+
distribution: 'installed-package',
|
|
171
|
+
npmPackage: 'native-base',
|
|
172
|
+
platform: 'native',
|
|
173
|
+
},
|
|
174
|
+
tamagui: {
|
|
175
|
+
id: 'tamagui',
|
|
176
|
+
label: 'Tamagui',
|
|
177
|
+
distribution: 'installed-package',
|
|
178
|
+
npmPackage: 'tamagui',
|
|
179
|
+
platform: 'native',
|
|
180
|
+
},
|
|
181
|
+
rnPaper: {
|
|
182
|
+
id: 'rnPaper',
|
|
183
|
+
label: 'React Native Paper',
|
|
184
|
+
distribution: 'installed-package',
|
|
185
|
+
npmPackage: 'react-native-paper',
|
|
186
|
+
platform: 'native',
|
|
187
|
+
},
|
|
188
|
+
restyle: {
|
|
189
|
+
id: 'restyle',
|
|
190
|
+
label: 'Restyle (Shopify)',
|
|
191
|
+
distribution: 'installed-package',
|
|
192
|
+
npmPackage: '@shopify/restyle',
|
|
193
|
+
platform: 'native',
|
|
194
|
+
},
|
|
195
|
+
rnElements: {
|
|
196
|
+
id: 'rnElements',
|
|
197
|
+
label: 'React Native Elements',
|
|
198
|
+
distribution: 'installed-package',
|
|
199
|
+
npmPackage: 'react-native-elements',
|
|
200
|
+
platform: 'native',
|
|
201
|
+
},
|
|
202
|
+
// The following six were live-verified 2026-08-26 (ADOPTION_APP_PLAN.md
|
|
203
|
+
// §10 decision #25, Track C) — chosen for distribution-mechanic diversity
|
|
204
|
+
// rather than popularity, to find export shapes the popular-and-similar
|
|
205
|
+
// systems above could not surface. Five came back clean; primereact
|
|
206
|
+
// produced KNOWN_DISTRIBUTION_PATTERNS #8. Registered here after the fact:
|
|
207
|
+
// they were verified in that round but never added, so `scan-foreign`'s
|
|
208
|
+
// tested-systems list under-reported what had actually been tested.
|
|
209
|
+
reactAria: {
|
|
210
|
+
id: 'reactAria',
|
|
211
|
+
label: 'React Aria (Adobe)',
|
|
212
|
+
distribution: 'installed-package',
|
|
213
|
+
npmPackage: 'react-aria',
|
|
214
|
+
platform: 'web',
|
|
215
|
+
},
|
|
216
|
+
headlessui: {
|
|
217
|
+
id: 'headlessui',
|
|
218
|
+
label: 'Headless UI',
|
|
219
|
+
distribution: 'installed-package',
|
|
220
|
+
npmPackage: '@headlessui/react',
|
|
221
|
+
platform: 'web',
|
|
222
|
+
},
|
|
223
|
+
blueprint: {
|
|
224
|
+
id: 'blueprint',
|
|
225
|
+
label: 'Blueprint (Palantir)',
|
|
226
|
+
distribution: 'installed-package',
|
|
227
|
+
npmPackage: '@blueprintjs/core',
|
|
228
|
+
platform: 'web',
|
|
229
|
+
},
|
|
230
|
+
// Registered on the same "what's been tested" principle as Restyle above:
|
|
231
|
+
// primereact discovers 0 components because it has no root barrel at all
|
|
232
|
+
// (every component lives under a subpath like `primereact/button`), which
|
|
233
|
+
// is pattern #8 — the adapter now reports that honestly via
|
|
234
|
+
// unresolvedReason instead of a false `installed: false`.
|
|
235
|
+
primereact: {
|
|
236
|
+
id: 'primereact',
|
|
237
|
+
label: 'PrimeReact',
|
|
238
|
+
distribution: 'installed-package',
|
|
239
|
+
npmPackage: 'primereact',
|
|
240
|
+
platform: 'web',
|
|
241
|
+
},
|
|
242
|
+
gluestack: {
|
|
243
|
+
id: 'gluestack',
|
|
244
|
+
label: 'gluestack-ui',
|
|
245
|
+
distribution: 'installed-package',
|
|
246
|
+
npmPackage: '@gluestack-ui/themed',
|
|
247
|
+
platform: 'native',
|
|
248
|
+
},
|
|
249
|
+
uiKitten: {
|
|
250
|
+
id: 'uiKitten',
|
|
251
|
+
label: 'UI Kitten (Eva Design)',
|
|
252
|
+
distribution: 'installed-package',
|
|
253
|
+
npmPackage: '@ui-kitten/components',
|
|
254
|
+
platform: 'native',
|
|
255
|
+
},
|
|
256
|
+
// Live-verified 2026-08-27 while probing whether it needed `npmPackages`.
|
|
257
|
+
// It does not — see the umbrella discussion in the block comment above
|
|
258
|
+
// `mui`: `@adobe/react-spectrum` enumerates all 101 components on its own,
|
|
259
|
+
// so it is registered by the umbrella and nothing else. Adding its
|
|
260
|
+
// `@react-spectrum/*` siblings alongside would double-count the shared
|
|
261
|
+
// catalog rather than widen it.
|
|
262
|
+
//
|
|
263
|
+
// Known limitation, specific to this entry's shape rather than a bug:
|
|
264
|
+
// coverage sees one import style at a time. Against a fixture importing
|
|
265
|
+
// from the umbrella this scored 3/101; against the same components
|
|
266
|
+
// imported from `@react-spectrum/button`/`-textfield` it scored 0/101,
|
|
267
|
+
// while the siblings scored the reverse. Adobe's docs teach the umbrella
|
|
268
|
+
// import, so that is the style registered for — but a repo that mixes both
|
|
269
|
+
// under-reports, and nothing warns about it yet.
|
|
270
|
+
spectrum: {
|
|
271
|
+
id: 'spectrum',
|
|
272
|
+
label: 'React Spectrum (Adobe)',
|
|
273
|
+
distribution: 'installed-package',
|
|
274
|
+
npmPackage: '@adobe/react-spectrum',
|
|
275
|
+
platform: 'web',
|
|
276
|
+
},
|
|
277
|
+
// The two below were live-verified 2026-08-27 against real npm installs in
|
|
278
|
+
// a throwaway fixture repo (an App.jsx importing five components each),
|
|
279
|
+
// scanned with `scan-foreign … --platform=native`.
|
|
280
|
+
//
|
|
281
|
+
// react-native-ui-lib@9.1.2 came back clean: types at `src/index.d.ts`,
|
|
282
|
+
// 71 components discovered, all five fixture imports counted, no
|
|
283
|
+
// unresolved entries and no duplicates. Worth noting for the next system
|
|
284
|
+
// with the same shape: its barrel contains `export * from 'uilib-native'`,
|
|
285
|
+
// a wildcard re-export from a sibling package, and discovery follows it
|
|
286
|
+
// without help.
|
|
287
|
+
uiLib: {
|
|
288
|
+
id: 'uiLib',
|
|
289
|
+
label: 'React Native UI Lib (Wix)',
|
|
290
|
+
distribution: 'installed-package',
|
|
291
|
+
npmPackage: 'react-native-ui-lib',
|
|
292
|
+
platform: 'native',
|
|
293
|
+
},
|
|
294
|
+
// @ant-design/react-native@5.4.3: types at `lib/index.d.ts`, 41 components
|
|
295
|
+
// discovered, three of the five fixture imports counted. Registered with
|
|
296
|
+
// its gap known rather than omitted, same as Restyle and PrimeReact above.
|
|
297
|
+
// Four barrel exports (Checkbox, Collapse, Input, List) are missed because
|
|
298
|
+
// they are declared as `typeof Internal & { …statics }` intersections —
|
|
299
|
+
// pattern #9 in installedPackageDiscoveryResolver.js, root-caused by
|
|
300
|
+
// reading the package's own .d.ts rather than inferred. A fifth absent
|
|
301
|
+
// export, ActionSheet, is excluded *correctly*: it declares only imperative
|
|
302
|
+
// methods (showActionSheetWithOptions, close), not a component.
|
|
303
|
+
antdRn: {
|
|
304
|
+
id: 'antdRn',
|
|
305
|
+
label: 'Ant Design Mobile RN',
|
|
306
|
+
distribution: 'installed-package',
|
|
307
|
+
npmPackage: '@ant-design/react-native',
|
|
308
|
+
platform: 'native',
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* The npm packages an entry's types come from, normalised to an array so no
|
|
314
|
+
* consumer has to know which of the two declaration forms an entry used —
|
|
315
|
+
* `[]` for a copy-paste system, which has no npm package at all.
|
|
316
|
+
*
|
|
317
|
+
* Single-string `npmPackage` was the only form until 2026-08-27, which made
|
|
318
|
+
* a system with no umbrella package unregisterable rather than merely
|
|
319
|
+
* untested — and the `atlaskit` entry above is what that cost in practice:
|
|
320
|
+
* `@atlaskit/button` was named as "the" package, so a repo using twenty
|
|
321
|
+
* @atlaskit/* siblings was scored against one sibling's four components.
|
|
322
|
+
* Multi-package entries are scanned per package and aggregated — see
|
|
323
|
+
* installedSystemScan.js.
|
|
324
|
+
*/
|
|
325
|
+
export function systemPackages(system) {
|
|
326
|
+
if (Array.isArray(system.npmPackages)) return system.npmPackages;
|
|
327
|
+
return system.npmPackage ? [system.npmPackage] : [];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** The tested-systems list surfaced by `scan-foreign` with no args and
|
|
331
|
+
* consulted by the auto-detector — a plain array so both consumers can
|
|
332
|
+
* map/filter without reaching into object-key iteration order.
|
|
333
|
+
* `npmPackage` stays the single primary package (first, for display and for
|
|
334
|
+
* the one-package case); `npmPackages` carries the full set. */
|
|
335
|
+
export function listForeignSystems() {
|
|
336
|
+
return Object.values(FOREIGN_SYSTEMS).map((system) => {
|
|
337
|
+
const packages = systemPackages(system);
|
|
338
|
+
return {
|
|
339
|
+
id: system.id,
|
|
340
|
+
label: system.label,
|
|
341
|
+
distribution: system.distribution,
|
|
342
|
+
npmPackage: packages[0] ?? null,
|
|
343
|
+
npmPackages: packages,
|
|
344
|
+
platform: system.platform,
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function getForeignSystem(systemId) {
|
|
350
|
+
const system = FOREIGN_SYSTEMS[systemId];
|
|
351
|
+
if (!system) {
|
|
352
|
+
const known = Object.keys(FOREIGN_SYSTEMS).join(', ');
|
|
353
|
+
throw new Error(`Unknown foreign system "${systemId}" — known systems: ${known}`);
|
|
354
|
+
}
|
|
355
|
+
return system;
|
|
356
|
+
}
|