naider 1.10.1 → 1.11.1
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 +1052 -1021
- package/SPEC.naide +17 -0
- package/bin/naide.js +879 -879
- package/lsp/server.js +348 -347
- package/package.json +58 -56
- package/src/generator-bun.js +513 -513
- package/src/generator-python.js +1519 -1449
- package/src/generator.js +77 -0
- package/src/index.js +103 -103
- package/src/parser.js +64 -0
- package/src/tokens.js +4 -0
- package/vscode-naide/syntaxes/naide.tmLanguage.json +1 -1
package/README.md
CHANGED
|
@@ -1,1021 +1,1052 @@
|
|
|
1
|
-
# NAIDE
|
|
2
|
-
|
|
3
|
-
**Node AI Development Environment** — A programming language designed for AI-speed code generation that transpiles to Node.js.
|
|
4
|
-
|
|
5
|
-
NAIDE is built on three principles: one way to write everything (zero ambiguity), keyword-driven intent (the first token decides meaning), and minimal token count (fewer tokens = faster AI generation). It includes built-in declarations for servers, databases, authentication, file uploads, WebSockets, job queues, testing, and more — all with zero external dependencies.
|
|
6
|
-
|
|
7
|
-
Two syntax modes:
|
|
8
|
-
|
|
9
|
-
- **NAIDE** (`.naide`) — Human-readable, ~40% fewer tokens than JavaScript
|
|
10
|
-
- **NAIDE-X** (`.nx`) — AI-only readability, ~80% fewer tokens than JavaScript
|
|
11
|
-
|
|
12
|
-
## Install
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install -g naider
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Quick Start
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
# Create a new project
|
|
22
|
-
naide init my-app
|
|
23
|
-
cd my-app
|
|
24
|
-
npm install
|
|
25
|
-
npm run dev
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Or write a file directly:
|
|
29
|
-
|
|
30
|
-
```python
|
|
31
|
-
# app.naide
|
|
32
|
-
server app port 3000:
|
|
33
|
-
get "/":
|
|
34
|
-
ret {message: "Hello from NAIDE"}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
naide app.naide
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## CLI
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
naide <file> # Run a .naide or .nx file
|
|
45
|
-
naide # Start interactive REPL
|
|
46
|
-
naide repl # Start interactive REPL
|
|
47
|
-
naide init [dir] # Scaffold a new project
|
|
48
|
-
naide build [dir] [outdir] # Transpile all files to JavaScript
|
|
49
|
-
naide check <files...> # Type-check without running
|
|
50
|
-
naide fmt <files...> # Format NAIDE files
|
|
51
|
-
naide lsp # Start Language Server (LSP)
|
|
52
|
-
naide vscode # Install VS Code extension
|
|
53
|
-
naide deploy [dir] # Generate Dockerfile for deployment
|
|
54
|
-
naide convert <files...> # Convert .nx ↔ .naide (bidirectional)
|
|
55
|
-
naide pkg init # Create naide.pkg.json manifest
|
|
56
|
-
naide pkg install <name> # Install a NAIDE package
|
|
57
|
-
naide pkg publish # Publish package to npm
|
|
58
|
-
naide -w <file> # Watch mode (auto-restart on changes)
|
|
59
|
-
naide -d <file> # Debug mode (Node.js inspector)
|
|
60
|
-
naide --emit <file> # Print generated JavaScript
|
|
61
|
-
naide -o <out.js> <file> # Write JavaScript to file
|
|
62
|
-
naide --mid <file.nx> # Show intermediate NAIDE v1 (debug X mode)
|
|
63
|
-
naide --ast <file> # Print AST
|
|
64
|
-
naide --tokens <file> # Print token stream
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### REPL
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
$ naide
|
|
71
|
-
NAIDE REPL v1.
|
|
72
|
-
Type .exit to quit, .eval to toggle eval mode
|
|
73
|
-
|
|
74
|
-
>>> str name = "hello"
|
|
75
|
-
const name = "hello";
|
|
76
|
-
|
|
77
|
-
>>> fn add(int a, int b) -> int:
|
|
78
|
-
... ret a + b
|
|
79
|
-
...
|
|
80
|
-
function add(a, b) {
|
|
81
|
-
return a + b;
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Type `.eval` to switch to evaluation mode (runs the code instead of showing JS).
|
|
86
|
-
|
|
87
|
-
### Playground
|
|
88
|
-
|
|
89
|
-
Open `playground/index.html` in a browser (via a local server) for a live in-browser transpiler with examples and token count comparison.
|
|
90
|
-
|
|
91
|
-
### Benchmark
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
node benchmark/compare.js
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
Language Tokens Chars Lines Savings
|
|
99
|
-
JavaScript 330 2590 78 —
|
|
100
|
-
NAIDE 66 517 19 -80%
|
|
101
|
-
NAIDE-X 39 435 19 -88%
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Language Reference
|
|
105
|
-
|
|
106
|
-
### Variables
|
|
107
|
-
|
|
108
|
-
```python
|
|
109
|
-
str name = "hello" # immutable (const)
|
|
110
|
-
int count = 42
|
|
111
|
-
num price = 9.99
|
|
112
|
-
bool active = true
|
|
113
|
-
list items = [1, 2, 3]
|
|
114
|
-
map config = {host: "localhost"}
|
|
115
|
-
any data = null
|
|
116
|
-
|
|
117
|
-
mut int counter = 0 # mutable (let)
|
|
118
|
-
mut str label = "init"
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Types: `str`, `int`, `num`, `bool`, `list`, `map`, `any`, `json`, `void`
|
|
122
|
-
|
|
123
|
-
### String Interpolation
|
|
124
|
-
|
|
125
|
-
```python
|
|
126
|
-
str greeting = "Hello {name}, you have {count} items"
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Double-quoted strings with `{expr}` are auto-interpolated.
|
|
130
|
-
|
|
131
|
-
### Functions
|
|
132
|
-
|
|
133
|
-
```python
|
|
134
|
-
fn add(int a, int b) -> int:
|
|
135
|
-
ret a + b
|
|
136
|
-
|
|
137
|
-
fn greet(str name, str prefix = "Hello"):
|
|
138
|
-
log "{prefix}, {name}!"
|
|
139
|
-
|
|
140
|
-
fn sum(...int nums) -> int:
|
|
141
|
-
mut int total = 0
|
|
142
|
-
each n in nums:
|
|
143
|
-
total += n
|
|
144
|
-
ret total
|
|
145
|
-
|
|
146
|
-
fn.async fetchUser(str id) -> map:
|
|
147
|
-
any res = await fetch("/api/users/{id}")
|
|
148
|
-
ret await res.json()
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Control Flow
|
|
152
|
-
|
|
153
|
-
```python
|
|
154
|
-
if count > 10:
|
|
155
|
-
log "many"
|
|
156
|
-
elif count > 5:
|
|
157
|
-
log "some"
|
|
158
|
-
else:
|
|
159
|
-
log "few"
|
|
160
|
-
|
|
161
|
-
str size = if count > 10 then "big" else "small"
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Loops
|
|
165
|
-
|
|
166
|
-
```python
|
|
167
|
-
each item in items:
|
|
168
|
-
log item
|
|
169
|
-
|
|
170
|
-
each key, val in config:
|
|
171
|
-
log "{key}: {val}"
|
|
172
|
-
|
|
173
|
-
for i in 0..10:
|
|
174
|
-
log i
|
|
175
|
-
|
|
176
|
-
while active:
|
|
177
|
-
log "running"
|
|
178
|
-
active = false
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Pattern Matching
|
|
182
|
-
|
|
183
|
-
```python
|
|
184
|
-
match status:
|
|
185
|
-
"ok": log "success"
|
|
186
|
-
"error": log "failed"
|
|
187
|
-
_: log "unknown"
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Error Handling
|
|
191
|
-
|
|
192
|
-
```python
|
|
193
|
-
try:
|
|
194
|
-
data = await fetchData(url)
|
|
195
|
-
fail e:
|
|
196
|
-
log.error e.message
|
|
197
|
-
ensure:
|
|
198
|
-
log "request completed"
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
`ensure:` is NAIDE's `finally` — the block always runs, whether the `try` succeeds or fails. `fail` is optional:
|
|
202
|
-
|
|
203
|
-
```python
|
|
204
|
-
try:
|
|
205
|
-
conn = await openConnection()
|
|
206
|
-
ret await conn.query("SELECT 1")
|
|
207
|
-
ensure:
|
|
208
|
-
conn.close()
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Type Checking
|
|
212
|
-
|
|
213
|
-
```python
|
|
214
|
-
if typeof data == "string":
|
|
215
|
-
log "is string"
|
|
216
|
-
|
|
217
|
-
if err instanceof TypeError:
|
|
218
|
-
log "type error"
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
`typeof` returns the type as a string. `instanceof` checks if a value is an instance of a class/constructor.
|
|
222
|
-
|
|
223
|
-
### Classes
|
|
224
|
-
|
|
225
|
-
```python
|
|
226
|
-
model User:
|
|
227
|
-
str name
|
|
228
|
-
str email
|
|
229
|
-
int age = 0
|
|
230
|
-
|
|
231
|
-
fn greet() -> str:
|
|
232
|
-
ret "Hi, I'm {self.name}"
|
|
233
|
-
|
|
234
|
-
model Admin extends User:
|
|
235
|
-
str role = "admin"
|
|
236
|
-
fn permissions() -> list:
|
|
237
|
-
ret ["read", "write", "delete"]
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Pipe Operator
|
|
241
|
-
|
|
242
|
-
```python
|
|
243
|
-
list result = data
|
|
244
|
-
|> filter((x) => x.active)
|
|
245
|
-
|> map((x) => x.name)
|
|
246
|
-
|> sort()
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Imports / Exports
|
|
250
|
-
|
|
251
|
-
```python
|
|
252
|
-
use express
|
|
253
|
-
use {readFile, writeFile} from "fs/promises"
|
|
254
|
-
use axios from "axios"
|
|
255
|
-
|
|
256
|
-
pub fn helper() -> str:
|
|
257
|
-
ret "exported"
|
|
258
|
-
pub str VERSION = "1.0.0"
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Server & API Features
|
|
262
|
-
|
|
263
|
-
All features below are zero-dependency — the runtime is bundled with the package.
|
|
264
|
-
|
|
265
|
-
### server — Express App
|
|
266
|
-
|
|
267
|
-
```python
|
|
268
|
-
server app port 3000:
|
|
269
|
-
get "/":
|
|
270
|
-
ret {message: "hello"}
|
|
271
|
-
post "/api/data" (req, res):
|
|
272
|
-
ret req.body
|
|
273
|
-
put "/api/data/:id" (req, res):
|
|
274
|
-
ret {updated: true}
|
|
275
|
-
del "/api/data/:id":
|
|
276
|
-
ret {deleted: true}
|
|
277
|
-
patch "/api/data/:id" (req, res):
|
|
278
|
-
ret {patched: true}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### schema — Data Models
|
|
282
|
-
|
|
283
|
-
```python
|
|
284
|
-
schema User:
|
|
285
|
-
id auto
|
|
286
|
-
name str required min(2) max(50)
|
|
287
|
-
email str required email unique
|
|
288
|
-
age int optional min(0) max(150)
|
|
289
|
-
role enum("admin", "user") default("user")
|
|
290
|
-
joined timestamp auto
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
Types: `str`, `int`, `num`, `bool`, `auto` (UUID), `timestamp`, `enum(...)`
|
|
294
|
-
Modifiers: `required`, `optional`, `min(n)`, `max(n)`, `email`, `url`, `unique`, `auto`, `default(val)`
|
|
295
|
-
|
|
296
|
-
### db — Persistent Storage
|
|
297
|
-
|
|
298
|
-
```python
|
|
299
|
-
db "data/"
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Schemas auto-persist to JSON files (`data/User.json`, etc.). Without `db`, data is in-memory only.
|
|
303
|
-
|
|
304
|
-
### crud — Auto-generated REST Endpoints
|
|
305
|
-
|
|
306
|
-
```python
|
|
307
|
-
server app port 3000:
|
|
308
|
-
crud "/api/users" User
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Generates GET (list + by-ID), POST, PUT, DELETE with validation. Built-in pagination, search, and sort:
|
|
312
|
-
|
|
313
|
-
```
|
|
314
|
-
GET /api/users?page=2&limit=10&q=john&sort=name&order=asc
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Response: `{ data: [...], total, page, limit, pages }`
|
|
318
|
-
|
|
319
|
-
### validate — Request Validation
|
|
320
|
-
|
|
321
|
-
```python
|
|
322
|
-
server app port 3000:
|
|
323
|
-
validate "/api/users" User
|
|
324
|
-
post "/api/users" (req, res):
|
|
325
|
-
user = UserStore.create(req.body) # body is pre-validated
|
|
326
|
-
ret user
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
Auto-validates POST/PUT/PATCH bodies against the schema. Returns `400` with error details if invalid. Validated data replaces `req.body`.
|
|
330
|
-
|
|
331
|
-
### auth — JWT Authentication
|
|
332
|
-
|
|
333
|
-
```python
|
|
334
|
-
server app port 3000:
|
|
335
|
-
auth JWT_SECRET:
|
|
336
|
-
protect "/api/*"
|
|
337
|
-
public "/api/auth/*"
|
|
338
|
-
|
|
339
|
-
post "/api/auth/login" (req, res):
|
|
340
|
-
token = auth.sign({id: user.id})
|
|
341
|
-
ret {token}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### cors / limit / cookie / session
|
|
345
|
-
|
|
346
|
-
```python
|
|
347
|
-
server app port 3000:
|
|
348
|
-
cors "*" # CORS middleware
|
|
349
|
-
limit "/api/*" 100 "1m" # rate limiting (100 req/min)
|
|
350
|
-
cookie # cookie parser → req.cookies
|
|
351
|
-
session "my-secret" # cookie sessions → req.session
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### upload — File Uploads
|
|
355
|
-
|
|
356
|
-
```python
|
|
357
|
-
server app port 3000:
|
|
358
|
-
upload "/api/upload" "avatar" (req, res):
|
|
359
|
-
ret {filename: req.file.filename, size: req.file.size}
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
Zero-dependency multipart parser. `req.file` = `{filename, contentType, data, size}`.
|
|
363
|
-
|
|
364
|
-
### view — Template Rendering
|
|
365
|
-
|
|
366
|
-
```python
|
|
367
|
-
server app port 3000:
|
|
368
|
-
view "./views"
|
|
369
|
-
get "/":
|
|
370
|
-
ret.render "home" {title: "Welcome", items: ["a", "b"]}
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
Reads `.html` files with `{{variable}}`, `{{if key}}...{{/if}}`, `{{each item in list}}...{{/each}}`.
|
|
374
|
-
|
|
375
|
-
### sse — Server-Sent Events
|
|
376
|
-
|
|
377
|
-
```python
|
|
378
|
-
server app port 3000:
|
|
379
|
-
sse "/events"
|
|
380
|
-
post "/api/notify" (req, res):
|
|
381
|
-
sse.broadcast req.body
|
|
382
|
-
ret {ok: true}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
`sse.send(data)`, `sse.broadcast(data)`, `sse.count`.
|
|
386
|
-
|
|
387
|
-
### cache — Response Caching
|
|
388
|
-
|
|
389
|
-
```python
|
|
390
|
-
server app port 3000:
|
|
391
|
-
cache "/api/*" "5m"
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
Caches GET responses in memory with TTL. Sets `X-Cache: HIT/MISS`.
|
|
395
|
-
|
|
396
|
-
### ws — WebSocket
|
|
397
|
-
|
|
398
|
-
```python
|
|
399
|
-
server app port 3000:
|
|
400
|
-
ws "/chat":
|
|
401
|
-
on "connect":
|
|
402
|
-
send({type: "welcome"})
|
|
403
|
-
on "message" (data):
|
|
404
|
-
broadcast(data)
|
|
405
|
-
on "close":
|
|
406
|
-
log "client left"
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
Built-in `send(data)` and `broadcast(data)`. Requires `npm install ws`.
|
|
410
|
-
|
|
411
|
-
### group — Route Groups
|
|
412
|
-
|
|
413
|
-
```python
|
|
414
|
-
server app port 3000:
|
|
415
|
-
group "/api/v1":
|
|
416
|
-
get "/users":
|
|
417
|
-
ret users
|
|
418
|
-
post "/users" (req, res):
|
|
419
|
-
ret req.body
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### mid — Named Middleware
|
|
423
|
-
|
|
424
|
-
```python
|
|
425
|
-
fn logger(req, res, next):
|
|
426
|
-
log req.method, req.url
|
|
427
|
-
next()
|
|
428
|
-
|
|
429
|
-
server app port 3000:
|
|
430
|
-
mid logger # apply globally
|
|
431
|
-
mid logger "/api" # apply to path only
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Route-Level Middleware
|
|
435
|
-
|
|
436
|
-
Apply middleware to specific routes with bracket syntax:
|
|
437
|
-
|
|
438
|
-
```python
|
|
439
|
-
server app port 3000:
|
|
440
|
-
get "/admin" [authCheck] (req, res):
|
|
441
|
-
ret {admin: true}
|
|
442
|
-
|
|
443
|
-
post "/api/data" [auth, logger, validator] (req, res):
|
|
444
|
-
ret req.body
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
Compiles to `app.get("/admin", authCheck, (req, res) => { ... })`.
|
|
448
|
-
|
|
449
|
-
### static — Serve Files
|
|
450
|
-
|
|
451
|
-
```python
|
|
452
|
-
server app port 3000:
|
|
453
|
-
static "/public"
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### error — Error Handler
|
|
457
|
-
|
|
458
|
-
```python
|
|
459
|
-
server app port 3000:
|
|
460
|
-
error (err, req, res):
|
|
461
|
-
log.error err.message
|
|
462
|
-
ret.status 500 {error: "Internal error"}
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### openapi — Auto-Generated API Docs
|
|
466
|
-
|
|
467
|
-
```python
|
|
468
|
-
server app port 3000:
|
|
469
|
-
openapi "/docs"
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
Generates an OpenAPI 3.1 JSON spec from your schemas, served at the specified path.
|
|
473
|
-
|
|
474
|
-
### Response Helpers
|
|
475
|
-
|
|
476
|
-
```python
|
|
477
|
-
ret {data: items} # JSON (default)
|
|
478
|
-
ret.status 404 {error: "nope"} # status code
|
|
479
|
-
ret.redirect "/login" # redirect
|
|
480
|
-
ret.redirect 301 "/new-url" # redirect with status
|
|
481
|
-
ret.html "<h1>Hello</h1>" # HTML
|
|
482
|
-
ret.text "pong" # plain text
|
|
483
|
-
ret.file "/path/to/file" # send file
|
|
484
|
-
ret.download "/path/to/file.zip" # file download
|
|
485
|
-
ret.download "/file.zip" "custom.zip" # download with filename
|
|
486
|
-
ret.render "template" {data} # render template (requires view)
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
## Testing
|
|
490
|
-
|
|
491
|
-
Built-in test syntax using Node.js test runner:
|
|
492
|
-
|
|
493
|
-
```python
|
|
494
|
-
test "user creation":
|
|
495
|
-
user = UserStore.create({name: "Alice", email: "alice@test.com"})
|
|
496
|
-
assert user.name == "Alice"
|
|
497
|
-
assert user.email == "alice@test.com"
|
|
498
|
-
|
|
499
|
-
test "math":
|
|
500
|
-
assert 1 + 1 == 2
|
|
501
|
-
assert 10 > 5
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
`assert a == b` generates `assert.strictEqual` for better error messages. Run with `node --test`.
|
|
505
|
-
|
|
506
|
-
### Mock / Spy (Test Utilities)
|
|
507
|
-
|
|
508
|
-
```python
|
|
509
|
-
fn.async main():
|
|
510
|
-
# Create a mock function
|
|
511
|
-
any mock = createMock()
|
|
512
|
-
mock(1, 2)
|
|
513
|
-
mock("hello")
|
|
514
|
-
log mock.callCount() # 2
|
|
515
|
-
log mock.calledWith(1, 2) # true
|
|
516
|
-
|
|
517
|
-
# Mock with return value
|
|
518
|
-
mock.returns(42)
|
|
519
|
-
log mock() # 42
|
|
520
|
-
|
|
521
|
-
# Spy on an existing method
|
|
522
|
-
any spy = createSpy(obj, "method")
|
|
523
|
-
obj.method("arg")
|
|
524
|
-
log spy.callCount() # 1
|
|
525
|
-
spy.restore() # restores original method
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
`createMock(fn?)` — create a mock function with `.calls`, `.callCount()`, `.calledWith(...)`, `.returns(val)`, `.impl(fn)`, `.reset()`.
|
|
529
|
-
`createSpy(obj, method)` — wraps an existing method with a mock. `.restore()` reverts it.
|
|
530
|
-
|
|
531
|
-
## Job Queue
|
|
532
|
-
|
|
533
|
-
In-memory async job queue for background processing:
|
|
534
|
-
|
|
535
|
-
```python
|
|
536
|
-
queue jobs:
|
|
537
|
-
job "sendEmail" (data):
|
|
538
|
-
log "sending to {data.to}"
|
|
539
|
-
job "resize" (data):
|
|
540
|
-
log "resizing {data.path}"
|
|
541
|
-
|
|
542
|
-
server app port 3000:
|
|
543
|
-
post "/api/notify" (req, res):
|
|
544
|
-
jobs.add("sendEmail", {to: req.body.email})
|
|
545
|
-
ret {queued: true}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
##
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
#
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
##
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
```
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
```
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1
|
+
# NAIDE
|
|
2
|
+
|
|
3
|
+
**Node AI Development Environment** — A programming language designed for AI-speed code generation that transpiles to Node.js.
|
|
4
|
+
|
|
5
|
+
NAIDE is built on three principles: one way to write everything (zero ambiguity), keyword-driven intent (the first token decides meaning), and minimal token count (fewer tokens = faster AI generation). It includes built-in declarations for servers, databases, authentication, file uploads, WebSockets, Discord bots, job queues, testing, and more — all with zero external dependencies.
|
|
6
|
+
|
|
7
|
+
Two syntax modes:
|
|
8
|
+
|
|
9
|
+
- **NAIDE** (`.naide`) — Human-readable, ~40% fewer tokens than JavaScript
|
|
10
|
+
- **NAIDE-X** (`.nx`) — AI-only readability, ~80% fewer tokens than JavaScript
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g naider
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Create a new project
|
|
22
|
+
naide init my-app
|
|
23
|
+
cd my-app
|
|
24
|
+
npm install
|
|
25
|
+
npm run dev
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or write a file directly:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# app.naide
|
|
32
|
+
server app port 3000:
|
|
33
|
+
get "/":
|
|
34
|
+
ret {message: "Hello from NAIDE"}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
naide app.naide
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## CLI
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
naide <file> # Run a .naide or .nx file
|
|
45
|
+
naide # Start interactive REPL
|
|
46
|
+
naide repl # Start interactive REPL
|
|
47
|
+
naide init [dir] # Scaffold a new project
|
|
48
|
+
naide build [dir] [outdir] # Transpile all files to JavaScript
|
|
49
|
+
naide check <files...> # Type-check without running
|
|
50
|
+
naide fmt <files...> # Format NAIDE files
|
|
51
|
+
naide lsp # Start Language Server (LSP)
|
|
52
|
+
naide vscode # Install VS Code extension
|
|
53
|
+
naide deploy [dir] # Generate Dockerfile for deployment
|
|
54
|
+
naide convert <files...> # Convert .nx ↔ .naide (bidirectional)
|
|
55
|
+
naide pkg init # Create naide.pkg.json manifest
|
|
56
|
+
naide pkg install <name> # Install a NAIDE package
|
|
57
|
+
naide pkg publish # Publish package to npm
|
|
58
|
+
naide -w <file> # Watch mode (auto-restart on changes)
|
|
59
|
+
naide -d <file> # Debug mode (Node.js inspector)
|
|
60
|
+
naide --emit <file> # Print generated JavaScript
|
|
61
|
+
naide -o <out.js> <file> # Write JavaScript to file
|
|
62
|
+
naide --mid <file.nx> # Show intermediate NAIDE v1 (debug X mode)
|
|
63
|
+
naide --ast <file> # Print AST
|
|
64
|
+
naide --tokens <file> # Print token stream
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### REPL
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
$ naide
|
|
71
|
+
NAIDE REPL v1.11.0 — type NAIDE code, see JavaScript output
|
|
72
|
+
Type .exit to quit, .eval to toggle eval mode
|
|
73
|
+
|
|
74
|
+
>>> str name = "hello"
|
|
75
|
+
const name = "hello";
|
|
76
|
+
|
|
77
|
+
>>> fn add(int a, int b) -> int:
|
|
78
|
+
... ret a + b
|
|
79
|
+
...
|
|
80
|
+
function add(a, b) {
|
|
81
|
+
return a + b;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Type `.eval` to switch to evaluation mode (runs the code instead of showing JS).
|
|
86
|
+
|
|
87
|
+
### Playground
|
|
88
|
+
|
|
89
|
+
Open `playground/index.html` in a browser (via a local server) for a live in-browser transpiler with examples and token count comparison.
|
|
90
|
+
|
|
91
|
+
### Benchmark
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
node benchmark/compare.js
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Language Tokens Chars Lines Savings
|
|
99
|
+
JavaScript 330 2590 78 —
|
|
100
|
+
NAIDE 66 517 19 -80%
|
|
101
|
+
NAIDE-X 39 435 19 -88%
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Language Reference
|
|
105
|
+
|
|
106
|
+
### Variables
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
str name = "hello" # immutable (const)
|
|
110
|
+
int count = 42
|
|
111
|
+
num price = 9.99
|
|
112
|
+
bool active = true
|
|
113
|
+
list items = [1, 2, 3]
|
|
114
|
+
map config = {host: "localhost"}
|
|
115
|
+
any data = null
|
|
116
|
+
|
|
117
|
+
mut int counter = 0 # mutable (let)
|
|
118
|
+
mut str label = "init"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Types: `str`, `int`, `num`, `bool`, `list`, `map`, `any`, `json`, `void`
|
|
122
|
+
|
|
123
|
+
### String Interpolation
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
str greeting = "Hello {name}, you have {count} items"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Double-quoted strings with `{expr}` are auto-interpolated.
|
|
130
|
+
|
|
131
|
+
### Functions
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
fn add(int a, int b) -> int:
|
|
135
|
+
ret a + b
|
|
136
|
+
|
|
137
|
+
fn greet(str name, str prefix = "Hello"):
|
|
138
|
+
log "{prefix}, {name}!"
|
|
139
|
+
|
|
140
|
+
fn sum(...int nums) -> int:
|
|
141
|
+
mut int total = 0
|
|
142
|
+
each n in nums:
|
|
143
|
+
total += n
|
|
144
|
+
ret total
|
|
145
|
+
|
|
146
|
+
fn.async fetchUser(str id) -> map:
|
|
147
|
+
any res = await fetch("/api/users/{id}")
|
|
148
|
+
ret await res.json()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Control Flow
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
if count > 10:
|
|
155
|
+
log "many"
|
|
156
|
+
elif count > 5:
|
|
157
|
+
log "some"
|
|
158
|
+
else:
|
|
159
|
+
log "few"
|
|
160
|
+
|
|
161
|
+
str size = if count > 10 then "big" else "small"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Loops
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
each item in items:
|
|
168
|
+
log item
|
|
169
|
+
|
|
170
|
+
each key, val in config:
|
|
171
|
+
log "{key}: {val}"
|
|
172
|
+
|
|
173
|
+
for i in 0..10:
|
|
174
|
+
log i
|
|
175
|
+
|
|
176
|
+
while active:
|
|
177
|
+
log "running"
|
|
178
|
+
active = false
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Pattern Matching
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
match status:
|
|
185
|
+
"ok": log "success"
|
|
186
|
+
"error": log "failed"
|
|
187
|
+
_: log "unknown"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Error Handling
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
try:
|
|
194
|
+
data = await fetchData(url)
|
|
195
|
+
fail e:
|
|
196
|
+
log.error e.message
|
|
197
|
+
ensure:
|
|
198
|
+
log "request completed"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`ensure:` is NAIDE's `finally` — the block always runs, whether the `try` succeeds or fails. `fail` is optional:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
try:
|
|
205
|
+
conn = await openConnection()
|
|
206
|
+
ret await conn.query("SELECT 1")
|
|
207
|
+
ensure:
|
|
208
|
+
conn.close()
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Type Checking
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
if typeof data == "string":
|
|
215
|
+
log "is string"
|
|
216
|
+
|
|
217
|
+
if err instanceof TypeError:
|
|
218
|
+
log "type error"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`typeof` returns the type as a string. `instanceof` checks if a value is an instance of a class/constructor.
|
|
222
|
+
|
|
223
|
+
### Classes
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
model User:
|
|
227
|
+
str name
|
|
228
|
+
str email
|
|
229
|
+
int age = 0
|
|
230
|
+
|
|
231
|
+
fn greet() -> str:
|
|
232
|
+
ret "Hi, I'm {self.name}"
|
|
233
|
+
|
|
234
|
+
model Admin extends User:
|
|
235
|
+
str role = "admin"
|
|
236
|
+
fn permissions() -> list:
|
|
237
|
+
ret ["read", "write", "delete"]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Pipe Operator
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
list result = data
|
|
244
|
+
|> filter((x) => x.active)
|
|
245
|
+
|> map((x) => x.name)
|
|
246
|
+
|> sort()
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Imports / Exports
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
use express
|
|
253
|
+
use {readFile, writeFile} from "fs/promises"
|
|
254
|
+
use axios from "axios"
|
|
255
|
+
|
|
256
|
+
pub fn helper() -> str:
|
|
257
|
+
ret "exported"
|
|
258
|
+
pub str VERSION = "1.0.0"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Server & API Features
|
|
262
|
+
|
|
263
|
+
All features below are zero-dependency — the runtime is bundled with the package.
|
|
264
|
+
|
|
265
|
+
### server — Express App
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
server app port 3000:
|
|
269
|
+
get "/":
|
|
270
|
+
ret {message: "hello"}
|
|
271
|
+
post "/api/data" (req, res):
|
|
272
|
+
ret req.body
|
|
273
|
+
put "/api/data/:id" (req, res):
|
|
274
|
+
ret {updated: true}
|
|
275
|
+
del "/api/data/:id":
|
|
276
|
+
ret {deleted: true}
|
|
277
|
+
patch "/api/data/:id" (req, res):
|
|
278
|
+
ret {patched: true}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### schema — Data Models
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
schema User:
|
|
285
|
+
id auto
|
|
286
|
+
name str required min(2) max(50)
|
|
287
|
+
email str required email unique
|
|
288
|
+
age int optional min(0) max(150)
|
|
289
|
+
role enum("admin", "user") default("user")
|
|
290
|
+
joined timestamp auto
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Types: `str`, `int`, `num`, `bool`, `auto` (UUID), `timestamp`, `enum(...)`
|
|
294
|
+
Modifiers: `required`, `optional`, `min(n)`, `max(n)`, `email`, `url`, `unique`, `auto`, `default(val)`
|
|
295
|
+
|
|
296
|
+
### db — Persistent Storage
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
db "data/"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Schemas auto-persist to JSON files (`data/User.json`, etc.). Without `db`, data is in-memory only.
|
|
303
|
+
|
|
304
|
+
### crud — Auto-generated REST Endpoints
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
server app port 3000:
|
|
308
|
+
crud "/api/users" User
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Generates GET (list + by-ID), POST, PUT, DELETE with validation. Built-in pagination, search, and sort:
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
GET /api/users?page=2&limit=10&q=john&sort=name&order=asc
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Response: `{ data: [...], total, page, limit, pages }`
|
|
318
|
+
|
|
319
|
+
### validate — Request Validation
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
server app port 3000:
|
|
323
|
+
validate "/api/users" User
|
|
324
|
+
post "/api/users" (req, res):
|
|
325
|
+
user = UserStore.create(req.body) # body is pre-validated
|
|
326
|
+
ret user
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Auto-validates POST/PUT/PATCH bodies against the schema. Returns `400` with error details if invalid. Validated data replaces `req.body`.
|
|
330
|
+
|
|
331
|
+
### auth — JWT Authentication
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
server app port 3000:
|
|
335
|
+
auth JWT_SECRET:
|
|
336
|
+
protect "/api/*"
|
|
337
|
+
public "/api/auth/*"
|
|
338
|
+
|
|
339
|
+
post "/api/auth/login" (req, res):
|
|
340
|
+
token = auth.sign({id: user.id})
|
|
341
|
+
ret {token}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### cors / limit / cookie / session
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
server app port 3000:
|
|
348
|
+
cors "*" # CORS middleware
|
|
349
|
+
limit "/api/*" 100 "1m" # rate limiting (100 req/min)
|
|
350
|
+
cookie # cookie parser → req.cookies
|
|
351
|
+
session "my-secret" # cookie sessions → req.session
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### upload — File Uploads
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
server app port 3000:
|
|
358
|
+
upload "/api/upload" "avatar" (req, res):
|
|
359
|
+
ret {filename: req.file.filename, size: req.file.size}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Zero-dependency multipart parser. `req.file` = `{filename, contentType, data, size}`.
|
|
363
|
+
|
|
364
|
+
### view — Template Rendering
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
server app port 3000:
|
|
368
|
+
view "./views"
|
|
369
|
+
get "/":
|
|
370
|
+
ret.render "home" {title: "Welcome", items: ["a", "b"]}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Reads `.html` files with `{{variable}}`, `{{if key}}...{{/if}}`, `{{each item in list}}...{{/each}}`.
|
|
374
|
+
|
|
375
|
+
### sse — Server-Sent Events
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
server app port 3000:
|
|
379
|
+
sse "/events"
|
|
380
|
+
post "/api/notify" (req, res):
|
|
381
|
+
sse.broadcast req.body
|
|
382
|
+
ret {ok: true}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`sse.send(data)`, `sse.broadcast(data)`, `sse.count`.
|
|
386
|
+
|
|
387
|
+
### cache — Response Caching
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
server app port 3000:
|
|
391
|
+
cache "/api/*" "5m"
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Caches GET responses in memory with TTL. Sets `X-Cache: HIT/MISS`.
|
|
395
|
+
|
|
396
|
+
### ws — WebSocket
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
server app port 3000:
|
|
400
|
+
ws "/chat":
|
|
401
|
+
on "connect":
|
|
402
|
+
send({type: "welcome"})
|
|
403
|
+
on "message" (data):
|
|
404
|
+
broadcast(data)
|
|
405
|
+
on "close":
|
|
406
|
+
log "client left"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Built-in `send(data)` and `broadcast(data)`. Requires `npm install ws`.
|
|
410
|
+
|
|
411
|
+
### group — Route Groups
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
server app port 3000:
|
|
415
|
+
group "/api/v1":
|
|
416
|
+
get "/users":
|
|
417
|
+
ret users
|
|
418
|
+
post "/users" (req, res):
|
|
419
|
+
ret req.body
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### mid — Named Middleware
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
fn logger(req, res, next):
|
|
426
|
+
log req.method, req.url
|
|
427
|
+
next()
|
|
428
|
+
|
|
429
|
+
server app port 3000:
|
|
430
|
+
mid logger # apply globally
|
|
431
|
+
mid logger "/api" # apply to path only
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Route-Level Middleware
|
|
435
|
+
|
|
436
|
+
Apply middleware to specific routes with bracket syntax:
|
|
437
|
+
|
|
438
|
+
```python
|
|
439
|
+
server app port 3000:
|
|
440
|
+
get "/admin" [authCheck] (req, res):
|
|
441
|
+
ret {admin: true}
|
|
442
|
+
|
|
443
|
+
post "/api/data" [auth, logger, validator] (req, res):
|
|
444
|
+
ret req.body
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Compiles to `app.get("/admin", authCheck, (req, res) => { ... })`.
|
|
448
|
+
|
|
449
|
+
### static — Serve Files
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
server app port 3000:
|
|
453
|
+
static "/public"
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### error — Error Handler
|
|
457
|
+
|
|
458
|
+
```python
|
|
459
|
+
server app port 3000:
|
|
460
|
+
error (err, req, res):
|
|
461
|
+
log.error err.message
|
|
462
|
+
ret.status 500 {error: "Internal error"}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### openapi — Auto-Generated API Docs
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
server app port 3000:
|
|
469
|
+
openapi "/docs"
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Generates an OpenAPI 3.1 JSON spec from your schemas, served at the specified path.
|
|
473
|
+
|
|
474
|
+
### Response Helpers
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
ret {data: items} # JSON (default)
|
|
478
|
+
ret.status 404 {error: "nope"} # status code
|
|
479
|
+
ret.redirect "/login" # redirect
|
|
480
|
+
ret.redirect 301 "/new-url" # redirect with status
|
|
481
|
+
ret.html "<h1>Hello</h1>" # HTML
|
|
482
|
+
ret.text "pong" # plain text
|
|
483
|
+
ret.file "/path/to/file" # send file
|
|
484
|
+
ret.download "/path/to/file.zip" # file download
|
|
485
|
+
ret.download "/file.zip" "custom.zip" # download with filename
|
|
486
|
+
ret.render "template" {data} # render template (requires view)
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## Testing
|
|
490
|
+
|
|
491
|
+
Built-in test syntax using Node.js test runner:
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
test "user creation":
|
|
495
|
+
user = UserStore.create({name: "Alice", email: "alice@test.com"})
|
|
496
|
+
assert user.name == "Alice"
|
|
497
|
+
assert user.email == "alice@test.com"
|
|
498
|
+
|
|
499
|
+
test "math":
|
|
500
|
+
assert 1 + 1 == 2
|
|
501
|
+
assert 10 > 5
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`assert a == b` generates `assert.strictEqual` for better error messages. Run with `node --test`.
|
|
505
|
+
|
|
506
|
+
### Mock / Spy (Test Utilities)
|
|
507
|
+
|
|
508
|
+
```python
|
|
509
|
+
fn.async main():
|
|
510
|
+
# Create a mock function
|
|
511
|
+
any mock = createMock()
|
|
512
|
+
mock(1, 2)
|
|
513
|
+
mock("hello")
|
|
514
|
+
log mock.callCount() # 2
|
|
515
|
+
log mock.calledWith(1, 2) # true
|
|
516
|
+
|
|
517
|
+
# Mock with return value
|
|
518
|
+
mock.returns(42)
|
|
519
|
+
log mock() # 42
|
|
520
|
+
|
|
521
|
+
# Spy on an existing method
|
|
522
|
+
any spy = createSpy(obj, "method")
|
|
523
|
+
obj.method("arg")
|
|
524
|
+
log spy.callCount() # 1
|
|
525
|
+
spy.restore() # restores original method
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
`createMock(fn?)` — create a mock function with `.calls`, `.callCount()`, `.calledWith(...)`, `.returns(val)`, `.impl(fn)`, `.reset()`.
|
|
529
|
+
`createSpy(obj, method)` — wraps an existing method with a mock. `.restore()` reverts it.
|
|
530
|
+
|
|
531
|
+
## Job Queue
|
|
532
|
+
|
|
533
|
+
In-memory async job queue for background processing:
|
|
534
|
+
|
|
535
|
+
```python
|
|
536
|
+
queue jobs:
|
|
537
|
+
job "sendEmail" (data):
|
|
538
|
+
log "sending to {data.to}"
|
|
539
|
+
job "resize" (data):
|
|
540
|
+
log "resizing {data.path}"
|
|
541
|
+
|
|
542
|
+
server app port 3000:
|
|
543
|
+
post "/api/notify" (req, res):
|
|
544
|
+
jobs.add("sendEmail", {to: req.body.email})
|
|
545
|
+
ret {queued: true}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Discord Bot
|
|
549
|
+
|
|
550
|
+
Built-in `bot` syntax for Discord bots — events, message handling, and slash commands with zero boilerplate:
|
|
551
|
+
|
|
552
|
+
```python
|
|
553
|
+
bot myBot token DISCORD_TOKEN:
|
|
554
|
+
on "ready":
|
|
555
|
+
log "Bot is online!"
|
|
556
|
+
|
|
557
|
+
on "message" (msg):
|
|
558
|
+
if msg.content == "!ping":
|
|
559
|
+
msg.reply("Pong!")
|
|
560
|
+
|
|
561
|
+
slash "hello" "Says hello":
|
|
562
|
+
interaction.reply("Hello!")
|
|
563
|
+
|
|
564
|
+
slash "ask" "Ask the AI":
|
|
565
|
+
str answer = await ai.ask(interaction.options.getString("question"))
|
|
566
|
+
interaction.reply(answer)
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Compiles to **discord.js** (Node.js/Bun) or **discord.py** (Python). Multi-target:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
naide bot.naide # Node.js (discord.js)
|
|
573
|
+
naide bot.naide --target bun # Bun (discord.js)
|
|
574
|
+
naide bot.naide --target python # Python (discord.py)
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
The `on "message"` event maps to `messageCreate` (discord.js) / `on_message` (discord.py). Slash commands are auto-registered on bot startup.
|
|
578
|
+
|
|
579
|
+
## Scheduled Tasks & Events
|
|
580
|
+
|
|
581
|
+
```python
|
|
582
|
+
every "5m":
|
|
583
|
+
log "cleanup running"
|
|
584
|
+
|
|
585
|
+
watch User.create (event):
|
|
586
|
+
log "new user: {event.data.name}"
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Intervals: `"30s"`, `"5m"`, `"1h"`, `"1d"`.
|
|
590
|
+
`watch` connects to `crud` events automatically.
|
|
591
|
+
|
|
592
|
+
## Environment Variables
|
|
593
|
+
|
|
594
|
+
```python
|
|
595
|
+
env:
|
|
596
|
+
PORT int default(3000)
|
|
597
|
+
JWT_SECRET str required
|
|
598
|
+
DB_URL str default("data/")
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Variables become constants. Missing `required` vars exit with an error.
|
|
602
|
+
|
|
603
|
+
## Built-in Functions
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
str id = uuid() # UUID
|
|
607
|
+
str hashed = hash("password") # scrypt hash
|
|
608
|
+
bool ok = verify("password", hashed) # timing-safe verify
|
|
609
|
+
str token = sign({id: 1}) # JWT (uses auth secret)
|
|
610
|
+
str token = sign({id: 1}, "my-secret") # JWT (explicit secret)
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Auto-imported from the runtime when used.
|
|
614
|
+
|
|
615
|
+
## HTTP Client
|
|
616
|
+
|
|
617
|
+
```python
|
|
618
|
+
fn.async getUsers() -> any:
|
|
619
|
+
any users = await api.get("https://api.example.com/users")
|
|
620
|
+
ret users
|
|
621
|
+
|
|
622
|
+
fn.async createUser(map data) -> any:
|
|
623
|
+
any result = await api.post("https://api.example.com/users", data)
|
|
624
|
+
ret result
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Methods: `api.get(url)`, `api.post(url, body)`, `api.put(url, body)`, `api.del(url)`, `api.raw(url, opts)`.
|
|
628
|
+
|
|
629
|
+
## AI / LLM Integration
|
|
630
|
+
|
|
631
|
+
Built-in AI client — zero dependencies, auto-detects provider from API key:
|
|
632
|
+
|
|
633
|
+
```python
|
|
634
|
+
fn.async main():
|
|
635
|
+
# Simple prompt
|
|
636
|
+
str answer = await ai.ask("Explain NAIDE in one sentence")
|
|
637
|
+
|
|
638
|
+
# Structured JSON output
|
|
639
|
+
map result = await ai.json("List 3 colors", {colors: ["string"]})
|
|
640
|
+
|
|
641
|
+
# Multi-turn chat
|
|
642
|
+
list msgs = [{role: "user", content: "Hi"}, {role: "assistant", content: "Hello!"}, {role: "user", content: "What is 2+2?"}]
|
|
643
|
+
str reply = await ai.chat(msgs)
|
|
644
|
+
|
|
645
|
+
# With options
|
|
646
|
+
str answer2 = await ai.ask("hello", {model: "gpt-4o", maxTokens: 100})
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
Set `AI_KEY`, `OPENAI_API_KEY`, or `ANTHROPIC_API_KEY` env var. Provider auto-detected: `sk-ant-*` → Anthropic, otherwise OpenAI-compatible.
|
|
650
|
+
|
|
651
|
+
### AI Streaming
|
|
652
|
+
|
|
653
|
+
```python
|
|
654
|
+
fn.async main():
|
|
655
|
+
each chunk in await ai.stream("Write a poem"):
|
|
656
|
+
log chunk
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
Returns an async iterator of text chunks — works with SSE for real-time chat UIs.
|
|
660
|
+
|
|
661
|
+
### Embeddings & Vector Search (RAG)
|
|
662
|
+
|
|
663
|
+
```python
|
|
664
|
+
fn.async main():
|
|
665
|
+
# Generate embeddings
|
|
666
|
+
list vec = await ai.embed("hello world")
|
|
667
|
+
list vecs = await ai.embed(["hello", "world"])
|
|
668
|
+
|
|
669
|
+
# Cosine similarity
|
|
670
|
+
num score = ai.similarity(vec1, vec2)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
Built-in vector store for RAG:
|
|
674
|
+
|
|
675
|
+
```python
|
|
676
|
+
fn.async search(str query):
|
|
677
|
+
list qVec = await ai.embed(query)
|
|
678
|
+
list results = vectors.search(qVec, 5)
|
|
679
|
+
ret results
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
`createVectorStore()` — in-memory vector store with `add(id, embedding, metadata)`, `search(queryEmbedding, topK)`, `remove(id)`, `clear()`.
|
|
683
|
+
|
|
684
|
+
### Prompt Templates
|
|
685
|
+
|
|
686
|
+
Reusable prompt declarations with default variables:
|
|
687
|
+
|
|
688
|
+
```python
|
|
689
|
+
prompt summarize {lang: "en"}:
|
|
690
|
+
"Summarize the following text in {lang}:"
|
|
691
|
+
"{text}"
|
|
692
|
+
|
|
693
|
+
fn.async main():
|
|
694
|
+
str p = summarize({text: "hello world"})
|
|
695
|
+
str answer = await ai.ask(p)
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
Multi-line templates are joined with newlines. Variables use `{name}` syntax.
|
|
699
|
+
|
|
700
|
+
### AI in Server Routes
|
|
701
|
+
|
|
702
|
+
```python
|
|
703
|
+
server app port 3000:
|
|
704
|
+
post "/api/ask" (req, res):
|
|
705
|
+
str answer = await ai.ask(req.body.prompt)
|
|
706
|
+
ret {answer}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
## SQL Database
|
|
710
|
+
|
|
711
|
+
SQLite and PostgreSQL support with the same store API:
|
|
712
|
+
|
|
713
|
+
```python
|
|
714
|
+
db.sql "sqlite" "app.db"
|
|
715
|
+
|
|
716
|
+
schema User:
|
|
717
|
+
id auto
|
|
718
|
+
name str required
|
|
719
|
+
email str required email
|
|
720
|
+
|
|
721
|
+
server app port 3000:
|
|
722
|
+
crud "/api/users" User
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Schemas with `db.sql` auto-use SQL storage instead of JSON files. Same API: `getAll`, `getById`, `create`, `update`, `delete`, `where`, `count`, `clear`.
|
|
726
|
+
|
|
727
|
+
Schema migration is automatic — when you add new fields to a schema, `ALTER TABLE ADD COLUMN` runs at startup. No manual migration needed.
|
|
728
|
+
|
|
729
|
+
PostgreSQL:
|
|
730
|
+
|
|
731
|
+
```python
|
|
732
|
+
db.sql "postgres" "postgresql://localhost/mydb"
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## Language Server (LSP)
|
|
736
|
+
|
|
737
|
+
Built-in LSP for editor integration — diagnostics, completions, and hover docs:
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
naide lsp
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### VS Code
|
|
744
|
+
|
|
745
|
+
```bash
|
|
746
|
+
naide vscode
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
One command installs the extension — syntax highlighting, LSP diagnostics, autocomplete, and hover docs for `.naide` and `.nx` files. Restart VS Code after install.
|
|
750
|
+
|
|
751
|
+
## Deploy
|
|
752
|
+
|
|
753
|
+
Generate deployment files with one command:
|
|
754
|
+
|
|
755
|
+
```bash
|
|
756
|
+
naide deploy
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
Creates `Dockerfile` and `.dockerignore`. Then:
|
|
760
|
+
|
|
761
|
+
```bash
|
|
762
|
+
docker build -t naide-app .
|
|
763
|
+
docker run -p 3000:3000 naide-app
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
## Multi-File Projects
|
|
767
|
+
|
|
768
|
+
Import between NAIDE files — extensions are auto-rewritten to `.mjs` in output:
|
|
769
|
+
|
|
770
|
+
```python
|
|
771
|
+
# routes.naide
|
|
772
|
+
pub fn.async handleUser(req, res):
|
|
773
|
+
ret {user: req.params.id}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
```python
|
|
777
|
+
# app.naide
|
|
778
|
+
use {handleUser} from "./routes.naide"
|
|
779
|
+
|
|
780
|
+
server app port 3000:
|
|
781
|
+
get "/user/:id" [handleUser] (req, res):
|
|
782
|
+
ret {ok: true}
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
Build all files with `naide build`, which compiles every `.naide`/`.nx` file to `.mjs`.
|
|
786
|
+
|
|
787
|
+
## Full Example
|
|
788
|
+
|
|
789
|
+
```python
|
|
790
|
+
db.sql "sqlite" "app.db"
|
|
791
|
+
|
|
792
|
+
env:
|
|
793
|
+
PORT int default(3000)
|
|
794
|
+
JWT_SECRET str required
|
|
795
|
+
|
|
796
|
+
schema User:
|
|
797
|
+
id auto
|
|
798
|
+
name str required min(2) max(50)
|
|
799
|
+
email str required email
|
|
800
|
+
password str required
|
|
801
|
+
|
|
802
|
+
queue jobs:
|
|
803
|
+
job "welcome" (data):
|
|
804
|
+
log "Welcome email to {data.email}"
|
|
805
|
+
|
|
806
|
+
server app port PORT:
|
|
807
|
+
cors "*"
|
|
808
|
+
cookie
|
|
809
|
+
session JWT_SECRET
|
|
810
|
+
auth JWT_SECRET:
|
|
811
|
+
protect "/api/*"
|
|
812
|
+
public "/api/auth/*"
|
|
813
|
+
limit "/api/*" 100 "1m"
|
|
814
|
+
static "/public"
|
|
815
|
+
cache "/api/users" "1m"
|
|
816
|
+
validate "/api/users" User
|
|
817
|
+
crud "/api/users" User
|
|
818
|
+
openapi "/docs"
|
|
819
|
+
|
|
820
|
+
post "/api/auth/register" (req, res):
|
|
821
|
+
try:
|
|
822
|
+
str hashed = hash(req.body.password)
|
|
823
|
+
user = UserStore.create({...req.body, password: hashed})
|
|
824
|
+
token = auth.sign({id: user.id})
|
|
825
|
+
jobs.add("welcome", {email: user.email})
|
|
826
|
+
ret {token, user}
|
|
827
|
+
fail e:
|
|
828
|
+
ret.status 500 {error: e.message}
|
|
829
|
+
ensure:
|
|
830
|
+
log.info "register attempt handled"
|
|
831
|
+
|
|
832
|
+
post "/api/auth/login" (req, res):
|
|
833
|
+
user = UserStore.where({email: req.body.email})[0]
|
|
834
|
+
if not user:
|
|
835
|
+
ret.status 401 {error: "Invalid credentials"}
|
|
836
|
+
if not verify(req.body.password, user.password):
|
|
837
|
+
ret.status 401 {error: "Invalid credentials"}
|
|
838
|
+
token = auth.sign({id: user.id})
|
|
839
|
+
ret {token}
|
|
840
|
+
|
|
841
|
+
post "/api/ask" (req, res):
|
|
842
|
+
str answer = await ai.ask(req.body.prompt)
|
|
843
|
+
ret {answer}
|
|
844
|
+
|
|
845
|
+
get "/admin" [authCheck] (req, res):
|
|
846
|
+
if typeof req.user == "undefined":
|
|
847
|
+
ret.status 401 {error: "not authenticated"}
|
|
848
|
+
ret {admin: true}
|
|
849
|
+
|
|
850
|
+
get "/old-page":
|
|
851
|
+
ret.redirect 301 "/new-page"
|
|
852
|
+
|
|
853
|
+
get "/download":
|
|
854
|
+
ret.download "/files/report.pdf" "report.pdf"
|
|
855
|
+
|
|
856
|
+
ws "/chat":
|
|
857
|
+
on "message" (data):
|
|
858
|
+
broadcast(data)
|
|
859
|
+
|
|
860
|
+
sse "/events"
|
|
861
|
+
|
|
862
|
+
get "/":
|
|
863
|
+
ret.html "<h1>Welcome</h1>"
|
|
864
|
+
|
|
865
|
+
error (err, req, res):
|
|
866
|
+
log.error err.message
|
|
867
|
+
ret.status 500 {error: "Something went wrong"}
|
|
868
|
+
|
|
869
|
+
watch User.create (event):
|
|
870
|
+
log "new user: {event.data.name}"
|
|
871
|
+
|
|
872
|
+
every "30m":
|
|
873
|
+
log "cleanup"
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
This generates a complete production API — SQLite database, AI/LLM integration, auth, password hashing, CORS, sessions, rate limiting, CRUD with pagination/search, WebSocket, SSE, file caching, request validation, background jobs, auto-generated API docs, route middleware, file downloads, redirects with status codes, type checking, error handling with ensure/finally, and event-driven hooks — from ~75 lines.
|
|
877
|
+
|
|
878
|
+
## NAIDE-X Syntax (.nx)
|
|
879
|
+
|
|
880
|
+
Every keyword is a single character. Line-start symbol = intent.
|
|
881
|
+
|
|
882
|
+
```
|
|
883
|
+
s:name="World" -- str name = "World"
|
|
884
|
+
i:count=42 -- int count = 42
|
|
885
|
+
~i:counter=0 -- mut int counter = 0
|
|
886
|
+
|
|
887
|
+
f greet(s:who)s -- fn greet(str who) -> str:
|
|
888
|
+
>"Hello, {who}!" -- ret "Hello, {who}!"
|
|
889
|
+
|
|
890
|
+
~f fetchData(s:url)a -- fn.async fetchData(str url) -> any:
|
|
891
|
+
>~fetch(url) -- ret await fetch(url)
|
|
892
|
+
|
|
893
|
+
?count>5 -- if count > 5:
|
|
894
|
+
log"many" -- log "many"
|
|
895
|
+
|count>2 -- elif count > 2:
|
|
896
|
+
log"some" -- log "some"
|
|
897
|
+
: -- else:
|
|
898
|
+
log"few" -- log "few"
|
|
899
|
+
|
|
900
|
+
@item<items -- each item in items:
|
|
901
|
+
@i<0..10 -- for i in 0..10:
|
|
902
|
+
*active -- while active:
|
|
903
|
+
|
|
904
|
+
$app:3000 -- server app port 3000:
|
|
905
|
+
G"/users" -- get "/users":
|
|
906
|
+
>users -- ret users
|
|
907
|
+
P"/users"(req,res) -- post "/users" (req, res):
|
|
908
|
+
>req.body -- ret req.body
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### NAIDE-X Cheat Sheet
|
|
912
|
+
|
|
913
|
+
| Symbol | Meaning | Symbol | Meaning |
|
|
914
|
+
|--------|---------|--------|---------|
|
|
915
|
+
| `>` | return | `?` | if |
|
|
916
|
+
| `\|` | elif | `:` | else |
|
|
917
|
+
| `@` | loop | `*` | while |
|
|
918
|
+
| `!` | try | `!!` | catch |
|
|
919
|
+
| `!!!` | ensure/finally | `>.d` | ret.download |
|
|
920
|
+
| `$` | server | `^` | model |
|
|
921
|
+
| `<` | import | `%` | match |
|
|
922
|
+
| `f` | function | `~f` | async function |
|
|
923
|
+
| `+` | export | `~` | await |
|
|
924
|
+
| `G` | GET | `P` | POST |
|
|
925
|
+
| `U` | PUT | `D` | DELETE |
|
|
926
|
+
| `X` | PATCH | `>.s` | ret.status |
|
|
927
|
+
| `>.r` | ret.redirect | `>.h` | ret.html |
|
|
928
|
+
| `>.t` | ret.text | `>.v` | ret.render |
|
|
929
|
+
|
|
930
|
+
Types: `s`=str `i`=int `n`=num `b`=bool `l`=list `m`=map `a`=any
|
|
931
|
+
|
|
932
|
+
High-level keywords work in both modes: `schema`, `crud`, `auth`, `cors`, `limit`, `env`, `every`, `watch`, `static`, `ws`, `db`, `db.sql`, `group`, `cookie`, `error`, `session`, `upload`, `view`, `sse`, `cache`, `patch`, `validate`, `test`, `assert`, `queue`, `openapi`, `typeof`, `instanceof`, `ensure`, `ai`, `prompt`, `bot`, `slash`.
|
|
933
|
+
|
|
934
|
+
NAIDE-X log shorthands: `log.e` = error, `log.w` = warn, `log.i` = info, `log.d` = debug.
|
|
935
|
+
|
|
936
|
+
## Plugin System
|
|
937
|
+
|
|
938
|
+
Register and use plugins for extensibility:
|
|
939
|
+
|
|
940
|
+
```python
|
|
941
|
+
registerPlugin("logger", (opts) =>
|
|
942
|
+
ret {log: (msg) => log "[{opts.prefix}] {msg}"}
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
any logger = usePlugin("logger", {prefix: "APP"})
|
|
946
|
+
logger.log("started")
|
|
947
|
+
|
|
948
|
+
list names = listPlugins()
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
`registerPlugin(name, setup)` — registers a plugin factory. `usePlugin(name, opts?)` — initializes on first call, returns cached exports. `listPlugins()` — returns registered plugin names.
|
|
952
|
+
|
|
953
|
+
## Convert (NX ↔ NAIDE)
|
|
954
|
+
|
|
955
|
+
Bidirectional conversion between `.nx` and `.naide`:
|
|
956
|
+
|
|
957
|
+
```bash
|
|
958
|
+
naide convert file.nx # → file.naide (expand to readable syntax)
|
|
959
|
+
naide convert file.naide # → file.nx (compress to NX syntax)
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
## Source Maps & Error Remapping
|
|
963
|
+
|
|
964
|
+
Runtime errors are automatically remapped to source file line numbers:
|
|
965
|
+
|
|
966
|
+
```
|
|
967
|
+
Error in app.naide:12
|
|
968
|
+
10 | user = UserStore.getById(id)
|
|
969
|
+
11 | if not user:
|
|
970
|
+
>>12 | throw "not found"
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
The CLI tracks source-to-output line mappings and shows context from your `.naide`/`.nx` file, not the generated JavaScript.
|
|
974
|
+
|
|
975
|
+
## Native Dependency Detection
|
|
976
|
+
|
|
977
|
+
When you run a `.naide` file, the CLI scans generated JavaScript for missing dependencies (`express`, `better-sqlite3`, `pg`, `ws`) and prints install hints:
|
|
978
|
+
|
|
979
|
+
```
|
|
980
|
+
[NAIDE] Missing: express — run: npm install express
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
## Type Checker
|
|
984
|
+
|
|
985
|
+
Compile-time type checking without running the code:
|
|
986
|
+
|
|
987
|
+
```bash
|
|
988
|
+
naide check app.naide
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
Catches type mismatches at compile time:
|
|
992
|
+
|
|
993
|
+
```
|
|
994
|
+
app.naide:3 ERROR: Type mismatch: cannot assign str to int
|
|
995
|
+
app.naide:7 WARN: Type warning: reassigning int variable 'count' with str
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
The type checker understands NAIDE's type annotations (`str`, `int`, `num`, `bool`, `list`, `map`), infers types from expressions and function return values, and checks assignments for compatibility. `num` accepts `int` values. `any` and `json` accept all types.
|
|
999
|
+
|
|
1000
|
+
Also available as a flag: `naide --check app.naide` or programmatically via `compile(source, { typeCheck: true })`.
|
|
1001
|
+
|
|
1002
|
+
## Async Error Handling
|
|
1003
|
+
|
|
1004
|
+
Async route handlers are automatically wrapped with try/catch to prevent unhandled rejections:
|
|
1005
|
+
|
|
1006
|
+
```python
|
|
1007
|
+
server app port 3000:
|
|
1008
|
+
post "/api/data" (req, res):
|
|
1009
|
+
any data = await fetchData() # if this throws...
|
|
1010
|
+
ret data # ...a 500 JSON error is returned automatically
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
Generated code includes `try { ... } catch (__err) { res.status(500).json({ error: __err.message }) }` around async handlers. Routes with explicit `try/fail` blocks are left as-is.
|
|
1014
|
+
|
|
1015
|
+
A global `process.on('unhandledRejection')` handler is also added to server code to catch any remaining async errors.
|
|
1016
|
+
|
|
1017
|
+
## Debugger
|
|
1018
|
+
|
|
1019
|
+
Debug NAIDE programs with the Node.js inspector:
|
|
1020
|
+
|
|
1021
|
+
```bash
|
|
1022
|
+
naide -d app.naide # starts with --inspect-brk
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
Then open `chrome://inspect` in Chrome to connect. The program pauses at the first line so you can set breakpoints before execution.
|
|
1026
|
+
|
|
1027
|
+
## Package Ecosystem
|
|
1028
|
+
|
|
1029
|
+
Manage NAIDE packages via npm:
|
|
1030
|
+
|
|
1031
|
+
```bash
|
|
1032
|
+
naide pkg init # create naide.pkg.json manifest
|
|
1033
|
+
naide pkg install my-plugin # install from npm + add to manifest
|
|
1034
|
+
naide pkg publish # publish to npm with naide-plugin keyword
|
|
1035
|
+
naide pkg list # list installed NAIDE packages
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
The `naide.pkg.json` manifest tracks NAIDE-specific metadata (main entry, exports, dependencies) while using npm as the underlying registry.
|
|
1039
|
+
|
|
1040
|
+
## Why NAIDE?
|
|
1041
|
+
|
|
1042
|
+
AI code generation speed depends on:
|
|
1043
|
+
|
|
1044
|
+
1. **Token count** — fewer output tokens = faster generation
|
|
1045
|
+
2. **Predictability** — one way to write everything = better next-token prediction
|
|
1046
|
+
3. **Context window** — shorter code = more room for complex projects
|
|
1047
|
+
|
|
1048
|
+
NAIDE-X is designed as an **AI-internal representation** — the AI thinks in NAIDE-X, users receive standard JavaScript.
|
|
1049
|
+
|
|
1050
|
+
## License
|
|
1051
|
+
|
|
1052
|
+
MIT
|