@birdapi/velinstyle 1.2.1 → 1.3.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.de.md +148 -11
- package/README.md +174 -16
- package/cli/__fixtures__/atelier-library/showcases/01-login/app.css +1 -0
- package/cli/__fixtures__/atelier-library/showcases/01-login/app.js +2 -0
- package/cli/__fixtures__/atelier-library/showcases/01-login/index.html +5 -0
- package/cli/__fixtures__/atelier-library/showcases/04-pricing/app.css +1 -0
- package/cli/__fixtures__/atelier-library/showcases/04-pricing/app.js +2 -0
- package/cli/__fixtures__/atelier-library/showcases/04-pricing/index.html +5 -0
- package/cli/__fixtures__/atelier-library/showcases/36-calendar/app.css +1 -0
- package/cli/__fixtures__/atelier-library/showcases/36-calendar/app.js +2 -0
- package/cli/__fixtures__/atelier-library/showcases/36-calendar/index.html +5 -0
- package/cli/atelier-catalog.json +1267 -0
- package/cli/atelier-formats.js +159 -0
- package/cli/atelier.js +382 -0
- package/cli/blueprints/empty-state.html +3 -5
- package/cli/cli-manifest.json +441 -161
- package/cli/docgen/extract-cli.js +2 -1
- package/cli/docs-generate.js +9 -0
- package/cli/experience.js +173 -0
- package/cli/index.js +1384 -361
- package/cli/motion.js +738 -0
- package/cli/production/component-graph.json +166 -0
- package/cli/production/explain.js +52 -0
- package/cli/production/extract.js +278 -0
- package/cli/production/graph.js +102 -0
- package/cli/production/report.js +168 -0
- package/cli/production/run.js +355 -0
- package/cli/production/trim-css.js +177 -0
- package/cli/production/trim-fonts.js +23 -0
- package/cli/production/trim-icons.js +38 -0
- package/cli/production/trim-js.js +48 -0
- package/cli/production/trim-motion.js +22 -0
- package/cli/production/trim-themes.js +50 -0
- package/cli/production/watch.js +38 -0
- package/cli/review.js +48 -1
- package/cli/scripts/write-atelier-fixtures.mjs +33 -0
- package/cli/security.js +190 -0
- package/cli/skills.js +53 -10
- package/cli/transparency.js +140 -41
- package/cli/workflow.js +32 -17
- package/components/index.js +3 -0
- package/components/runtime/component-loaders.js +3 -0
- package/components/velin-drawer.js +43 -4
- package/components/velin-empty-state.js +83 -0
- package/components/velin-modal.js +76 -15
- package/components/velin-otp-input.js +220 -0
- package/components/velin-password-strength.js +147 -0
- package/components/velin-sheet.js +49 -4
- package/components/velin-theme-toggle.js +15 -1
- package/core/a11y/component-contracts.json +391 -255
- package/core/attributes/registry.js +1 -1
- package/core/meta/knowledge/components.json +75 -3
- package/core/motion/analyze.js +38 -0
- package/core/motion/blueprint.js +51 -0
- package/core/motion/composer.js +114 -0
- package/core/motion/data/registry.json +352 -0
- package/core/motion/effects.js +17 -1
- package/core/motion/index.js +112 -9
- package/core/motion/optimize.js +44 -0
- package/core/motion/policy.js +77 -0
- package/core/motion/registry.js +131 -0
- package/core/motion/review.js +59 -0
- package/core/motion/scan.js +150 -0
- package/core/motion/timeline.js +68 -0
- package/core/motion/transitions.js +49 -0
- package/core/motion/triggers.js +168 -0
- package/core/security/checks/fs.js +92 -0
- package/core/security/detect.js +35 -0
- package/core/security/engine.js +116 -0
- package/core/security/index.js +18 -0
- package/core/security/registry.js +85 -0
- package/core/security/report/report.js +113 -0
- package/core/security/rules/ci/index.js +146 -0
- package/core/security/rules/consumer/index.js +174 -0
- package/core/security/rules/deps/index.js +182 -0
- package/core/security/rules/index.js +24 -0
- package/core/security/rules/publish/index.js +230 -0
- package/core/security/rules/repo/index.js +143 -0
- package/core/security/rules/secrets/index.js +133 -0
- package/core/security/score/dependency-health.js +53 -0
- package/core/security/score/release-health.js +66 -0
- package/core/security/score/score.js +102 -0
- package/core/security/timeline/timeline.js +76 -0
- package/dist/chunks/attributes-2ORR27KA.js +404 -0
- package/dist/chunks/attributes-HK7VIOLA.js +1080 -0
- package/dist/chunks/attributes-VRHHVNDO.js +1080 -0
- package/dist/chunks/chunk-MYKUI364.js +116 -0
- package/dist/chunks/chunk-NEVBPD5T.js +116 -0
- package/dist/chunks/chunk-Y6HN7HWX.js +116 -0
- package/dist/chunks/runtime-entry.js +1 -1
- package/dist/chunks/velin-drawer-A7Q7EBOS.js +162 -0
- package/dist/chunks/velin-empty-state-3NTUH2RF.js +81 -0
- package/dist/chunks/velin-modal-3MV4FYBK.js +231 -0
- package/dist/chunks/velin-otp-input-PSK42MM6.js +207 -0
- package/dist/chunks/velin-password-strength-TAXMLSED.js +136 -0
- package/dist/chunks/velin-sheet-N2VVYXFP.js +157 -0
- package/dist/chunks/velin-theme-toggle-J2NHC7IM.js +304 -0
- package/dist/llms.txt +4 -4
- package/dist/search-index.json +232 -5
- package/dist/velin-agent.json +433 -46
- package/dist/velinstyle-components.iife.js +3695 -2272
- package/dist/velinstyle-components.js +3698 -2271
- package/dist/velinstyle-components.min.js +262 -120
- package/dist/velinstyle.css +96 -13
- package/dist/velinstyle.d.ts +3 -0
- package/dist/velinstyle.min.css +1 -1
- package/package.json +19 -4
- package/packages/velinstyle-cli-core/package.json +11 -0
- package/packages/velinstyle-cli-core/src/config.js +84 -0
- package/packages/velinstyle-cli-core/src/contract.js +56 -0
- package/packages/velinstyle-cli-core/src/event-bus.js +50 -0
- package/packages/velinstyle-cli-core/src/index.js +18 -0
- package/packages/velinstyle-cli-core/src/lifecycle.js +33 -0
- package/packages/velinstyle-cli-core/src/runner.js +129 -0
- package/packages/velinstyle-cli-registry/data/commands.json +1216 -0
- package/packages/velinstyle-cli-registry/data/layouts.json +33 -0
- package/packages/velinstyle-cli-registry/data/widgets.json +35 -0
- package/packages/velinstyle-cli-registry/package.json +13 -0
- package/packages/velinstyle-cli-registry/src/index.js +88 -0
- package/packages/velinstyle-cli-renderer/package.json +8 -0
- package/packages/velinstyle-cli-renderer/src/index.js +286 -0
- package/packages/velinstyle-cli-theme/package.json +8 -0
- package/packages/velinstyle-cli-theme/src/index.js +84 -0
- package/packages/velinstyle-cli-ui/package.json +8 -0
- package/packages/velinstyle-cli-ui/src/index.js +34 -0
- package/packages/velinstyle-motion/README.md +81 -0
- package/packages/velinstyle-motion/package.json +37 -0
- package/packages/velinstyle-motion/src/ai/mini-prompt.js +119 -0
- package/packages/velinstyle-motion/src/ai/provider.js +59 -0
- package/packages/velinstyle-motion/src/ai/providers/pixverse-setup.js +402 -0
- package/packages/velinstyle-motion/src/ai/providers/pixverse.js +245 -0
- package/packages/velinstyle-motion/src/config.js +257 -0
- package/packages/velinstyle-motion/src/export/html-clip.js +141 -0
- package/packages/velinstyle-motion/src/index.js +304 -0
- package/packages/velinstyle-motion/src/jobs/cache.js +30 -0
- package/packages/velinstyle-motion/src/jobs/queue.js +69 -0
- package/packages/velinstyle-motion/src/jobs/usage.js +53 -0
- package/packages/velinstyle-motion/src/local/effects.js +139 -0
- package/packages/velinstyle-motion/src/local/engine.js +96 -0
- package/packages/velinstyle-motion/src/providers-catalog.js +44 -0
- package/packages/velinstyle-motion/src/registry/presets.js +25 -0
- package/packages/velinstyle-motion/src/registry/presets.json +85 -0
- package/packages/velinstyle-motion/src/ux/cost-gate.js +37 -0
- package/packages/velinstyle-motion/src/ux/pixverse-gate.js +153 -0
- package/packages/velinstyle-motion/src/ux/wizard.js +171 -0
- package/packages/velinstyle-motion/src/validate-image.js +91 -0
- package/packages/velinstyle-skills/catalog.json +1 -1
- package/packages/velinstyle-skills/registry.json +1 -142
- package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +1 -1
- package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +1 -1
- package/src/components/data-table.css +19 -0
- package/src/components/empty-auth.css +33 -0
- package/src/components/table.css +16 -6
- package/src/tokens/motion.css +7 -0
- package/src/utilities/scroll-animation.css +66 -0
- package/src/velinstyle.css +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@birdapi/velinstyle",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"registry": "https://registry.npmjs.org"
|
|
@@ -24,12 +24,20 @@
|
|
|
24
24
|
"./email": "./components/velin-email.js",
|
|
25
25
|
"./search": "./core/search/index.js",
|
|
26
26
|
"./motion": "./core/motion/index.js",
|
|
27
|
+
"./motion-clip": "./packages/velinstyle-motion/src/index.js",
|
|
27
28
|
"./attributes": "./core/attributes/index.js",
|
|
28
29
|
"./highlight": "./core/highlight/index.js",
|
|
29
30
|
"./meta": "./core/meta/index.js",
|
|
30
31
|
"./transparency": "./core/transparency/index.js",
|
|
32
|
+
"./security": "./core/security/index.js",
|
|
31
33
|
"./a11y": "./components/a11y-entry.js",
|
|
32
|
-
"./skills-registry": "./packages/velinstyle-skills/registry.json"
|
|
34
|
+
"./skills-registry": "./packages/velinstyle-skills/registry.json",
|
|
35
|
+
"./experience": "./cli/experience.js",
|
|
36
|
+
"./cli-core": "./packages/velinstyle-cli-core/src/index.js",
|
|
37
|
+
"./cli-registry": "./packages/velinstyle-cli-registry/src/index.js",
|
|
38
|
+
"./cli-theme": "./packages/velinstyle-cli-theme/src/index.js",
|
|
39
|
+
"./cli-ui": "./packages/velinstyle-cli-ui/src/index.js",
|
|
40
|
+
"./cli-renderer": "./packages/velinstyle-cli-renderer/src/index.js"
|
|
33
41
|
},
|
|
34
42
|
"bin": {
|
|
35
43
|
"velinstyle": "cli/index.js"
|
|
@@ -52,7 +60,13 @@
|
|
|
52
60
|
"packages/velinstyle-skills/",
|
|
53
61
|
"schemas/",
|
|
54
62
|
"examples/search-index.schema.json",
|
|
55
|
-
"icons/velin-icons.svg"
|
|
63
|
+
"icons/velin-icons.svg",
|
|
64
|
+
"packages/velinstyle-cli-core/",
|
|
65
|
+
"packages/velinstyle-cli-registry/",
|
|
66
|
+
"packages/velinstyle-cli-theme/",
|
|
67
|
+
"packages/velinstyle-cli-ui/",
|
|
68
|
+
"packages/velinstyle-cli-renderer/",
|
|
69
|
+
"packages/velinstyle-motion/"
|
|
56
70
|
],
|
|
57
71
|
"scripts": {
|
|
58
72
|
"build": "npm run build:css:min && npm run build:css:dev && npm run build:js:min && npm run build:js:dev && npm run build:js:iife && npm run build:js:chunks && npm run build:loaders && npm run build:react:src && npm run build:types && npm run build:themes && npm run build:icons",
|
|
@@ -75,7 +89,8 @@
|
|
|
75
89
|
"test:a11y": "node tests/a11y/run.js",
|
|
76
90
|
"test:a11y:coverage": "node tests/a11y/coverage.js",
|
|
77
91
|
"test:contrast": "node scripts/check-contrast.js",
|
|
78
|
-
"test:
|
|
92
|
+
"test:motion-clip": "vitest run tests/motion-clip.test.js",
|
|
93
|
+
"test:security": "vitest run tests/sanitize.test.js tests/cli-security-scanner.test.js tests/security.test.js",
|
|
79
94
|
"test:perf": "vitest run tests/perf-audit.test.js",
|
|
80
95
|
"test:search:bench": "vitest run tests/search-benchmark.test.js",
|
|
81
96
|
"test:e2e": "playwright test",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@birdapi/velinstyle-cli-core",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"description": "Velin Experience core — lifecycle, event bus, config, output contract"
|
|
11
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Velin Experience config — velinstyle-cli.json (+ flags/env).
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join, resolve } from 'path';
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_EXPERIENCE_CONFIG = {
|
|
8
|
+
theme: 'modern',
|
|
9
|
+
output: 'standard',
|
|
10
|
+
icons: true,
|
|
11
|
+
unicode: true,
|
|
12
|
+
animations: true,
|
|
13
|
+
progress: true,
|
|
14
|
+
compact: false,
|
|
15
|
+
analytics: false,
|
|
16
|
+
record: false,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const CONFIG_NAMES = ['velinstyle-cli.json', 'velin-experience.json'];
|
|
20
|
+
|
|
21
|
+
export function loadExperienceConfig(cwd = process.cwd(), overrides = {}) {
|
|
22
|
+
const root = resolve(cwd);
|
|
23
|
+
let fileConfig = {};
|
|
24
|
+
for (const name of CONFIG_NAMES) {
|
|
25
|
+
const path = join(root, name);
|
|
26
|
+
if (existsSync(path)) {
|
|
27
|
+
try {
|
|
28
|
+
fileConfig = JSON.parse(readFileSync(path, 'utf8'));
|
|
29
|
+
} catch {
|
|
30
|
+
fileConfig = {};
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const envTheme = process.env.VELIN_EXPERIENCE_THEME;
|
|
37
|
+
const envOutput = process.env.VELIN_EXPERIENCE_OUTPUT;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...DEFAULT_EXPERIENCE_CONFIG,
|
|
41
|
+
...fileConfig,
|
|
42
|
+
...(envTheme ? { theme: envTheme } : {}),
|
|
43
|
+
...(envOutput ? { output: envOutput } : {}),
|
|
44
|
+
...overrides,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function analyticsPath(cwd = process.cwd()) {
|
|
49
|
+
return join(resolve(cwd), '.velin', 'experience-analytics.json');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function recordUsage(commandId, cwd = process.cwd()) {
|
|
53
|
+
const path = analyticsPath(cwd);
|
|
54
|
+
mkdirSync(join(resolve(cwd), '.velin'), { recursive: true });
|
|
55
|
+
let data = { commands: {} };
|
|
56
|
+
if (existsSync(path)) {
|
|
57
|
+
try {
|
|
58
|
+
data = JSON.parse(readFileSync(path, 'utf8'));
|
|
59
|
+
} catch {
|
|
60
|
+
data = { commands: {} };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
data.commands[commandId] = (data.commands[commandId] || 0) + 1;
|
|
64
|
+
data.updatedAt = new Date().toISOString();
|
|
65
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function suggestFromAnalytics(cwd = process.cwd()) {
|
|
70
|
+
const path = analyticsPath(cwd);
|
|
71
|
+
if (!existsSync(path)) return null;
|
|
72
|
+
let data;
|
|
73
|
+
try {
|
|
74
|
+
data = JSON.parse(readFileSync(path, 'utf8'));
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const cmds = data.commands || {};
|
|
79
|
+
const hot = ['production', 'review', 'doctor'].filter((id) => (cmds[id] || 0) >= 2);
|
|
80
|
+
if (hot.length >= 2 && !(cmds.dashboard > 0)) {
|
|
81
|
+
return 'You often run production / review / doctor — try `velinstyle dashboard`.';
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output Contract — every command fills the same shape (no free-form text).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function createEmptyContract(partial = {}) {
|
|
6
|
+
return {
|
|
7
|
+
project: partial.project || {},
|
|
8
|
+
lifecycle: partial.lifecycle || { stage: 'init', stages: [] },
|
|
9
|
+
result: partial.result || { status: 'PASS', message: '' },
|
|
10
|
+
stats: partial.stats || {},
|
|
11
|
+
widgets: Array.isArray(partial.widgets) ? [...partial.widgets] : [],
|
|
12
|
+
suggestions: Array.isArray(partial.suggestions) ? [...partial.suggestions] : [],
|
|
13
|
+
diagnostics: Array.isArray(partial.diagnostics) ? [...partial.diagnostics] : [],
|
|
14
|
+
notifications: Array.isArray(partial.notifications) ? [...partial.notifications] : [],
|
|
15
|
+
meta: partial.meta || {},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function validateContract(contract) {
|
|
20
|
+
const problems = [];
|
|
21
|
+
if (!contract || typeof contract !== 'object') {
|
|
22
|
+
return { ok: false, problems: ['contract must be an object'] };
|
|
23
|
+
}
|
|
24
|
+
for (const key of ['project', 'lifecycle', 'result', 'stats']) {
|
|
25
|
+
if (contract[key] == null || typeof contract[key] !== 'object') {
|
|
26
|
+
problems.push(`missing or invalid "${key}"`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const key of ['widgets', 'suggestions', 'diagnostics']) {
|
|
30
|
+
if (!Array.isArray(contract[key])) problems.push(`"${key}" must be an array`);
|
|
31
|
+
}
|
|
32
|
+
if (contract.result && !['PASS', 'WARNING', 'ERROR', 'SUGGESTION'].includes(contract.result.status)) {
|
|
33
|
+
problems.push('result.status must be PASS|WARNING|ERROR|SUGGESTION');
|
|
34
|
+
}
|
|
35
|
+
return { ok: problems.length === 0, problems };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function pushWidget(contract, id, props = {}) {
|
|
39
|
+
contract.widgets.push({ id, props });
|
|
40
|
+
return contract;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function pushSuggestion(contract, text, opts = {}) {
|
|
44
|
+
contract.suggestions.push({ text, ...opts });
|
|
45
|
+
return contract;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function pushDiagnostic(contract, diagnostic) {
|
|
49
|
+
contract.diagnostics.push(diagnostic);
|
|
50
|
+
return contract;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function setResult(contract, status, message = '') {
|
|
54
|
+
contract.result = { status, message };
|
|
55
|
+
return contract;
|
|
56
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Velin Experience event bus — commands emit; renderers/plugins subscribe.
|
|
3
|
+
*/
|
|
4
|
+
export function createEventBus() {
|
|
5
|
+
/** @type {Map<string, Set<Function>>} */
|
|
6
|
+
const listeners = new Map();
|
|
7
|
+
|
|
8
|
+
function on(event, handler) {
|
|
9
|
+
if (!listeners.has(event)) listeners.set(event, new Set());
|
|
10
|
+
listeners.get(event).add(handler);
|
|
11
|
+
return () => off(event, handler);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function off(event, handler) {
|
|
15
|
+
listeners.get(event)?.delete(handler);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function emit(event, payload) {
|
|
19
|
+
const set = listeners.get(event);
|
|
20
|
+
if (!set) return;
|
|
21
|
+
for (const handler of set) {
|
|
22
|
+
try {
|
|
23
|
+
handler(payload);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (event !== 'onError') emit('onError', { error: err, source: event });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function once(event, handler) {
|
|
31
|
+
const wrap = (payload) => {
|
|
32
|
+
off(event, wrap);
|
|
33
|
+
handler(payload);
|
|
34
|
+
};
|
|
35
|
+
return on(event, wrap);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { on, off, once, emit };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const EXPERIENCE_EVENTS = [
|
|
42
|
+
'onStart',
|
|
43
|
+
'onProgress',
|
|
44
|
+
'onWarning',
|
|
45
|
+
'onSuggestion',
|
|
46
|
+
'onResult',
|
|
47
|
+
'onFinish',
|
|
48
|
+
'onError',
|
|
49
|
+
'onNotification',
|
|
50
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { createEventBus, EXPERIENCE_EVENTS } from './event-bus.js';
|
|
2
|
+
export {
|
|
3
|
+
createEmptyContract,
|
|
4
|
+
validateContract,
|
|
5
|
+
pushWidget,
|
|
6
|
+
pushSuggestion,
|
|
7
|
+
pushDiagnostic,
|
|
8
|
+
setResult,
|
|
9
|
+
} from './contract.js';
|
|
10
|
+
export { LIFECYCLE_STAGES, createLifecycleTracker } from './lifecycle.js';
|
|
11
|
+
export {
|
|
12
|
+
DEFAULT_EXPERIENCE_CONFIG,
|
|
13
|
+
loadExperienceConfig,
|
|
14
|
+
recordUsage,
|
|
15
|
+
suggestFromAnalytics,
|
|
16
|
+
analyticsPath,
|
|
17
|
+
} from './config.js';
|
|
18
|
+
export { runExperienceCommand } from './runner.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Fixed lifecycle stages for every Experience command. */
|
|
2
|
+
export const LIFECYCLE_STAGES = [
|
|
3
|
+
'init',
|
|
4
|
+
'loadConfig',
|
|
5
|
+
'environment',
|
|
6
|
+
'input',
|
|
7
|
+
'analysis',
|
|
8
|
+
'processing',
|
|
9
|
+
'validation',
|
|
10
|
+
'result',
|
|
11
|
+
'hints',
|
|
12
|
+
'summary',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export function createLifecycleTracker() {
|
|
16
|
+
const stages = [];
|
|
17
|
+
let current = 'init';
|
|
18
|
+
|
|
19
|
+
function enter(stage) {
|
|
20
|
+
if (!LIFECYCLE_STAGES.includes(stage)) {
|
|
21
|
+
throw new Error(`Unknown lifecycle stage: ${stage}`);
|
|
22
|
+
}
|
|
23
|
+
current = stage;
|
|
24
|
+
stages.push({ stage, at: new Date().toISOString() });
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function snapshot() {
|
|
29
|
+
return { stage: current, stages: [...stages] };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { enter, snapshot, get stage() { return current; } };
|
|
33
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Experience command runner — lifecycle + bus + contract collection.
|
|
3
|
+
*/
|
|
4
|
+
import { createEventBus } from './event-bus.js';
|
|
5
|
+
import { createEmptyContract, validateContract } from './contract.js';
|
|
6
|
+
import { createLifecycleTracker } from './lifecycle.js';
|
|
7
|
+
import { loadExperienceConfig, recordUsage, suggestFromAnalytics } from './config.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {object} opts
|
|
11
|
+
* @param {string} opts.commandId
|
|
12
|
+
* @param {object} [opts.configOverrides]
|
|
13
|
+
* @param {string} [opts.cwd]
|
|
14
|
+
* @param {(ctx: object) => Promise<object>|object} opts.run — returns contract partial or full contract
|
|
15
|
+
*/
|
|
16
|
+
export async function runExperienceCommand(opts) {
|
|
17
|
+
const {
|
|
18
|
+
commandId,
|
|
19
|
+
configOverrides = {},
|
|
20
|
+
cwd = process.cwd(),
|
|
21
|
+
run,
|
|
22
|
+
layout = 'standard',
|
|
23
|
+
meta = {},
|
|
24
|
+
} = opts;
|
|
25
|
+
|
|
26
|
+
const config = loadExperienceConfig(cwd, configOverrides);
|
|
27
|
+
const bus = createEventBus();
|
|
28
|
+
const lifecycle = createLifecycleTracker();
|
|
29
|
+
const startedAt = Date.now();
|
|
30
|
+
|
|
31
|
+
const events = [];
|
|
32
|
+
const recordEvent = (type, payload) => {
|
|
33
|
+
events.push({ type, payload, at: new Date().toISOString() });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
for (const ev of ['onStart', 'onProgress', 'onWarning', 'onSuggestion', 'onResult', 'onFinish', 'onNotification']) {
|
|
37
|
+
bus.on(ev, (payload) => recordEvent(ev, payload));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
lifecycle.enter('init');
|
|
41
|
+
bus.emit('onStart', { commandId, config });
|
|
42
|
+
|
|
43
|
+
lifecycle.enter('loadConfig');
|
|
44
|
+
lifecycle.enter('environment');
|
|
45
|
+
|
|
46
|
+
if (config.analytics) {
|
|
47
|
+
try {
|
|
48
|
+
recordUsage(commandId, cwd);
|
|
49
|
+
} catch {
|
|
50
|
+
/* ignore analytics failures */
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let contract = createEmptyContract({
|
|
55
|
+
project: { cwd, commandId },
|
|
56
|
+
meta: { ...meta, layout, commandId, version: meta.version || null },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
lifecycle.enter('input');
|
|
60
|
+
lifecycle.enter('analysis');
|
|
61
|
+
lifecycle.enter('processing');
|
|
62
|
+
|
|
63
|
+
const ctx = {
|
|
64
|
+
bus,
|
|
65
|
+
config,
|
|
66
|
+
cwd,
|
|
67
|
+
commandId,
|
|
68
|
+
lifecycle,
|
|
69
|
+
contract,
|
|
70
|
+
emitProgress: (label, percent) => bus.emit('onProgress', { label, percent }),
|
|
71
|
+
notify: (message, tone = 'info') => {
|
|
72
|
+
bus.emit('onNotification', { message, tone });
|
|
73
|
+
contract.notifications.push({ message, tone });
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const result = await run(ctx);
|
|
78
|
+
if (result && typeof result === 'object') {
|
|
79
|
+
contract = {
|
|
80
|
+
...contract,
|
|
81
|
+
...result,
|
|
82
|
+
widgets: result.widgets || contract.widgets,
|
|
83
|
+
suggestions: result.suggestions || contract.suggestions,
|
|
84
|
+
diagnostics: result.diagnostics || contract.diagnostics,
|
|
85
|
+
notifications: result.notifications || contract.notifications,
|
|
86
|
+
project: { ...contract.project, ...(result.project || {}) },
|
|
87
|
+
stats: { ...contract.stats, ...(result.stats || {}) },
|
|
88
|
+
result: result.result || contract.result,
|
|
89
|
+
meta: { ...contract.meta, ...(result.meta || {}) },
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
lifecycle.enter('validation');
|
|
94
|
+
const validation = validateContract(contract);
|
|
95
|
+
if (!validation.ok) {
|
|
96
|
+
contract.diagnostics.push({
|
|
97
|
+
severity: 'warning',
|
|
98
|
+
message: `Contract validation: ${validation.problems.join('; ')}`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
lifecycle.enter('hints');
|
|
103
|
+
if (config.analytics) {
|
|
104
|
+
const tip = suggestFromAnalytics(cwd);
|
|
105
|
+
if (tip) {
|
|
106
|
+
contract.suggestions.push({ text: tip, source: 'analytics' });
|
|
107
|
+
bus.emit('onSuggestion', { text: tip });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
lifecycle.enter('result');
|
|
112
|
+
contract.lifecycle = lifecycle.snapshot();
|
|
113
|
+
bus.emit('onResult', { result: contract.result });
|
|
114
|
+
|
|
115
|
+
lifecycle.enter('summary');
|
|
116
|
+
contract.meta = {
|
|
117
|
+
...contract.meta,
|
|
118
|
+
durationMs: Date.now() - startedAt,
|
|
119
|
+
layout: contract.meta.layout || layout,
|
|
120
|
+
};
|
|
121
|
+
bus.emit('onFinish', { contract, durationMs: contract.meta.durationMs });
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
contract,
|
|
125
|
+
config,
|
|
126
|
+
events,
|
|
127
|
+
bus,
|
|
128
|
+
};
|
|
129
|
+
}
|