@santi020k/lumen 1.6.0 → 1.7.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 +51 -13
- package/dist/cli.js +17 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +32 -2
- package/dist/integration-diagnostics.d.ts.map +1 -1
- package/dist/integration-diagnostics.js +8 -2
- package/dist/native-audit.d.ts +43 -2
- package/dist/native-audit.d.ts.map +1 -1
- package/dist/native-audit.js +274 -6
- package/dist/registry-data.d.ts +50 -10
- package/dist/registry-data.d.ts.map +1 -1
- package/dist/registry-data.js +117 -17
- package/dist/registry-path.d.ts +3 -0
- package/dist/registry-path.d.ts.map +1 -0
- package/dist/registry-path.js +15 -0
- package/dist/registry-types.d.ts +1 -0
- package/dist/registry-types.d.ts.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +76 -14
- package/dist/v2-migration.d.ts +29 -0
- package/dist/v2-migration.d.ts.map +1 -0
- package/dist/v2-migration.js +729 -0
- package/native-components.json +861 -0
- package/package.json +8 -3
- package/release-manifest.json +15 -14
- package/styles/critical.css +2054 -0
- package/styles.css +191 -38
package/dist/native-audit.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { access, readdir, readFile, stat } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
2
4
|
import { extname, join, relative, resolve } from 'node:path';
|
|
3
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
4
7
|
const ignoredDirectories = new Set([
|
|
5
8
|
'.build',
|
|
6
9
|
'.git',
|
|
@@ -11,7 +14,8 @@ const ignoredDirectories = new Set([
|
|
|
11
14
|
'DerivedData',
|
|
12
15
|
'node_modules'
|
|
13
16
|
]);
|
|
14
|
-
const nativeExtensions = new Set(['.gradle', '.json', '.kts', '.swift', '.toml']);
|
|
17
|
+
const nativeExtensions = new Set(['.gradle', '.json', '.kt', '.kts', '.properties', '.swift', '.toml']);
|
|
18
|
+
const execFileAsync = promisify(execFile);
|
|
15
19
|
const discoverNativeSources = async (root) => {
|
|
16
20
|
const files = [];
|
|
17
21
|
const visit = async (directory) => {
|
|
@@ -30,6 +34,235 @@ const discoverNativeSources = async (root) => {
|
|
|
30
34
|
await visit(root);
|
|
31
35
|
return Promise.all(files.map(async (file) => ({ file, source: await readFile(file, 'utf8') })));
|
|
32
36
|
};
|
|
37
|
+
const adoptionRules = [
|
|
38
|
+
{ componentId: 'text', family: 'content', platform: 'swift', primitive: 'Text' },
|
|
39
|
+
{ componentId: 'button', family: 'actions', platform: 'swift', primitive: 'Button' },
|
|
40
|
+
{ componentId: 'text-field', family: 'forms', platform: 'swift', primitive: 'TextField' },
|
|
41
|
+
{ componentId: 'toggle', family: 'forms', platform: 'swift', primitive: 'Toggle' },
|
|
42
|
+
{ componentId: 'date-field', family: 'forms', platform: 'swift', primitive: 'DatePicker' },
|
|
43
|
+
{ componentId: 'spinner', family: 'feedback', platform: 'swift', primitive: 'ProgressView' },
|
|
44
|
+
{ componentId: 'text', family: 'content', platform: 'compose', primitive: 'Text' },
|
|
45
|
+
{ componentId: 'button', family: 'actions', platform: 'compose', primitive: 'Button' },
|
|
46
|
+
{ componentId: 'text-field', family: 'forms', platform: 'compose', primitive: 'TextField' },
|
|
47
|
+
{ componentId: 'text-field', family: 'forms', platform: 'compose', primitive: 'OutlinedTextField' },
|
|
48
|
+
{ componentId: 'toggle', family: 'forms', platform: 'compose', primitive: 'Switch' },
|
|
49
|
+
{ componentId: 'checkbox', family: 'forms', platform: 'compose', primitive: 'Checkbox' },
|
|
50
|
+
{ componentId: 'radio-group', family: 'forms', platform: 'compose', primitive: 'RadioButton' },
|
|
51
|
+
{ componentId: 'date-field', family: 'forms', platform: 'compose', primitive: 'DatePicker' },
|
|
52
|
+
{ componentId: 'card', family: 'content', platform: 'compose', primitive: 'Card' },
|
|
53
|
+
{ componentId: 'divider', family: 'content', platform: 'compose', primitive: 'HorizontalDivider' },
|
|
54
|
+
{ componentId: 'spinner', family: 'feedback', platform: 'compose', primitive: 'CircularProgressIndicator' }
|
|
55
|
+
];
|
|
56
|
+
const resolveSourcePlatform = (file) => {
|
|
57
|
+
if (file.endsWith('.swift'))
|
|
58
|
+
return 'swift';
|
|
59
|
+
if (file.endsWith('.kt') || file.endsWith('.kts'))
|
|
60
|
+
return 'compose';
|
|
61
|
+
return null;
|
|
62
|
+
};
|
|
63
|
+
const inspectAdoptionInventory = (root, sources, registry) => {
|
|
64
|
+
const symbols = new Map(registry.components.map(component => [component.id, component.symbols]));
|
|
65
|
+
const suggestions = [];
|
|
66
|
+
const implementationDirectories = [
|
|
67
|
+
registry.adapters.compose.sourceDirectory,
|
|
68
|
+
...(registry.adapters.compose.additionalSourceDirectories ?? []),
|
|
69
|
+
registry.adapters.swiftUI.sourceDirectory,
|
|
70
|
+
...(registry.adapters.swiftUI.additionalSourceDirectories ?? [])
|
|
71
|
+
];
|
|
72
|
+
for (const entry of sources) {
|
|
73
|
+
const repositoryPath = relative(root, entry.file);
|
|
74
|
+
const platform = resolveSourcePlatform(entry.file);
|
|
75
|
+
if (!platform ||
|
|
76
|
+
/(?:^|\/)(?:test|tests|androidTest)(?:\/|$)/iu.test(entry.file) ||
|
|
77
|
+
implementationDirectories.some(directory => repositoryPath === directory || repositoryPath.startsWith(`${directory}/`)))
|
|
78
|
+
continue;
|
|
79
|
+
const lines = entry.source.split(/\r?\n/u);
|
|
80
|
+
lines.forEach((line, index) => {
|
|
81
|
+
const code = line.split('//', 1)[0] ?? '';
|
|
82
|
+
for (const rule of adoptionRules) {
|
|
83
|
+
if (rule.platform !== platform)
|
|
84
|
+
continue;
|
|
85
|
+
const lumenComponent = symbols.get(rule.componentId)?.[platform === 'swift' ? 'swiftUI' : 'compose'];
|
|
86
|
+
if (!lumenComponent || !new RegExp(`\\b${rule.primitive}\\s*\\(`, 'u').test(code))
|
|
87
|
+
continue;
|
|
88
|
+
suggestions.push({
|
|
89
|
+
family: rule.family,
|
|
90
|
+
file: repositoryPath,
|
|
91
|
+
line: index + 1,
|
|
92
|
+
lumenComponent,
|
|
93
|
+
nativePrimitive: rule.primitive,
|
|
94
|
+
platform
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const familyCounts = suggestions.reduce((counts, suggestion) => {
|
|
100
|
+
counts.set(suggestion.family, (counts.get(suggestion.family) ?? 0) + 1);
|
|
101
|
+
return counts;
|
|
102
|
+
}, new Map());
|
|
103
|
+
const families = [...familyCounts]
|
|
104
|
+
.map(([family, count]) => ({ count, family }))
|
|
105
|
+
.sort((first, second) => first.family.localeCompare(second.family));
|
|
106
|
+
return { families, suggestions };
|
|
107
|
+
};
|
|
108
|
+
const pathExists = async (path) => access(path).then(() => true).catch(() => false);
|
|
109
|
+
const parseJavaMajor = (value) => {
|
|
110
|
+
const match = /(?:JAVA_VERSION=|version\s+)["']?(?:1\.)?([0-9]+)/u.exec(value);
|
|
111
|
+
return match?.[1] ? Number.parseInt(match[1], 10) : undefined;
|
|
112
|
+
};
|
|
113
|
+
const readJavaMajor = async (environment) => {
|
|
114
|
+
const javaHome = environment.JAVA_HOME;
|
|
115
|
+
if (javaHome) {
|
|
116
|
+
const release = await readFile(join(javaHome, 'release'), 'utf8').catch(() => '');
|
|
117
|
+
const major = parseJavaMajor(release);
|
|
118
|
+
if (major)
|
|
119
|
+
return { home: javaHome, major };
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const result = await execFileAsync('java', ['-version'], { env: environment });
|
|
123
|
+
const major = parseJavaMajor(`${result.stdout}\n${result.stderr}`);
|
|
124
|
+
return { ...(javaHome ? { home: javaHome } : {}), ...(major ? { major } : {}) };
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return javaHome ? { home: javaHome } : {};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const resolveAndroidSdkRoot = async (root, environment) => {
|
|
131
|
+
const localProperties = await readFile(join(root, 'local.properties'), 'utf8').catch(() => '');
|
|
132
|
+
const localSdk = /^sdk\.dir=(.+)$/mu.exec(localProperties)?.[1]?.replaceAll('\\\\', '\\');
|
|
133
|
+
const candidates = [
|
|
134
|
+
environment.ANDROID_SDK_ROOT,
|
|
135
|
+
environment.ANDROID_HOME,
|
|
136
|
+
localSdk,
|
|
137
|
+
join(homedir(), 'Library', 'Android', 'sdk'),
|
|
138
|
+
join(homedir(), 'Android', 'Sdk')
|
|
139
|
+
].filter((value) => Boolean(value));
|
|
140
|
+
for (const candidate of candidates) {
|
|
141
|
+
if (await pathExists(candidate))
|
|
142
|
+
return resolve(candidate);
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
};
|
|
146
|
+
const findDeclaredNdkVersion = (sources) => {
|
|
147
|
+
for (const source of sources) {
|
|
148
|
+
const version = /(?<![A-Za-z\d_])ndkVersion\s*=\s*["']([^"']+)["']/u.exec(source.source)?.[1];
|
|
149
|
+
if (version)
|
|
150
|
+
return version;
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
};
|
|
154
|
+
const createJavaCheck = (java, toolchain) => {
|
|
155
|
+
if (!java.major) {
|
|
156
|
+
return {
|
|
157
|
+
detail: 'No readable Java runtime was found through JAVA_HOME or PATH.',
|
|
158
|
+
id: 'jdk',
|
|
159
|
+
remediation: `Install JDK ${toolchain.jdk} and set JAVA_HOME for Gradle.`,
|
|
160
|
+
status: 'failure'
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (java.major < toolchain.jdk) {
|
|
164
|
+
return {
|
|
165
|
+
detail: `JDK ${java.major} is active; Lumen documents JDK ${toolchain.jdk}.`,
|
|
166
|
+
id: 'jdk',
|
|
167
|
+
remediation: `Run Gradle with JDK ${toolchain.jdk} or newer.`,
|
|
168
|
+
status: 'failure'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
detail: `JDK ${java.major} is available${java.home ? ` at ${java.home}` : ''}.`,
|
|
173
|
+
id: 'jdk',
|
|
174
|
+
status: 'pass'
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
const inspectSdkChecks = async (sdkRoot, toolchain) => {
|
|
178
|
+
if (!sdkRoot) {
|
|
179
|
+
return [{
|
|
180
|
+
detail: 'No Android SDK directory was found through local.properties, ANDROID_SDK_ROOT, ANDROID_HOME, or platform defaults.',
|
|
181
|
+
id: 'android-sdk',
|
|
182
|
+
remediation: 'Install the Android SDK and configure sdk.dir or ANDROID_SDK_ROOT.',
|
|
183
|
+
status: 'failure'
|
|
184
|
+
}];
|
|
185
|
+
}
|
|
186
|
+
const installedPlatforms = await readdir(join(sdkRoot, 'platforms')).catch(() => []);
|
|
187
|
+
const hasPlatform = (api) => installedPlatforms.some(name => (name === `android-${api}` || name.startsWith(`android-${api}.`)));
|
|
188
|
+
const requiredPackages = [
|
|
189
|
+
{
|
|
190
|
+
available: hasPlatform(toolchain.compileSdk),
|
|
191
|
+
id: 'compile-sdk',
|
|
192
|
+
path: join(sdkRoot, 'platforms', `android-${toolchain.compileSdk}`),
|
|
193
|
+
status: 'failure'
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
available: hasPlatform(toolchain.instrumentationApi),
|
|
197
|
+
id: 'instrumentation-sdk',
|
|
198
|
+
path: join(sdkRoot, 'platforms', `android-${toolchain.instrumentationApi}`),
|
|
199
|
+
status: 'warning'
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
available: await pathExists(join(sdkRoot, 'platform-tools')),
|
|
203
|
+
id: 'platform-tools',
|
|
204
|
+
path: join(sdkRoot, 'platform-tools'),
|
|
205
|
+
status: 'warning'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
available: await pathExists(join(sdkRoot, 'build-tools')),
|
|
209
|
+
id: 'build-tools',
|
|
210
|
+
path: join(sdkRoot, 'build-tools'),
|
|
211
|
+
status: 'warning'
|
|
212
|
+
}
|
|
213
|
+
];
|
|
214
|
+
const packageChecks = requiredPackages.map(required => required.available ?
|
|
215
|
+
{ detail: `${required.id} is installed.`, id: required.id, status: 'pass' } :
|
|
216
|
+
{
|
|
217
|
+
detail: `${required.id} is missing from the Android SDK.`,
|
|
218
|
+
id: required.id,
|
|
219
|
+
remediation: `Install the repository-compatible Android package at ${required.path}.`,
|
|
220
|
+
status: required.status
|
|
221
|
+
});
|
|
222
|
+
return [
|
|
223
|
+
{ detail: `Android SDK is available at ${sdkRoot}.`, id: 'android-sdk', status: 'pass' },
|
|
224
|
+
...packageChecks
|
|
225
|
+
];
|
|
226
|
+
};
|
|
227
|
+
const inspectNdkCheck = async (sdkRoot, sources) => {
|
|
228
|
+
const ndkVersion = findDeclaredNdkVersion(sources);
|
|
229
|
+
const needsNdk = Boolean(ndkVersion) || sources.some(source => (/\b(?:externalNativeBuild|cmake)\b/u.test(source.source)));
|
|
230
|
+
if (!needsNdk) {
|
|
231
|
+
return { detail: 'No NDK-backed build configuration was detected.', id: 'ndk', status: 'skipped' };
|
|
232
|
+
}
|
|
233
|
+
if (!sdkRoot) {
|
|
234
|
+
return { detail: 'The NDK cannot be checked until the Android SDK is configured.', id: 'ndk', status: 'failure' };
|
|
235
|
+
}
|
|
236
|
+
const ndkPath = ndkVersion ? join(sdkRoot, 'ndk', ndkVersion) : join(sdkRoot, 'ndk');
|
|
237
|
+
if (await pathExists(ndkPath)) {
|
|
238
|
+
return {
|
|
239
|
+
detail: `The required Android NDK${ndkVersion ? ` ${ndkVersion}` : ''} is installed.`,
|
|
240
|
+
id: 'ndk',
|
|
241
|
+
status: 'pass'
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
detail: `The project requires an Android NDK${ndkVersion ? ` (${ndkVersion})` : ''}, but it is not installed.`,
|
|
246
|
+
id: 'ndk',
|
|
247
|
+
remediation: `Install the required side-by-side NDK at ${ndkPath}.`,
|
|
248
|
+
status: 'failure'
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
const inspectAndroidPreflight = async (root, sources, toolchain, environment = process.env) => {
|
|
252
|
+
const sdkRoot = await resolveAndroidSdkRoot(root, environment);
|
|
253
|
+
const java = await readJavaMajor(environment);
|
|
254
|
+
const checks = [
|
|
255
|
+
createJavaCheck(java, toolchain),
|
|
256
|
+
...await inspectSdkChecks(sdkRoot, toolchain),
|
|
257
|
+
await inspectNdkCheck(sdkRoot, sources)
|
|
258
|
+
];
|
|
259
|
+
return {
|
|
260
|
+
checks,
|
|
261
|
+
healthy: !checks.some(check => check.status === 'failure'),
|
|
262
|
+
...(sdkRoot ? { sdkRoot } : {}),
|
|
263
|
+
toolchain
|
|
264
|
+
};
|
|
265
|
+
};
|
|
33
266
|
const parseSwiftResolution = (entry) => {
|
|
34
267
|
if (!entry.file.endsWith('Package.resolved'))
|
|
35
268
|
return [];
|
|
@@ -154,16 +387,35 @@ const collectResolutionFindings = (manifest, resolved, usesSwift, usesCompose) =
|
|
|
154
387
|
}
|
|
155
388
|
return findings;
|
|
156
389
|
};
|
|
157
|
-
|
|
390
|
+
const detectedPlatforms = (usesSwift, usesCompose) => {
|
|
391
|
+
const platforms = [];
|
|
392
|
+
if (usesSwift)
|
|
393
|
+
platforms.push('swift');
|
|
394
|
+
if (usesCompose)
|
|
395
|
+
platforms.push('compose');
|
|
396
|
+
return platforms;
|
|
397
|
+
};
|
|
398
|
+
const nativeAuditIsHealthy = (findings, androidPreflight) => {
|
|
399
|
+
if (findings.some(finding => finding.severity === 'failure'))
|
|
400
|
+
return false;
|
|
401
|
+
return androidPreflight?.healthy ?? true;
|
|
402
|
+
};
|
|
403
|
+
export const inspectNativeAdoption = async (repository, manifestPath = fileURLToPath(new URL('../release-manifest.json', import.meta.url)), registryPath = fileURLToPath(new URL('../native-components.json', import.meta.url)), environment = process.env) => {
|
|
158
404
|
const root = resolve(repository);
|
|
159
405
|
if (!(await stat(root)).isDirectory())
|
|
160
406
|
throw new Error(`Not a directory: ${root}`);
|
|
161
407
|
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
|
|
408
|
+
const registry = JSON.parse(await readFile(registryPath, 'utf8'));
|
|
162
409
|
const sources = await discoverNativeSources(root);
|
|
163
410
|
const swiftSources = sources.filter(entry => entry.file.endsWith('.swift'));
|
|
164
|
-
const composeSources = sources.filter(entry => ['.gradle', '.kts', '.toml'].includes(extname(entry.file)));
|
|
411
|
+
const composeSources = sources.filter(entry => ['.gradle', '.kt', '.kts', '.toml'].includes(extname(entry.file)));
|
|
165
412
|
const usesSwift = usesSwiftPackage(sources, swiftSources);
|
|
166
413
|
const usesCompose = usesComposePackage(composeSources);
|
|
414
|
+
const adoptionInventory = inspectAdoptionInventory(root, sources, registry);
|
|
415
|
+
let androidPreflight = null;
|
|
416
|
+
if (usesCompose) {
|
|
417
|
+
androidPreflight = await inspectAndroidPreflight(root, sources, registry.adapters.compose.toolchain, environment);
|
|
418
|
+
}
|
|
167
419
|
const resolved = unique([
|
|
168
420
|
...sources.flatMap(parseSwiftResolution),
|
|
169
421
|
...composeSources.flatMap(parseComposeResolution)
|
|
@@ -174,9 +426,12 @@ export const inspectNativeAdoption = async (repository, manifestPath = fileURLTo
|
|
|
174
426
|
...collectResolutionFindings(manifest, resolved, usesSwift, usesCompose)
|
|
175
427
|
];
|
|
176
428
|
return {
|
|
429
|
+
adoptionInventory,
|
|
430
|
+
androidPreflight,
|
|
177
431
|
findings: unique(findings, item => `${item.file}:${item.rule}:${item.message}`),
|
|
178
432
|
generatedAt: new Date().toISOString(),
|
|
179
|
-
|
|
433
|
+
healthy: nativeAuditIsHealthy(findings, androidPreflight),
|
|
434
|
+
platforms: detectedPlatforms(usesSwift, usesCompose),
|
|
180
435
|
releaseVersion: manifest.release.version,
|
|
181
436
|
repository: root,
|
|
182
437
|
resolved
|
|
@@ -193,6 +448,19 @@ export const formatNativeAdoptionAudit = (report) => {
|
|
|
193
448
|
item.version ? `version=${item.version}` : undefined,
|
|
194
449
|
item.revision ? `revision=${item.revision}` : undefined
|
|
195
450
|
].filter(Boolean).join(' ')),
|
|
196
|
-
...report.findings.map(item => (`[${item.severity}] ${item.file} ${item.rule}: ${item.message} ${item.remediation}`))
|
|
451
|
+
...report.findings.map(item => (`[${item.severity}] ${item.file} ${item.rule}: ${item.message} ${item.remediation}`)),
|
|
452
|
+
...(report.adoptionInventory.suggestions.length === 0 ?
|
|
453
|
+
['[inventory] No likely direct native primitives with public Lumen equivalents were found.'] :
|
|
454
|
+
[
|
|
455
|
+
`[inventory] ${report.adoptionInventory.suggestions.length} non-authoritative adoption suggestion${report.adoptionInventory.suggestions.length === 1 ? '' : 's'} across ${report.adoptionInventory.families.length} families.`,
|
|
456
|
+
...report.adoptionInventory.families.map(family => ` ${family.family}: ${family.count}`),
|
|
457
|
+
...report.adoptionInventory.suggestions.map(suggestion => (`[suggestion] ${suggestion.file}:${suggestion.line} ${suggestion.nativePrimitive} may map to ${suggestion.lumenComponent}; verify product ownership and behavior before adopting.`))
|
|
458
|
+
]),
|
|
459
|
+
...(report.androidPreflight ?
|
|
460
|
+
[
|
|
461
|
+
`[android-preflight] ${report.androidPreflight.healthy ? 'ready' : 'blocked'} for the documented JDK ${report.androidPreflight.toolchain.jdk}, compile SDK ${report.androidPreflight.toolchain.compileSdk}, Gradle ${report.androidPreflight.toolchain.gradle}, AGP ${report.androidPreflight.toolchain.androidGradlePlugin}, and Kotlin ${report.androidPreflight.toolchain.kotlin} toolchain.`,
|
|
462
|
+
...report.androidPreflight.checks.map(check => (`[${check.status}] ${check.id}: ${check.detail}${check.remediation ? ` ${check.remediation}` : ''}`))
|
|
463
|
+
] :
|
|
464
|
+
[])
|
|
197
465
|
].join('\n');
|
|
198
466
|
};
|
package/dist/registry-data.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export declare const lumenRegistry: {
|
|
|
11
11
|
readonly name: "advanced-fields";
|
|
12
12
|
readonly type: "recipe";
|
|
13
13
|
readonly components: readonly ["Autocomplete", "SearchField", "NumberField", "TimeField", "DateRangePicker", "ColorPicker", "TagGroup"];
|
|
14
|
+
}, {
|
|
15
|
+
readonly name: "error-handling";
|
|
16
|
+
readonly type: "recipe";
|
|
17
|
+
readonly components: readonly ["Alert", "ErrorState", "ErrorSummary", "FieldError", "Toast"];
|
|
18
|
+
readonly files: readonly ["docs/error-handling.md"];
|
|
14
19
|
}, {
|
|
15
20
|
readonly name: "scheduler";
|
|
16
21
|
readonly type: "recipe";
|
|
@@ -34,7 +39,7 @@ export declare const lumenRegistry: {
|
|
|
34
39
|
}, {
|
|
35
40
|
readonly name: "data-visualization";
|
|
36
41
|
readonly type: "recipe";
|
|
37
|
-
readonly components: readonly ["Chart", "Sparkline", "BarChart", "LineChart", "PieChart", "Stat", "Meter"];
|
|
42
|
+
readonly components: readonly ["Chart", "Sparkline", "BarChart", "LineChart", "PieChart", "ScatterChart", "Heatmap", "RangeChart", "ComboChart", "Stat", "Meter"];
|
|
38
43
|
}, {
|
|
39
44
|
readonly name: "analytics-dashboard";
|
|
40
45
|
readonly type: "recipe";
|
|
@@ -402,6 +407,13 @@ export declare const lumenRegistry: {
|
|
|
402
407
|
readonly category: "Feedback";
|
|
403
408
|
readonly files: readonly ["packages/astro/components/Empty.astro", "packages/astro/styles/lumen.css"];
|
|
404
409
|
readonly dependencies: readonly ["styles"];
|
|
410
|
+
}, {
|
|
411
|
+
readonly name: "ErrorState";
|
|
412
|
+
readonly type: "component";
|
|
413
|
+
readonly description: "Explains why primary content is unavailable and provides an application-owned recovery path.";
|
|
414
|
+
readonly category: "Feedback";
|
|
415
|
+
readonly files: readonly ["packages/core/src/error-state.ts", "packages/astro/components/ErrorState.astro", "packages/astro/components/Illustration.astro", "packages/astro/styles/lumen.css", "docs/error-handling.md"];
|
|
416
|
+
readonly dependencies: readonly ["Illustration", "styles"];
|
|
405
417
|
}, {
|
|
406
418
|
readonly name: "ErrorSummary";
|
|
407
419
|
readonly type: "component";
|
|
@@ -629,10 +641,10 @@ export declare const lumenRegistry: {
|
|
|
629
641
|
}, {
|
|
630
642
|
readonly name: "PhoneInput";
|
|
631
643
|
readonly type: "component";
|
|
632
|
-
readonly description: "
|
|
644
|
+
readonly description: "Combines localized country metadata, calling codes, formatting, validation, and E.164 output.";
|
|
633
645
|
readonly category: "Forms";
|
|
634
646
|
readonly files: readonly ["packages/astro/components/PhoneInput.astro", "packages/astro/styles/lumen.css"];
|
|
635
|
-
readonly dependencies: readonly ["Input", "NativeSelect", "styles"];
|
|
647
|
+
readonly dependencies: readonly ["Input", "NativeSelect", "styles", "runtime"];
|
|
636
648
|
}, {
|
|
637
649
|
readonly name: "PieChart";
|
|
638
650
|
readonly type: "component";
|
|
@@ -766,13 +778,6 @@ export declare const lumenRegistry: {
|
|
|
766
778
|
readonly category: "Forms";
|
|
767
779
|
readonly files: readonly ["packages/astro/components/Slider.astro", "packages/astro/styles/lumen.css"];
|
|
768
780
|
readonly dependencies: readonly ["styles"];
|
|
769
|
-
}, {
|
|
770
|
-
readonly name: "Sonner";
|
|
771
|
-
readonly type: "component";
|
|
772
|
-
readonly description: "Provides the toast viewport for notifications.";
|
|
773
|
-
readonly category: "Feedback";
|
|
774
|
-
readonly files: readonly ["packages/astro/components/Sonner.astro", "packages/astro/styles/lumen.css", "packages/astro/runtime/UIPrimitives.astro"];
|
|
775
|
-
readonly dependencies: readonly ["styles", "runtime"];
|
|
776
781
|
}, {
|
|
777
782
|
readonly name: "Sparkline";
|
|
778
783
|
readonly type: "component";
|
|
@@ -850,6 +855,13 @@ export declare const lumenRegistry: {
|
|
|
850
855
|
readonly category: "Feedback";
|
|
851
856
|
readonly files: readonly ["packages/astro/components/Toast.astro", "packages/astro/styles/lumen.css", "packages/astro/runtime/UIPrimitives.astro"];
|
|
852
857
|
readonly dependencies: readonly ["styles", "runtime"];
|
|
858
|
+
}, {
|
|
859
|
+
readonly name: "ToastViewport";
|
|
860
|
+
readonly type: "component";
|
|
861
|
+
readonly description: "Provides the configurable viewport for transient toast notifications.";
|
|
862
|
+
readonly category: "Feedback";
|
|
863
|
+
readonly files: readonly ["packages/astro/components/ToastViewport.astro", "packages/astro/styles/lumen.css", "packages/astro/runtime/UIPrimitives.astro"];
|
|
864
|
+
readonly dependencies: readonly ["styles", "runtime"];
|
|
853
865
|
}, {
|
|
854
866
|
readonly name: "Toggle";
|
|
855
867
|
readonly type: "component";
|
|
@@ -1137,6 +1149,34 @@ export declare const lumenRegistry: {
|
|
|
1137
1149
|
readonly description: "Arranges content in fixed or responsive tokenized columns.";
|
|
1138
1150
|
readonly files: readonly ["packages/astro/components/Grid.astro", "packages/astro/styles/lumen.css"];
|
|
1139
1151
|
readonly dependencies: readonly ["styles"];
|
|
1152
|
+
}, {
|
|
1153
|
+
readonly name: "ScatterChart";
|
|
1154
|
+
readonly type: "component";
|
|
1155
|
+
readonly category: "Data display";
|
|
1156
|
+
readonly description: "Shows numeric scatter and bubble relationships with accessible fallback data.";
|
|
1157
|
+
readonly files: readonly ["packages/astro/components/ScatterChart.astro", "packages/astro/components/Chart.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
|
|
1158
|
+
readonly dependencies: readonly ["Chart", "styles"];
|
|
1159
|
+
}, {
|
|
1160
|
+
readonly name: "Heatmap";
|
|
1161
|
+
readonly type: "component";
|
|
1162
|
+
readonly category: "Data display";
|
|
1163
|
+
readonly description: "Encodes a labeled matrix with the canonical sequential visualization scale.";
|
|
1164
|
+
readonly files: readonly ["packages/astro/components/Heatmap.astro", "packages/astro/components/Chart.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
|
|
1165
|
+
readonly dependencies: readonly ["Chart", "styles"];
|
|
1166
|
+
}, {
|
|
1167
|
+
readonly name: "RangeChart";
|
|
1168
|
+
readonly type: "component";
|
|
1169
|
+
readonly category: "Data display";
|
|
1170
|
+
readonly description: "Shows ordered low-to-high intervals as a tokenized range band.";
|
|
1171
|
+
readonly files: readonly ["packages/astro/components/RangeChart.astro", "packages/astro/components/Chart.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
|
|
1172
|
+
readonly dependencies: readonly ["Chart", "styles"];
|
|
1173
|
+
}, {
|
|
1174
|
+
readonly name: "ComboChart";
|
|
1175
|
+
readonly type: "component";
|
|
1176
|
+
readonly category: "Data display";
|
|
1177
|
+
readonly description: "Mixes bars, lines, and areas on one meaningful shared value domain.";
|
|
1178
|
+
readonly files: readonly ["packages/astro/components/ComboChart.astro", "packages/astro/components/Chart.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
|
|
1179
|
+
readonly dependencies: readonly ["Chart", "styles"];
|
|
1140
1180
|
}, {
|
|
1141
1181
|
readonly name: "VisuallyHidden";
|
|
1142
1182
|
readonly type: "component";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-data.d.ts","sourceRoot":"","sources":["../src/registry-data.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"registry-data.d.ts","sourceRoot":"","sources":["../src/registry-data.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAm5EQ,CAAA"}
|
package/dist/registry-data.js
CHANGED
|
@@ -34,6 +34,20 @@ export const lumenRegistry = {
|
|
|
34
34
|
'TagGroup'
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
name: 'error-handling',
|
|
39
|
+
type: 'recipe',
|
|
40
|
+
components: [
|
|
41
|
+
'Alert',
|
|
42
|
+
'ErrorState',
|
|
43
|
+
'ErrorSummary',
|
|
44
|
+
'FieldError',
|
|
45
|
+
'Toast'
|
|
46
|
+
],
|
|
47
|
+
files: [
|
|
48
|
+
'docs/error-handling.md'
|
|
49
|
+
]
|
|
50
|
+
},
|
|
37
51
|
{
|
|
38
52
|
name: 'scheduler',
|
|
39
53
|
type: 'recipe',
|
|
@@ -96,6 +110,10 @@ export const lumenRegistry = {
|
|
|
96
110
|
'BarChart',
|
|
97
111
|
'LineChart',
|
|
98
112
|
'PieChart',
|
|
113
|
+
'ScatterChart',
|
|
114
|
+
'Heatmap',
|
|
115
|
+
'RangeChart',
|
|
116
|
+
'ComboChart',
|
|
99
117
|
'Stat',
|
|
100
118
|
'Meter'
|
|
101
119
|
]
|
|
@@ -901,6 +919,23 @@ export const lumenRegistry = {
|
|
|
901
919
|
'styles'
|
|
902
920
|
]
|
|
903
921
|
},
|
|
922
|
+
{
|
|
923
|
+
name: 'ErrorState',
|
|
924
|
+
type: 'component',
|
|
925
|
+
description: 'Explains why primary content is unavailable and provides an application-owned recovery path.',
|
|
926
|
+
category: 'Feedback',
|
|
927
|
+
files: [
|
|
928
|
+
'packages/core/src/error-state.ts',
|
|
929
|
+
'packages/astro/components/ErrorState.astro',
|
|
930
|
+
'packages/astro/components/Illustration.astro',
|
|
931
|
+
'packages/astro/styles/lumen.css',
|
|
932
|
+
'docs/error-handling.md'
|
|
933
|
+
],
|
|
934
|
+
dependencies: [
|
|
935
|
+
'Illustration',
|
|
936
|
+
'styles'
|
|
937
|
+
]
|
|
938
|
+
},
|
|
904
939
|
{
|
|
905
940
|
name: 'ErrorSummary',
|
|
906
941
|
type: 'component',
|
|
@@ -1361,7 +1396,7 @@ export const lumenRegistry = {
|
|
|
1361
1396
|
{
|
|
1362
1397
|
name: 'PhoneInput',
|
|
1363
1398
|
type: 'component',
|
|
1364
|
-
description: '
|
|
1399
|
+
description: 'Combines localized country metadata, calling codes, formatting, validation, and E.164 output.',
|
|
1365
1400
|
category: 'Forms',
|
|
1366
1401
|
files: [
|
|
1367
1402
|
'packages/astro/components/PhoneInput.astro',
|
|
@@ -1370,7 +1405,8 @@ export const lumenRegistry = {
|
|
|
1370
1405
|
dependencies: [
|
|
1371
1406
|
'Input',
|
|
1372
1407
|
'NativeSelect',
|
|
1373
|
-
'styles'
|
|
1408
|
+
'styles',
|
|
1409
|
+
'runtime'
|
|
1374
1410
|
]
|
|
1375
1411
|
},
|
|
1376
1412
|
{
|
|
@@ -1646,21 +1682,6 @@ export const lumenRegistry = {
|
|
|
1646
1682
|
'styles'
|
|
1647
1683
|
]
|
|
1648
1684
|
},
|
|
1649
|
-
{
|
|
1650
|
-
name: 'Sonner',
|
|
1651
|
-
type: 'component',
|
|
1652
|
-
description: 'Provides the toast viewport for notifications.',
|
|
1653
|
-
category: 'Feedback',
|
|
1654
|
-
files: [
|
|
1655
|
-
'packages/astro/components/Sonner.astro',
|
|
1656
|
-
'packages/astro/styles/lumen.css',
|
|
1657
|
-
'packages/astro/runtime/UIPrimitives.astro'
|
|
1658
|
-
],
|
|
1659
|
-
dependencies: [
|
|
1660
|
-
'styles',
|
|
1661
|
-
'runtime'
|
|
1662
|
-
]
|
|
1663
|
-
},
|
|
1664
1685
|
{
|
|
1665
1686
|
name: 'Sparkline',
|
|
1666
1687
|
type: 'component',
|
|
@@ -1812,6 +1833,21 @@ export const lumenRegistry = {
|
|
|
1812
1833
|
'runtime'
|
|
1813
1834
|
]
|
|
1814
1835
|
},
|
|
1836
|
+
{
|
|
1837
|
+
name: 'ToastViewport',
|
|
1838
|
+
type: 'component',
|
|
1839
|
+
description: 'Provides the configurable viewport for transient toast notifications.',
|
|
1840
|
+
category: 'Feedback',
|
|
1841
|
+
files: [
|
|
1842
|
+
'packages/astro/components/ToastViewport.astro',
|
|
1843
|
+
'packages/astro/styles/lumen.css',
|
|
1844
|
+
'packages/astro/runtime/UIPrimitives.astro'
|
|
1845
|
+
],
|
|
1846
|
+
dependencies: [
|
|
1847
|
+
'styles',
|
|
1848
|
+
'runtime'
|
|
1849
|
+
]
|
|
1850
|
+
},
|
|
1815
1851
|
{
|
|
1816
1852
|
name: 'Toggle',
|
|
1817
1853
|
type: 'component',
|
|
@@ -2335,6 +2371,70 @@ export const lumenRegistry = {
|
|
|
2335
2371
|
'styles'
|
|
2336
2372
|
]
|
|
2337
2373
|
},
|
|
2374
|
+
{
|
|
2375
|
+
name: 'ScatterChart',
|
|
2376
|
+
type: 'component',
|
|
2377
|
+
category: 'Data display',
|
|
2378
|
+
description: 'Shows numeric scatter and bubble relationships with accessible fallback data.',
|
|
2379
|
+
files: [
|
|
2380
|
+
'packages/astro/components/ScatterChart.astro',
|
|
2381
|
+
'packages/astro/components/Chart.astro',
|
|
2382
|
+
'packages/astro/styles/lumen.css',
|
|
2383
|
+
'packages/react/src/components.tsx'
|
|
2384
|
+
],
|
|
2385
|
+
dependencies: [
|
|
2386
|
+
'Chart',
|
|
2387
|
+
'styles'
|
|
2388
|
+
]
|
|
2389
|
+
},
|
|
2390
|
+
{
|
|
2391
|
+
name: 'Heatmap',
|
|
2392
|
+
type: 'component',
|
|
2393
|
+
category: 'Data display',
|
|
2394
|
+
description: 'Encodes a labeled matrix with the canonical sequential visualization scale.',
|
|
2395
|
+
files: [
|
|
2396
|
+
'packages/astro/components/Heatmap.astro',
|
|
2397
|
+
'packages/astro/components/Chart.astro',
|
|
2398
|
+
'packages/astro/styles/lumen.css',
|
|
2399
|
+
'packages/react/src/components.tsx'
|
|
2400
|
+
],
|
|
2401
|
+
dependencies: [
|
|
2402
|
+
'Chart',
|
|
2403
|
+
'styles'
|
|
2404
|
+
]
|
|
2405
|
+
},
|
|
2406
|
+
{
|
|
2407
|
+
name: 'RangeChart',
|
|
2408
|
+
type: 'component',
|
|
2409
|
+
category: 'Data display',
|
|
2410
|
+
description: 'Shows ordered low-to-high intervals as a tokenized range band.',
|
|
2411
|
+
files: [
|
|
2412
|
+
'packages/astro/components/RangeChart.astro',
|
|
2413
|
+
'packages/astro/components/Chart.astro',
|
|
2414
|
+
'packages/astro/styles/lumen.css',
|
|
2415
|
+
'packages/react/src/components.tsx'
|
|
2416
|
+
],
|
|
2417
|
+
dependencies: [
|
|
2418
|
+
'Chart',
|
|
2419
|
+
'styles'
|
|
2420
|
+
]
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
name: 'ComboChart',
|
|
2424
|
+
type: 'component',
|
|
2425
|
+
category: 'Data display',
|
|
2426
|
+
description: 'Mixes bars, lines, and areas on one meaningful shared value domain.',
|
|
2427
|
+
files: [
|
|
2428
|
+
'packages/astro/components/ComboChart.astro',
|
|
2429
|
+
'packages/astro/components/Chart.astro',
|
|
2430
|
+
'packages/astro/styles/lumen.css',
|
|
2431
|
+
'packages/react/src/components.tsx'
|
|
2432
|
+
],
|
|
2433
|
+
dependencies: [
|
|
2434
|
+
'Chart',
|
|
2435
|
+
'styles'
|
|
2436
|
+
]
|
|
2437
|
+
},
|
|
2338
2438
|
{
|
|
2339
2439
|
name: 'VisuallyHidden',
|
|
2340
2440
|
type: 'component',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-path.d.ts","sourceRoot":"","sources":["../src/registry-path.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,GAAI,MAAM,MAAM,KAAG,OAQrD,CAAA;AAED,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,KAAG,IAIzD,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
|
+
const windowsAbsolutePath = /^[A-Za-z]:[\\/]/;
|
|
3
|
+
export const isSafeRegistryFilePath = (path) => {
|
|
4
|
+
if (!path || path.includes('\0') || path.includes('\\'))
|
|
5
|
+
return false;
|
|
6
|
+
if (isAbsolute(path) || windowsAbsolutePath.test(path))
|
|
7
|
+
return false;
|
|
8
|
+
const segments = path.split('/');
|
|
9
|
+
return segments.every(segment => segment !== '' && segment !== '.' && segment !== '..');
|
|
10
|
+
};
|
|
11
|
+
export const assertSafeRegistryFilePath = (path) => {
|
|
12
|
+
if (!isSafeRegistryFilePath(path)) {
|
|
13
|
+
throw new Error(`Unsafe Lumen registry file path: ${JSON.stringify(path)}`);
|
|
14
|
+
}
|
|
15
|
+
};
|