thyscript 1.0.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/bin/thy-node.js +332 -0
- package/bin/thy.js +828 -0
- package/bin/thys.js +25 -0
- package/examples/calculator.thy +274 -0
- package/examples/calculator.thy.js +628 -0
- package/examples/hello.thy +34 -0
- package/examples/login.thy +382 -0
- package/examples/login.thy.js +710 -0
- package/package.json +54 -0
- package/scripts/_class.js +18 -0
- package/scripts/_class2.js +11 -0
- package/scripts/_class3.js +21 -0
- package/scripts/_dstruct.js +20 -0
- package/scripts/_dstruct2.js +24 -0
- package/scripts/_scaffold.js +16 -0
- package/scripts/_scaffold2.js +16 -0
- package/scripts/_scaffold3.js +26 -0
- package/scripts/_tpl_compile.js +13 -0
- package/scripts/_tpl_test.js +9 -0
- package/scripts/_typeof.js +26 -0
- package/scripts/_typeof2.js +25 -0
- package/scripts/patch_genmethod.js +44 -0
- package/scripts/smoke_test.js +167 -0
- package/scripts/smoke_test2.js +151 -0
- package/scripts/test_control_panel.js +58 -0
- package/src/compiler/CodeGenerator.js +1449 -0
- package/src/compiler/JITCompiler.js +325 -0
- package/src/database/DatabaseEngine.js +931 -0
- package/src/errors/ErrorDetector.js +305 -0
- package/src/errors/ErrorMessages.js +497 -0
- package/src/index.js +169 -0
- package/src/lexer/Lexer.js +342 -0
- package/src/lexer/TokenTypes.js +1234 -0
- package/src/memory/MemoryManager.js +283 -0
- package/src/parser/AST.js +606 -0
- package/src/parser/Parser.js +1942 -0
- package/src/runtime/ThyRuntime.js +265 -0
- package/src/runtime/stdlib/ArrayLib.js +207 -0
- package/src/runtime/stdlib/FileLib.js +168 -0
- package/src/runtime/stdlib/MathLib.js +411 -0
- package/src/runtime/stdlib/StringLib.js +387 -0
- package/src/server/ControlPanel.js +1188 -0
- package/src/server/HTMLCSSEngine.js +866 -0
- package/src/server/SecurityEngine.js +714 -0
- package/src/server/ServerEngine.js +738 -0
- package/tests/test1_math.thy +82 -0
- package/tests/test2_oop.thy +133 -0
- package/tests/test3_server.thy +143 -0
- package/tests/test4_functions.thy +159 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// ThyScript Authentication & Login System
|
|
3
|
+
// Full-stack Auth with Hashing, JWT Tokens & Sessions
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
const table port = 3001
|
|
7
|
+
const table secretKey = "thyscript_super_secret_auth_key_2024"
|
|
8
|
+
|
|
9
|
+
// In-memory database of registered users
|
|
10
|
+
const table users = []
|
|
11
|
+
const table sessions = {}
|
|
12
|
+
|
|
13
|
+
// User Entity Class
|
|
14
|
+
public class(User):
|
|
15
|
+
const table id = ""
|
|
16
|
+
const table username = ""
|
|
17
|
+
const table email = ""
|
|
18
|
+
const table passwordHash = ""
|
|
19
|
+
const table role = "user"
|
|
20
|
+
const table createdAt = ""
|
|
21
|
+
end()
|
|
22
|
+
|
|
23
|
+
// Initialize Web Server
|
|
24
|
+
server(3001):
|
|
25
|
+
print("🔐 Starting ThyScript Auth Server on port 3001...")
|
|
26
|
+
end()
|
|
27
|
+
|
|
28
|
+
// Web UI Route
|
|
29
|
+
route get "/":
|
|
30
|
+
classHtml(login_page):
|
|
31
|
+
<!DOCTYPE html>
|
|
32
|
+
<html lang="id">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8">
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
36
|
+
<title>ThyScript Secure Auth</title>
|
|
37
|
+
<style>
|
|
38
|
+
* { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
|
|
39
|
+
body {
|
|
40
|
+
background: linear-gradient(135deg, #090919 0%, #151528 50%, #0d1b2a 100%);
|
|
41
|
+
min-height: 100vh;
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
color: #fff;
|
|
46
|
+
padding: 20px;
|
|
47
|
+
}
|
|
48
|
+
.container {
|
|
49
|
+
width: 100%;
|
|
50
|
+
max-width: 420px;
|
|
51
|
+
}
|
|
52
|
+
.brand {
|
|
53
|
+
text-align: center;
|
|
54
|
+
margin-bottom: 24px;
|
|
55
|
+
}
|
|
56
|
+
.brand .logo {
|
|
57
|
+
font-size: 42px;
|
|
58
|
+
display: inline-block;
|
|
59
|
+
background: -webkit-linear-gradient(#6366f1, #a855f7);
|
|
60
|
+
-webkit-background-clip: text;
|
|
61
|
+
-webkit-text-fill-color: transparent;
|
|
62
|
+
font-weight: 900;
|
|
63
|
+
}
|
|
64
|
+
.brand p {
|
|
65
|
+
color: rgba(255, 255, 255, 0.5);
|
|
66
|
+
font-size: 14px;
|
|
67
|
+
margin-top: 4px;
|
|
68
|
+
}
|
|
69
|
+
.card {
|
|
70
|
+
background: rgba(255, 255, 255, 0.04);
|
|
71
|
+
backdrop-filter: blur(20px);
|
|
72
|
+
-webkit-backdrop-filter: blur(20px);
|
|
73
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
74
|
+
border-radius: 24px;
|
|
75
|
+
padding: 32px;
|
|
76
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
|
|
77
|
+
}
|
|
78
|
+
.tabs {
|
|
79
|
+
display: flex;
|
|
80
|
+
background: rgba(0, 0, 0, 0.3);
|
|
81
|
+
padding: 4px;
|
|
82
|
+
border-radius: 14px;
|
|
83
|
+
margin-bottom: 24px;
|
|
84
|
+
}
|
|
85
|
+
.tab {
|
|
86
|
+
flex: 1;
|
|
87
|
+
padding: 10px;
|
|
88
|
+
border: none;
|
|
89
|
+
background: transparent;
|
|
90
|
+
color: rgba(255, 255, 255, 0.6);
|
|
91
|
+
font-size: 14px;
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
border-radius: 10px;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
transition: all 0.2s ease;
|
|
96
|
+
}
|
|
97
|
+
.tab.active {
|
|
98
|
+
background: linear-gradient(135deg, #6366f1, #a855f7);
|
|
99
|
+
color: #fff;
|
|
100
|
+
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.35);
|
|
101
|
+
}
|
|
102
|
+
.form-group {
|
|
103
|
+
margin-bottom: 18px;
|
|
104
|
+
}
|
|
105
|
+
label {
|
|
106
|
+
display: block;
|
|
107
|
+
font-size: 13px;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
margin-bottom: 8px;
|
|
110
|
+
color: rgba(255, 255, 255, 0.8);
|
|
111
|
+
}
|
|
112
|
+
input {
|
|
113
|
+
width: 100%;
|
|
114
|
+
background: rgba(255, 255, 255, 0.06);
|
|
115
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
116
|
+
border-radius: 12px;
|
|
117
|
+
padding: 14px 16px;
|
|
118
|
+
font-size: 15px;
|
|
119
|
+
color: #fff;
|
|
120
|
+
outline: none;
|
|
121
|
+
transition: all 0.2s;
|
|
122
|
+
}
|
|
123
|
+
input:focus {
|
|
124
|
+
border-color: #818cf8;
|
|
125
|
+
background: rgba(255, 255, 255, 0.09);
|
|
126
|
+
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
|
127
|
+
}
|
|
128
|
+
.btn-submit {
|
|
129
|
+
width: 100%;
|
|
130
|
+
padding: 14px;
|
|
131
|
+
border: none;
|
|
132
|
+
border-radius: 12px;
|
|
133
|
+
background: linear-gradient(135deg, #6366f1, #a855f7);
|
|
134
|
+
color: white;
|
|
135
|
+
font-size: 16px;
|
|
136
|
+
font-weight: 600;
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
margin-top: 10px;
|
|
139
|
+
transition: all 0.2s ease;
|
|
140
|
+
}
|
|
141
|
+
.btn-submit:hover { transform: translateY(-2px); filter: brightness(1.1); }
|
|
142
|
+
.btn-submit:active { transform: translateY(0); }
|
|
143
|
+
.alert {
|
|
144
|
+
padding: 12px 16px;
|
|
145
|
+
border-radius: 12px;
|
|
146
|
+
margin-top: 16px;
|
|
147
|
+
font-size: 14px;
|
|
148
|
+
display: none;
|
|
149
|
+
}
|
|
150
|
+
.alert-success { background: rgba(16, 185, 129, 0.2); border: 1px solid rgba(16, 185, 129, 0.4); color: #34d399; }
|
|
151
|
+
.alert-error { background: rgba(239, 68, 68, 0.2); border: 1px solid rgba(239, 68, 68, 0.4); color: #f87171; }
|
|
152
|
+
|
|
153
|
+
/* Dashboard view */
|
|
154
|
+
.dashboard {
|
|
155
|
+
display: none;
|
|
156
|
+
text-align: center;
|
|
157
|
+
}
|
|
158
|
+
.user-icon {
|
|
159
|
+
width: 80px;
|
|
160
|
+
height: 80px;
|
|
161
|
+
border-radius: 50%;
|
|
162
|
+
background: linear-gradient(135deg, #6366f1, #a855f7);
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: center;
|
|
166
|
+
font-size: 32px;
|
|
167
|
+
margin: 0 auto 16px;
|
|
168
|
+
color: white;
|
|
169
|
+
}
|
|
170
|
+
.token-box {
|
|
171
|
+
background: rgba(0, 0, 0, 0.4);
|
|
172
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
173
|
+
border-radius: 12px;
|
|
174
|
+
padding: 14px;
|
|
175
|
+
font-size: 12px;
|
|
176
|
+
font-family: monospace;
|
|
177
|
+
word-break: break-all;
|
|
178
|
+
color: #a78bfa;
|
|
179
|
+
margin: 16px 0;
|
|
180
|
+
text-align: left;
|
|
181
|
+
}
|
|
182
|
+
.btn-logout {
|
|
183
|
+
padding: 10px 24px;
|
|
184
|
+
border-radius: 12px;
|
|
185
|
+
border: 1px solid rgba(239, 68, 68, 0.4);
|
|
186
|
+
background: rgba(239, 68, 68, 0.15);
|
|
187
|
+
color: #fca5a5;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
}
|
|
191
|
+
</style>
|
|
192
|
+
</head>
|
|
193
|
+
<body>
|
|
194
|
+
<div class="container">
|
|
195
|
+
<div class="brand">
|
|
196
|
+
<div class="logo">⚡ ThyScript Auth</div>
|
|
197
|
+
<p>High-Security Authentication System</p>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div class="card" id="authBox">
|
|
201
|
+
<div class="tabs">
|
|
202
|
+
<button class="tab active" onclick="switchTab('login')">Masuk</button>
|
|
203
|
+
<button class="tab" onclick="switchTab('register')">Daftar</button>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<div id="loginForm">
|
|
207
|
+
<div class="form-group">
|
|
208
|
+
<label>Username</label>
|
|
209
|
+
<input type="text" id="logUser" placeholder="Masukkan username">
|
|
210
|
+
</div>
|
|
211
|
+
<div class="form-group">
|
|
212
|
+
<label>Password</label>
|
|
213
|
+
<input type="password" id="logPass" placeholder="Masukkan password">
|
|
214
|
+
</div>
|
|
215
|
+
<button class="btn-submit" onclick="doLogin()">🔑 Masuk Sekarang</button>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div id="registerForm" style="display:none">
|
|
219
|
+
<div class="form-group">
|
|
220
|
+
<label>Username</label>
|
|
221
|
+
<input type="text" id="regUser" placeholder="Pilih username unik">
|
|
222
|
+
</div>
|
|
223
|
+
<div class="form-group">
|
|
224
|
+
<label>Email</label>
|
|
225
|
+
<input type="email" id="regEmail" placeholder="nama@domain.com">
|
|
226
|
+
</div>
|
|
227
|
+
<div class="form-group">
|
|
228
|
+
<label>Password</label>
|
|
229
|
+
<input type="password" id="regPass" placeholder="Minimal 6 karakter">
|
|
230
|
+
</div>
|
|
231
|
+
<button class="btn-submit" onclick="doRegister()">✨ Buat Akun Baru</button>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div id="alertBox" class="alert"></div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div class="card dashboard" id="dashBox">
|
|
238
|
+
<div class="user-icon" id="userInitial">U</div>
|
|
239
|
+
<h2 id="userNameText">Selamat Datang!</h2>
|
|
240
|
+
<p id="userEmailText" style="color: rgba(255,255,255,0.6); margin-top: 4px;"></p>
|
|
241
|
+
<div class="token-box">
|
|
242
|
+
<div style="color: rgba(255,255,255,0.4); margin-bottom: 4px;">ACTIVE AUTH TOKEN (JWT):</div>
|
|
243
|
+
<span id="tokenText"></span>
|
|
244
|
+
</div>
|
|
245
|
+
<button class="btn-logout" onclick="doLogout()">🚪 Keluar</button>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<script>
|
|
250
|
+
function switchTab(t) {
|
|
251
|
+
document.querySelectorAll('.tab').forEach((el, idx) => {
|
|
252
|
+
el.classList.toggle('active', (t === 'login' && idx === 0) || (t === 'register' && idx === 1));
|
|
253
|
+
});
|
|
254
|
+
document.getElementById('loginForm').style.display = t === 'login' ? 'block' : 'none';
|
|
255
|
+
document.getElementById('registerForm').style.display = t === 'register' ? 'block' : 'none';
|
|
256
|
+
hideAlert();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function showAlert(msg, isError) {
|
|
260
|
+
const b = document.getElementById('alertBox');
|
|
261
|
+
b.className = 'alert ' + (isError ? 'alert-error' : 'alert-success');
|
|
262
|
+
b.innerText = msg;
|
|
263
|
+
b.style.display = 'block';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function hideAlert() {
|
|
267
|
+
document.getElementById('alertBox').style.display = 'none';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function doRegister() {
|
|
271
|
+
const username = document.getElementById('regUser').value.trim();
|
|
272
|
+
const email = document.getElementById('regEmail').value.trim();
|
|
273
|
+
const password = document.getElementById('regPass').value;
|
|
274
|
+
|
|
275
|
+
if (!username || !email || !password) return showAlert('Semua bidang harus diisi!', true);
|
|
276
|
+
|
|
277
|
+
const res = await fetch('/api/register', {
|
|
278
|
+
method: 'POST',
|
|
279
|
+
headers: { 'Content-Type': 'application/json' },
|
|
280
|
+
body: JSON.stringify({ username, email, password })
|
|
281
|
+
});
|
|
282
|
+
const data = await res.json();
|
|
283
|
+
if (data.success) {
|
|
284
|
+
showAlert('Pendaftaran sukses! Silakan masuk.', false);
|
|
285
|
+
switchTab('login');
|
|
286
|
+
} else {
|
|
287
|
+
showAlert(data.message || 'Registrasi gagal', true);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function doLogin() {
|
|
292
|
+
const username = document.getElementById('logUser').value.trim();
|
|
293
|
+
const password = document.getElementById('logPass').value;
|
|
294
|
+
|
|
295
|
+
if (!username || !password) return showAlert('Masukkan username dan password!', true);
|
|
296
|
+
|
|
297
|
+
const res = await fetch('/api/login', {
|
|
298
|
+
method: 'POST',
|
|
299
|
+
headers: { 'Content-Type': 'application/json' },
|
|
300
|
+
body: JSON.stringify({ username, password })
|
|
301
|
+
});
|
|
302
|
+
const data = await res.json();
|
|
303
|
+
if (data.success) {
|
|
304
|
+
document.getElementById('authBox').style.display = 'none';
|
|
305
|
+
document.getElementById('dashBox').style.display = 'block';
|
|
306
|
+
document.getElementById('userNameText').innerText = 'Halo, ' + data.user.username + '!';
|
|
307
|
+
document.getElementById('userEmailText').innerText = data.user.email;
|
|
308
|
+
document.getElementById('userInitial').innerText = data.user.username[0].toUpperCase();
|
|
309
|
+
document.getElementById('tokenText').innerText = data.token;
|
|
310
|
+
} else {
|
|
311
|
+
showAlert(data.message || 'Username atau password salah', true);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function doLogout() {
|
|
316
|
+
document.getElementById('dashBox').style.display = 'none';
|
|
317
|
+
document.getElementById('authBox').style.display = 'block';
|
|
318
|
+
document.getElementById('logPass').value = '';
|
|
319
|
+
hideAlert();
|
|
320
|
+
}
|
|
321
|
+
</script>
|
|
322
|
+
</body>
|
|
323
|
+
</html>
|
|
324
|
+
end()
|
|
325
|
+
end()
|
|
326
|
+
|
|
327
|
+
// API Registration
|
|
328
|
+
route post "/api/register":
|
|
329
|
+
const table username = request.body.username
|
|
330
|
+
const table email = request.body.email
|
|
331
|
+
const table password = request.body.password
|
|
332
|
+
|
|
333
|
+
if(not username or not email or not password):
|
|
334
|
+
response.json({ success: false, message: "Field tidak boleh kosong" })
|
|
335
|
+
end()
|
|
336
|
+
|
|
337
|
+
// Check if user exists
|
|
338
|
+
const table found = users.find(u => u.username == username)
|
|
339
|
+
if(found):
|
|
340
|
+
response.json({ success: false, message: "Username sudah terdaftar" })
|
|
341
|
+
else:
|
|
342
|
+
// Hash password with built-in security
|
|
343
|
+
const table hash = security.hashPassword ? await security.hashPassword(password) : security.hash(password)
|
|
344
|
+
const table newUser = {
|
|
345
|
+
id: String(Date.now()),
|
|
346
|
+
username: username,
|
|
347
|
+
email: email,
|
|
348
|
+
passwordHash: hash,
|
|
349
|
+
role: "user",
|
|
350
|
+
createdAt: new Date().toISOString()
|
|
351
|
+
}
|
|
352
|
+
add newUser in users
|
|
353
|
+
print("👤 New user registered: " + username)
|
|
354
|
+
response.json({ success: true, message: "User registered successfully" })
|
|
355
|
+
end()
|
|
356
|
+
end()
|
|
357
|
+
|
|
358
|
+
// API Login
|
|
359
|
+
route post "/api/login":
|
|
360
|
+
const table username = request.body.username
|
|
361
|
+
const table password = request.body.password
|
|
362
|
+
|
|
363
|
+
const table user = users.find(u => u.username == username)
|
|
364
|
+
if(not user):
|
|
365
|
+
response.json({ success: false, message: "User tidak ditemukan" })
|
|
366
|
+
else:
|
|
367
|
+
const table isValid = security.verifyPassword ? await security.verifyPassword(password, user.passwordHash) : (security.hash(password) == user.passwordHash)
|
|
368
|
+
if(not isValid):
|
|
369
|
+
response.json({ success: false, message: "Password salah" })
|
|
370
|
+
else:
|
|
371
|
+
const table token = security.generateToken ? security.generateToken({ username: user.username, email: user.email }, secretKey) : security.hash(user.username + secretKey)
|
|
372
|
+
print("🔑 User logged in: " + username)
|
|
373
|
+
response.json({
|
|
374
|
+
success: true,
|
|
375
|
+
token: token,
|
|
376
|
+
user: { username: user.username, email: user.email, role: user.role }
|
|
377
|
+
})
|
|
378
|
+
end()
|
|
379
|
+
end()
|
|
380
|
+
end()
|
|
381
|
+
|
|
382
|
+
print("🚀 ThyScript Login Server ready on port " + port)
|