juxscript 1.0.64 → 1.0.65
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/bin/cli.js +17 -2
- package/create/all.jux +343 -0
- package/create/index.jux +90 -0
- package/create/layout.jux +57 -0
- package/create/style.css +1952 -0
- package/create/themes/assets/jux.svg +34 -0
- package/create/themes/base.css +60 -0
- package/create/themes/base2.css +54 -0
- package/create/themes/layouts/base.jux +16 -0
- package/create/themes/layouts/base_blog.jux +0 -0
- package/create/themes/layouts/base_docs.jux +0 -0
- package/create/themes/layouts/base_login.jux +0 -0
- package/create/themes/layouts/base_marketing.jux +0 -0
- package/create/themes/layouts/base_saas.jux +0 -0
- package/package.json +4 -11
- package/tests/dropdown-test.js +25 -0
- package/tests/juxerrors/bad_syntax.jux +8 -0
- package/tests/juxerrors/ghost_dep.jux +10 -0
- package/tests/server_plugin_test.ts +191 -0
- package/docs/DEPRECATIONS.md +0 -244
- package/docs/deprecation-code.png +0 -0
- package/docs/deprecation-console.png +0 -0
- package/docs/grid.png +0 -0
- package/docs/v2comps/HEADLESS.md +0 -83
- package/docs/v2comps/ISOMORPHISM.md +0 -10
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="textGrad" x1="100" y1="0" x2="412" y2="0" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0%" stop-color="#22D3EE"/>
|
|
5
|
+
<stop offset="50%" stop-color="#A855F7"/>
|
|
6
|
+
<stop offset="100%" stop-color="#F472B6"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
|
|
10
|
+
<!-- Background -->
|
|
11
|
+
<rect width="512" height="512" rx="64" fill="#0F172A"/>
|
|
12
|
+
|
|
13
|
+
<!-- Left Curly Brace { -->
|
|
14
|
+
<path d="M80 130
|
|
15
|
+
Q80 200, 55 230
|
|
16
|
+
Q30 256, 55 282
|
|
17
|
+
Q80 312, 80 382"
|
|
18
|
+
stroke="#94A3B8" stroke-width="14" stroke-linecap="round" fill="none"/>
|
|
19
|
+
|
|
20
|
+
<!-- Right Curly Brace } -->
|
|
21
|
+
<path d="M432 130
|
|
22
|
+
Q432 200, 457 230
|
|
23
|
+
Q482 256, 457 282
|
|
24
|
+
Q432 312, 432 382"
|
|
25
|
+
stroke="#94A3B8" stroke-width="14" stroke-linecap="round" fill="none"/>
|
|
26
|
+
|
|
27
|
+
<!-- JUX Text -->
|
|
28
|
+
<text x="256" y="285"
|
|
29
|
+
font-family="system-ui, -apple-system, sans-serif"
|
|
30
|
+
font-size="130"
|
|
31
|
+
font-weight="900"
|
|
32
|
+
text-anchor="middle"
|
|
33
|
+
fill="url(#textGrad)">JUX</text>
|
|
34
|
+
</svg>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
/* Typography */
|
|
7
|
+
--font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif;
|
|
8
|
+
--font-size-sm: 0.875rem;
|
|
9
|
+
--font-size-base: 1rem;
|
|
10
|
+
--font-size-lg: 1.125rem;
|
|
11
|
+
--line-height-normal: 1.6;
|
|
12
|
+
|
|
13
|
+
/* Spacing Scale */
|
|
14
|
+
--space-xs: 0.25rem;
|
|
15
|
+
--space-sm: 0.5rem;
|
|
16
|
+
--space-md: 1rem;
|
|
17
|
+
--space-lg: 1.5rem;
|
|
18
|
+
--space-xl: 2rem;
|
|
19
|
+
|
|
20
|
+
/* Layout */
|
|
21
|
+
--radius-sm: 6px;
|
|
22
|
+
--radius-md: 10px;
|
|
23
|
+
--border-width: 1px;
|
|
24
|
+
|
|
25
|
+
/* Animation */
|
|
26
|
+
--transition-fast: 150ms ease;
|
|
27
|
+
--transition-normal: 300ms ease;
|
|
28
|
+
|
|
29
|
+
/* Color Palette - RxTrail Brand Colors */
|
|
30
|
+
--color-brand: #e57373;
|
|
31
|
+
--color-brand-hover: #ef5350;
|
|
32
|
+
--color-brand-subtle: rgba(229, 115, 115, 0.08);
|
|
33
|
+
|
|
34
|
+
--color-text-primary: #2c2f3e;
|
|
35
|
+
--color-text-secondary: #6b7280;
|
|
36
|
+
--color-text-inverse: #ffffff;
|
|
37
|
+
|
|
38
|
+
--color-background: #fafbfc;
|
|
39
|
+
--color-surface-base: #ffffff;
|
|
40
|
+
--color-surface-hover: #fef5f5;
|
|
41
|
+
--color-surface-active: #fce8e8;
|
|
42
|
+
|
|
43
|
+
--color-border: #e5e7eb;
|
|
44
|
+
--color-border-hover: #d1d5db;
|
|
45
|
+
|
|
46
|
+
/* Accent colors for clinical data */
|
|
47
|
+
--color-success: #00a878;
|
|
48
|
+
--color-warning: #f39c12;
|
|
49
|
+
--color-danger: #e74c3c;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Dark Mode Overrides */
|
|
53
|
+
[data-theme="dark"] {
|
|
54
|
+
--color-text-primary: #e8edf3;
|
|
55
|
+
--color-text-secondary: #9aa6b5;
|
|
56
|
+
--color-background: #0f1419;
|
|
57
|
+
--color-surface-base: #1a2332;
|
|
58
|
+
--color-surface-hover: #243447;
|
|
59
|
+
--color-border: #384456;
|
|
60
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
/*gemini*/
|
|
3
|
+
:root {
|
|
4
|
+
/* Typography - Larger, more comfortable */
|
|
5
|
+
--font-family-base: 'Google Sans', system-ui, -apple-system, sans-serif;
|
|
6
|
+
--font-size-sm: 0.9375rem; /* 15px */
|
|
7
|
+
--font-size-base: 1.0625rem; /* 17px */
|
|
8
|
+
--font-size-lg: 1.25rem; /* 20px */
|
|
9
|
+
--line-height-normal: 1.6;
|
|
10
|
+
|
|
11
|
+
/* Spacing Scale - More generous */
|
|
12
|
+
--space-xs: 0.5rem; /* 8px */
|
|
13
|
+
--space-sm: 0.75rem; /* 12px */
|
|
14
|
+
--space-md: 1.25rem; /* 20px */
|
|
15
|
+
--space-lg: 2rem; /* 32px */
|
|
16
|
+
--space-xl: 3rem; /* 48px */
|
|
17
|
+
|
|
18
|
+
/* Layout - Softer, rounder */
|
|
19
|
+
--radius-sm: 8px;
|
|
20
|
+
--radius-md: 16px;
|
|
21
|
+
--radius-lg: 24px;
|
|
22
|
+
--border-width: 1px;
|
|
23
|
+
|
|
24
|
+
/* Animation - Smoother */
|
|
25
|
+
--transition-fast: 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
26
|
+
--transition-normal: 350ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
27
|
+
|
|
28
|
+
/* Color Palette (Semantic) - Gemini-inspired */
|
|
29
|
+
--color-brand: #1a73e8;
|
|
30
|
+
--color-brand-hover: #1557b0;
|
|
31
|
+
--color-brand-subtle: rgba(26, 115, 232, 0.08);
|
|
32
|
+
|
|
33
|
+
--color-text-primary: #202124;
|
|
34
|
+
--color-text-secondary: #5f6368;
|
|
35
|
+
--color-text-inverse: #ffffff;
|
|
36
|
+
|
|
37
|
+
--color-background: #ffffff;
|
|
38
|
+
--color-surface-base: #f8f9fa;
|
|
39
|
+
--color-surface-hover: #f1f3f4;
|
|
40
|
+
--color-surface-active: #e8eaed;
|
|
41
|
+
|
|
42
|
+
--color-border: #dadce0;
|
|
43
|
+
--color-border-hover: #bdc1c6;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Dark Mode Overrides */
|
|
47
|
+
[data-theme="dark"] {
|
|
48
|
+
--color-text-primary: #e8eaed;
|
|
49
|
+
--color-text-secondary: #9aa0a6;
|
|
50
|
+
--color-background: #202124;
|
|
51
|
+
--color-surface-base: #292a2d;
|
|
52
|
+
--color-surface-hover: #35363a;
|
|
53
|
+
--color-border: #5f6368;
|
|
54
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { juxV2 } from '../../lib/componentsv2';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
juxV2.Grid('base-layout')
|
|
5
|
+
.rows([
|
|
6
|
+
{ size: '60px', class: 'header-row' }, // Header
|
|
7
|
+
{ size: '1fr', class: 'content-row' }, // Content
|
|
8
|
+
{ size: '40px', class: 'footer-row' } // Footer
|
|
9
|
+
])
|
|
10
|
+
.columns([
|
|
11
|
+
{ size: '200px', class: 'sidebar-col' }, // Sidebar
|
|
12
|
+
{ size: '1fr', class: 'main-col' } // Main Content
|
|
13
|
+
])
|
|
14
|
+
.gap('12px')
|
|
15
|
+
.gridder(true) // Enable gridder to visualize the layout
|
|
16
|
+
.render('app');
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.65",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "lib/jux.js",
|
|
@@ -16,21 +16,14 @@
|
|
|
16
16
|
"import": "./lib/jux.js",
|
|
17
17
|
"default": "./lib/jux.js"
|
|
18
18
|
},
|
|
19
|
-
"./reactivity": {
|
|
20
|
-
"types": "./lib/reactivity/index.d.ts",
|
|
21
|
-
"import": "./lib/reactivity/index.js",
|
|
22
|
-
"default": "./lib/reactivity/index.js"
|
|
23
|
-
},
|
|
24
|
-
"./components/*": "./lib/components/*/index.js",
|
|
25
|
-
"./presets/*": "./presets/*.*",
|
|
26
19
|
"./package.json": "./package.json"
|
|
27
20
|
},
|
|
28
21
|
"files": [
|
|
29
|
-
"lib",
|
|
30
22
|
"bin",
|
|
31
|
-
"
|
|
23
|
+
"create",
|
|
24
|
+
"lib",
|
|
32
25
|
"machinery",
|
|
33
|
-
"
|
|
26
|
+
"tests",
|
|
34
27
|
"types",
|
|
35
28
|
"juxconfig.example.js",
|
|
36
29
|
"README.md",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { dropdown } from '../lib/components/dropdown.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const dd = dropdown('test-dropdown', {
|
|
5
|
+
items: [
|
|
6
|
+
{ value: 'option1', label: 'Option 1' },
|
|
7
|
+
{ value: 'option2', label: 'Option 2' },
|
|
8
|
+
{ value: 'option3', label: 'Option 3' }
|
|
9
|
+
],
|
|
10
|
+
placeholder: 'Select an option',
|
|
11
|
+
searchable: true,
|
|
12
|
+
multiSelect: false
|
|
13
|
+
});
|
|
14
|
+
//dd.open();
|
|
15
|
+
|
|
16
|
+
console.log('Current Props', dd.props)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
dd.select('option3');
|
|
21
|
+
dd.open();
|
|
22
|
+
|
|
23
|
+
console.log('Dropdown opened:', dd.props);
|
|
24
|
+
//dd.selectItem('option2');
|
|
25
|
+
//console.log('Selected item:', dd.getSelectedItems());
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jux } from 'juxscript';
|
|
2
|
+
|
|
3
|
+
// ❌ This dependency does not exist in node_modules
|
|
4
|
+
// The compiler will warn, but might proceed.
|
|
5
|
+
// The Verifier should CATCH this because 'phantom-lib' won't be in the import map.
|
|
6
|
+
import { magic } from 'phantom-lib';
|
|
7
|
+
|
|
8
|
+
jux.hero('dep-test', { title: 'Ghost Dependency' }).render('#app');
|
|
9
|
+
|
|
10
|
+
console.log(magic);
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { ListEngine, ListItem } from '../lib/componentsv2/list/engine.js';
|
|
2
|
+
import { ServerSQLitePlugin } from '../lib/componentsv2/plugins/ServerSQLitePlugin.js';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const DB_PATH = path.resolve('temp/jux_test.db');
|
|
8
|
+
const PRESERVE_DB = process.argv.includes('--preserve-db');
|
|
9
|
+
|
|
10
|
+
// --- Integrity System ---
|
|
11
|
+
const SNAPSHOTS: Record<string, any[]> = {};
|
|
12
|
+
const EXPECTATIONS: Record<string, any[]> = {};
|
|
13
|
+
|
|
14
|
+
function capture(phase: string, items: ListItem[]) {
|
|
15
|
+
// Deep copy and simplify to core data shape
|
|
16
|
+
SNAPSHOTS[phase] = items.map(i => ({
|
|
17
|
+
id: i.id,
|
|
18
|
+
text: i.text,
|
|
19
|
+
classes: i.classes,
|
|
20
|
+
details: i.details
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function expectShape(phase: string, shape: any[]) {
|
|
25
|
+
EXPECTATIONS[phase] = shape;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate a precision checksum for data integrity.
|
|
30
|
+
*/
|
|
31
|
+
function getChecksum(data: any): string {
|
|
32
|
+
const str = JSON.stringify(data);
|
|
33
|
+
let hash = 5381;
|
|
34
|
+
for (let i = 0; i < str.length; i++) {
|
|
35
|
+
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
36
|
+
}
|
|
37
|
+
return (hash >>> 0).toString(16).toUpperCase().padStart(8, '0');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runTest() {
|
|
41
|
+
console.log('--- STARTING RIGOROUS SERVER-SIDE PLUGIN TEST ---');
|
|
42
|
+
if (PRESERVE_DB) console.log('ℹ️ MODE: Preserving Database File');
|
|
43
|
+
|
|
44
|
+
// Ensure temp directory exists
|
|
45
|
+
const tempDir = path.dirname(DB_PATH);
|
|
46
|
+
if (!fs.existsSync(tempDir)) {
|
|
47
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Cleanup previous runs
|
|
51
|
+
if (fs.existsSync(DB_PATH)) fs.unlinkSync(DB_PATH);
|
|
52
|
+
|
|
53
|
+
// 1. Initialize DB
|
|
54
|
+
const db = new Database(DB_PATH);
|
|
55
|
+
|
|
56
|
+
// 🔍 PROOF OF LIFE
|
|
57
|
+
const sqliteVersion = db.prepare('select sqlite_version()').pluck().get();
|
|
58
|
+
const TEST_TIMESTAMP = new Date().toISOString();
|
|
59
|
+
const EPHEMERAL_TAG = ` [v${sqliteVersion}::${TEST_TIMESTAMP}]`;
|
|
60
|
+
console.log(`🔌 CONNECTION ACTIVE: SQLite ${sqliteVersion} | Run ID: ${TEST_TIMESTAMP}`);
|
|
61
|
+
|
|
62
|
+
const engine = new ListEngine('server-list-01');
|
|
63
|
+
|
|
64
|
+
// --- DATA CONTRACTS ---
|
|
65
|
+
const SHAPE_1_EMPTY: any[] = [];
|
|
66
|
+
|
|
67
|
+
const SHAPE_2_HYDRATED = [
|
|
68
|
+
{ id: '1', text: `Alice${EPHEMERAL_TAG}`, classes: ['admin'], details: { active: true } },
|
|
69
|
+
{ id: '2', text: `Bob${EPHEMERAL_TAG}`, classes: ['user'], details: { active: true } },
|
|
70
|
+
{ id: '3', text: `Charlie${EPHEMERAL_TAG}`, classes: ['guest'], details: { active: false } }
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const SHAPE_3_MODIFIED = [
|
|
74
|
+
{ id: '1', text: `Alice${EPHEMERAL_TAG}`, classes: ['superadmin'], details: { active: true } },
|
|
75
|
+
{ id: '2', text: `Bob${EPHEMERAL_TAG}`, classes: ['user'], details: { active: true } },
|
|
76
|
+
{ id: '4', text: `Dave${EPHEMERAL_TAG}`, classes: ['user'], details: {} }
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
// Register Contracts
|
|
80
|
+
expectShape('PHASE 1: Empty', SHAPE_1_EMPTY);
|
|
81
|
+
expectShape('PHASE 2: Hydrated', SHAPE_2_HYDRATED);
|
|
82
|
+
expectShape('PHASE 3: Modified', SHAPE_3_MODIFIED);
|
|
83
|
+
|
|
84
|
+
// === EXECUTION ===
|
|
85
|
+
|
|
86
|
+
// Phase 1
|
|
87
|
+
console.log('\n▶ PHASE 1: Initialization');
|
|
88
|
+
capture('PHASE 1: Empty', engine.state.items);
|
|
89
|
+
|
|
90
|
+
// Phase 2
|
|
91
|
+
console.log('\n▶ PHASE 2: Schema & Hydration');
|
|
92
|
+
db.exec(`CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, role TEXT DEFAULT 'user', meta TEXT);`);
|
|
93
|
+
const insert = db.prepare('INSERT INTO users (username, role, meta) VALUES (?, ?, ?)');
|
|
94
|
+
insert.run('Alice', 'admin', JSON.stringify({ active: true }));
|
|
95
|
+
insert.run('Bob', 'user', JSON.stringify({ active: true }));
|
|
96
|
+
insert.run('Charlie', 'guest', JSON.stringify({ active: false }));
|
|
97
|
+
|
|
98
|
+
// Inject Decoupled Plugin
|
|
99
|
+
const plugin = ServerSQLitePlugin({
|
|
100
|
+
database: db,
|
|
101
|
+
query: `SELECT id, username || '${EPHEMERAL_TAG}' as username, role, meta FROM users`,
|
|
102
|
+
bindTo: 'items', // ✅ Bind to ListEngine's 'items' property
|
|
103
|
+
mapRow: (row: any) => ({
|
|
104
|
+
id: String(row.id),
|
|
105
|
+
text: row.username,
|
|
106
|
+
classes: row.role ? [row.role] : [],
|
|
107
|
+
details: row.meta ? JSON.parse(row.meta) : {}
|
|
108
|
+
}),
|
|
109
|
+
autoLoad: true
|
|
110
|
+
});
|
|
111
|
+
engine.addPlugin(plugin);
|
|
112
|
+
|
|
113
|
+
capture('PHASE 2: Hydrated', engine.state.items);
|
|
114
|
+
|
|
115
|
+
// Phase 3
|
|
116
|
+
console.log('\n▶ PHASE 3: External Access');
|
|
117
|
+
db.prepare("UPDATE users SET role = 'superadmin' WHERE username = 'Alice'").run();
|
|
118
|
+
db.prepare("DELETE FROM users WHERE username = 'Charlie'").run();
|
|
119
|
+
db.prepare("INSERT INTO users (username, role, meta) VALUES (?, ?, ?)").run('Dave', 'user', '{}');
|
|
120
|
+
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
engine['emit']('db:reload', {});
|
|
123
|
+
|
|
124
|
+
capture('PHASE 3: Modified', engine.state.items);
|
|
125
|
+
|
|
126
|
+
// === VERIFICATION REPORT ===
|
|
127
|
+
console.log('\n=============================================================');
|
|
128
|
+
console.log(' 🛡️ INTEGRITY CHECKSUM REPORT ');
|
|
129
|
+
console.log('=============================================================');
|
|
130
|
+
console.log(` CONNECTION ACTIVE: SQLite ${sqliteVersion} | Run ID: ${TEST_TIMESTAMP}`);
|
|
131
|
+
console.log('-------------------------------------------------------------');
|
|
132
|
+
console.log(' PHASES | CHECKSUM | EXPECTED | STATUS ');
|
|
133
|
+
console.log('-------------------------|----------|----------|-------------');
|
|
134
|
+
|
|
135
|
+
let allPassed = true;
|
|
136
|
+
Object.keys(EXPECTATIONS).forEach(phase => {
|
|
137
|
+
const actual = SNAPSHOTS[phase] || [];
|
|
138
|
+
const expected = EXPECTATIONS[phase];
|
|
139
|
+
|
|
140
|
+
const actHash = getChecksum(actual);
|
|
141
|
+
const expHash = getChecksum(expected);
|
|
142
|
+
const match = actHash === expHash;
|
|
143
|
+
|
|
144
|
+
if (!match) allPassed = false;
|
|
145
|
+
|
|
146
|
+
const status = match ? '✅ PASS' : '❌ FAIL';
|
|
147
|
+
console.log(` ${phase.padEnd(23)} | ${actHash} | ${expHash} | ${status}`);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
console.log('-------------------------------------------------------------');
|
|
151
|
+
|
|
152
|
+
// === HISTORY DUMP ===
|
|
153
|
+
console.log('\n==================================================');
|
|
154
|
+
console.log(' 📜 STATE LEDGER ');
|
|
155
|
+
console.log('==================================================');
|
|
156
|
+
engine.stateHistory.forEach((snap, i) => {
|
|
157
|
+
const s = JSON.parse(snap);
|
|
158
|
+
console.log(`${i.toString().padStart(2, '0')} | Items: ${s.items.length} | Loading: ${String(s.loading).padEnd(5)} | Msg: ${s.noItemsMessage || 'None'}`);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log('\n==================================================');
|
|
162
|
+
console.log(' 📡 EVENT TOPOLOGY ');
|
|
163
|
+
console.log('==================================================');
|
|
164
|
+
engine.emitHistory.forEach((entryStr, i) => {
|
|
165
|
+
try {
|
|
166
|
+
const entry = JSON.parse(entryStr);
|
|
167
|
+
const time = entry.timestamp.split('T')[1].slice(0, 8);
|
|
168
|
+
const dataSummary = JSON.stringify(entry.data).substring(0, 60);
|
|
169
|
+
console.log(`#${i.toString().padStart(2, '0')} [${time}] ${entry.event.padEnd(15)} -> ${dataSummary}`);
|
|
170
|
+
} catch (e) { }
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
engine.dispose();
|
|
174
|
+
db.close();
|
|
175
|
+
|
|
176
|
+
// Cleanup
|
|
177
|
+
if (PRESERVE_DB) {
|
|
178
|
+
console.log(`\n💾 DATABASE PRESERVED: ${DB_PATH}`);
|
|
179
|
+
} else {
|
|
180
|
+
if (fs.existsSync(DB_PATH)) fs.unlinkSync(DB_PATH);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (allPassed) {
|
|
184
|
+
console.log('\n🌟🌟🌟 RESOUNDING ALL TESTS PASSED 🌟🌟🌟\n');
|
|
185
|
+
} else {
|
|
186
|
+
console.error('\n💥 VERIFICATION FAILED: Checksum Mismatch detected.\n');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
runTest();
|
package/docs/DEPRECATIONS.md
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
# JUX Deprecation System
|
|
2
|
-
|
|
3
|
-
This document explains how deprecation warnings are implemented in JUX components to help users migrate to newer patterns while maintaining backward compatibility.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
JUX uses a **console-based deprecation warning system** that:
|
|
8
|
-
- Shows warnings **once per feature per page load** (no spam)
|
|
9
|
-
- Includes **removal date and version** information
|
|
10
|
-
- Provides **migration guidance** via documentation links
|
|
11
|
-
- Warns on **constructor options, fluent methods, and sync bindings**
|
|
12
|
-
|
|
13
|
-
## Implementation Pattern
|
|
14
|
-
|
|
15
|
-
### 1. Define the Deprecation Message
|
|
16
|
-
|
|
17
|
-
At the top of any component file:
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
// filepath: /path/to/component.ts
|
|
21
|
-
|
|
22
|
-
// Deprecation warning template
|
|
23
|
-
const DEPRECATION_WARNING = (feature: string) =>
|
|
24
|
-
`[JUX Deprecation Warning] ComponentName.${feature} will be removed in v1.0.30+ (January 30, 2026). ` +
|
|
25
|
-
`Please use CSS or the .style() method instead. See: [docs placeholder]`;
|
|
26
|
-
|
|
27
|
-
// Track which warnings have been shown (prevents spam)
|
|
28
|
-
const _shownWarnings = new Set<string>();
|
|
29
|
-
|
|
30
|
-
function warnOnce(feature: string): void {
|
|
31
|
-
if (!_shownWarnings.has(feature)) {
|
|
32
|
-
console.warn(DEPRECATION_WARNING(feature));
|
|
33
|
-
_shownWarnings.add(feature);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 2. Warn in Constructor (Options)
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
export class Container extends BaseComponent<ContainerState> {
|
|
42
|
-
constructor(id: string, options: ContainerOptions = {}) {
|
|
43
|
-
// Check for deprecated options and warn
|
|
44
|
-
if (options.direction !== undefined) warnOnce('direction (option)');
|
|
45
|
-
if (options.gap !== undefined) warnOnce('gap (option)');
|
|
46
|
-
if (options.padding !== undefined) warnOnce('padding (option)');
|
|
47
|
-
|
|
48
|
-
super(id, {
|
|
49
|
-
direction: options.direction ?? 'column',
|
|
50
|
-
gap: options.gap ?? 0,
|
|
51
|
-
padding: options.padding ?? '0'
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 3. Warn in Fluent Methods
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
export class Container extends BaseComponent<ContainerState> {
|
|
61
|
-
|
|
62
|
-
direction(value: 'row' | 'column'): this {
|
|
63
|
-
warnOnce('direction()');
|
|
64
|
-
this.state.direction = value;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
gap(value: number | string): this {
|
|
69
|
-
warnOnce('gap()');
|
|
70
|
-
this.state.gap = value;
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
padding(value: string): this {
|
|
75
|
-
warnOnce('padding()');
|
|
76
|
-
this.state.padding = value;
|
|
77
|
-
return this;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### 4. Warn in Sync Bindings (Optional)
|
|
83
|
-
|
|
84
|
-
If a deprecated property can be synced with state:
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
render(targetId?: string): this {
|
|
88
|
-
// ...existing render code...
|
|
89
|
-
|
|
90
|
-
this._syncBindings.forEach(({ property, stateObj, toComponent }) => {
|
|
91
|
-
const transform = toComponent || ((v: any) => v);
|
|
92
|
-
|
|
93
|
-
if (property === 'direction') {
|
|
94
|
-
warnOnce('direction (sync)');
|
|
95
|
-
stateObj.subscribe((val: any) => {
|
|
96
|
-
this.state.direction = String(transform(val));
|
|
97
|
-
// ...apply changes to DOM...
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
// ...existing code...
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
return this;
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## User Experience
|
|
108
|
-
|
|
109
|
-
When a user writes deprecated code:
|
|
110
|
-
|
|
111
|
-
```javascript
|
|
112
|
-
// This will show a console warning
|
|
113
|
-
jux.container('my-container')
|
|
114
|
-
.direction('row') // ⚠️ Warning: Container.direction() will be removed...
|
|
115
|
-
.gap(20) // ⚠️ Warning: Container.gap() will be removed...
|
|
116
|
-
.render('#app');
|
|
117
|
-
|
|
118
|
-
// Subsequent calls DON'T show warnings (no spam)
|
|
119
|
-
jux.container('another')
|
|
120
|
-
.gap(10) // Silent (already warned once)
|
|
121
|
-
.render('#app');
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**Console output:**
|
|
125
|
-
```
|
|
126
|
-
[JUX Deprecation Warning] Container.direction() will be removed in v1.0.30+
|
|
127
|
-
(January 30, 2026). Please use CSS or the .style() method instead.
|
|
128
|
-
See: [docs placeholder]
|
|
129
|
-
|
|
130
|
-
[JUX Deprecation Warning] Container.gap() will be removed in v1.0.30+
|
|
131
|
-
(January 30, 2026). Please use CSS or the .style() method instead.
|
|
132
|
-
See: [docs placeholder]
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Migration Path
|
|
136
|
-
|
|
137
|
-
Users should migrate from deprecated properties to CSS:
|
|
138
|
-
|
|
139
|
-
```javascript
|
|
140
|
-
// ❌ Old (deprecated)
|
|
141
|
-
jux.container('box')
|
|
142
|
-
.direction('row')
|
|
143
|
-
.gap(20)
|
|
144
|
-
.padding('1rem')
|
|
145
|
-
.render('#app');
|
|
146
|
-
|
|
147
|
-
// ✅ New (recommended)
|
|
148
|
-
jux.container('box')
|
|
149
|
-
.style('display: flex; flex-direction: row; gap: 20px; padding: 1rem;')
|
|
150
|
-
.render('#app');
|
|
151
|
-
|
|
152
|
-
// ✅ Or use a CSS class
|
|
153
|
-
jux.container('box')
|
|
154
|
-
.class('flex-row gap-20 p-4')
|
|
155
|
-
.render('#app');
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Why This Approach?
|
|
159
|
-
|
|
160
|
-
1. **Non-breaking**: Old code continues to work during deprecation period
|
|
161
|
-
2. **Gradual migration**: Developers can update at their own pace
|
|
162
|
-
3. **Clear timeline**: Version and date make planning easy
|
|
163
|
-
4. **No console spam**: Each warning shown only once per page load
|
|
164
|
-
5. **Clear guidance**: Links to migration documentation
|
|
165
|
-
|
|
166
|
-
## Adding New Deprecations
|
|
167
|
-
|
|
168
|
-
To deprecate a feature in any component:
|
|
169
|
-
|
|
170
|
-
### Step 1: Add Warning Infrastructure
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
const DEPRECATION_WARNING = (feature: string) =>
|
|
174
|
-
`[JUX Deprecation Warning] YourComponent.${feature} will be removed in vX.X.X+ (Date). ` +
|
|
175
|
-
`Migration guide: [docs placeholder]`;
|
|
176
|
-
|
|
177
|
-
const _shownWarnings = new Set<string>();
|
|
178
|
-
|
|
179
|
-
function warnOnce(feature: string): void {
|
|
180
|
-
if (!_shownWarnings.has(feature)) {
|
|
181
|
-
console.warn(DEPRECATION_WARNING(feature));
|
|
182
|
-
_shownWarnings.add(feature);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Step 2: Call warnOnce() Everywhere the Feature is Used
|
|
188
|
-
|
|
189
|
-
- In constructor for options
|
|
190
|
-
- In fluent methods
|
|
191
|
-
- In sync binding handlers (if applicable)
|
|
192
|
-
|
|
193
|
-
### Step 3: Update Documentation
|
|
194
|
-
|
|
195
|
-
- Add migration examples to docs
|
|
196
|
-
- Update changelog
|
|
197
|
-
- Set removal date (typically 6-12 months out)
|
|
198
|
-
|
|
199
|
-
### Step 4: Add to Current Deprecations Table Below
|
|
200
|
-
|
|
201
|
-
## Current Deprecations
|
|
202
|
-
|
|
203
|
-
| Component | Feature | Replacement | Removal Version | Removal Date |
|
|
204
|
-
|-----------|---------|-------------|-----------------|--------------|
|
|
205
|
-
| Container | `.direction()` | `.style('flex-direction: ...')` | v1.0.30+ | Jan 30, 2026 |
|
|
206
|
-
| Container | `.gap()` | `.style('gap: ...')` | v1.0.30+ | Jan 30, 2026 |
|
|
207
|
-
| Container | `.wrap()` | `.style('flex-wrap: ...')` | v1.0.30+ | Jan 30, 2026 |
|
|
208
|
-
| Container | `.align()` | `.style('align-items: ...')` | v1.0.30+ | Jan 30, 2026 |
|
|
209
|
-
| Container | `.justify()` | `.style('justify-content: ...')` | v1.0.30+ | Jan 30, 2026 |
|
|
210
|
-
| Container | `.padding()` | `.style('padding: ...')` | v1.0.30+ | Jan 30, 2026 |
|
|
211
|
-
|
|
212
|
-
## Testing Deprecation Warnings
|
|
213
|
-
|
|
214
|
-
You can verify deprecation warnings are working:
|
|
215
|
-
|
|
216
|
-
```javascript
|
|
217
|
-
// Open browser console and run:
|
|
218
|
-
jux.container('test')
|
|
219
|
-
.direction('row') // Should show warning
|
|
220
|
-
.gap(20) // Should show warning
|
|
221
|
-
.render('#app');
|
|
222
|
-
|
|
223
|
-
jux.container('test2')
|
|
224
|
-
.direction('row') // Should NOT show warning (already shown)
|
|
225
|
-
.render('#app');
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Best Practices
|
|
229
|
-
|
|
230
|
-
1. **Give users time**: At least 6 months before removal
|
|
231
|
-
2. **Be specific**: Show exact feature name in warning
|
|
232
|
-
3. **Provide alternatives**: Always suggest what to use instead
|
|
233
|
-
4. **Document migrations**: Create clear before/after examples
|
|
234
|
-
5. **Version appropriately**: Use semver correctly (major bump for removals)
|
|
235
|
-
|
|
236
|
-
## Questions?
|
|
237
|
-
|
|
238
|
-
- Migration guides: [docs placeholder - coming soon]
|
|
239
|
-
- Breaking changes: [docs placeholder - coming soon]
|
|
240
|
-
- Changelog: [GitHub releases placeholder]
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
**Last updated**: January 28, 2026
|