naider 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -15
- package/SPEC.naide +74 -0
- package/lsp/server.js +15 -1
- package/package.json +16 -3
- package/src/generator-bun.js +89 -0
- package/src/generator-python.js +441 -2
- package/src/generator.js +425 -4
- package/src/parser.js +200 -2
- package/src/tokens.js +12 -0
- package/vscode-naide/syntaxes/naide.tmLanguage.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Node AI Development Environment** — A programming language designed for AI-speed code generation that transpiles to Node.js.
|
|
4
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
|
|
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, bots (Discord/Slack/Telegram/LINE), CLI apps, HTML pages, email, GraphQL, desktop apps, mobile screens, cron jobs, job queues, testing, and more — all with zero external dependencies.
|
|
6
6
|
|
|
7
7
|
Two syntax modes:
|
|
8
8
|
|
|
@@ -545,36 +545,120 @@ server app port 3000:
|
|
|
545
545
|
ret {queued: true}
|
|
546
546
|
```
|
|
547
547
|
|
|
548
|
-
## Discord
|
|
548
|
+
## Bots (Discord / Slack / Telegram / LINE)
|
|
549
549
|
|
|
550
|
-
Built-in `bot` syntax
|
|
550
|
+
Built-in `bot` syntax with multi-platform support — events, message handling, and slash commands with zero boilerplate:
|
|
551
551
|
|
|
552
552
|
```python
|
|
553
|
+
# Discord (default)
|
|
553
554
|
bot myBot token DISCORD_TOKEN:
|
|
554
555
|
on "ready":
|
|
555
556
|
log "Bot is online!"
|
|
556
|
-
|
|
557
557
|
on "message" (msg):
|
|
558
558
|
if msg.content == "!ping":
|
|
559
559
|
msg.reply("Pong!")
|
|
560
|
-
|
|
561
560
|
slash "hello" "Says hello":
|
|
562
561
|
interaction.reply("Hello!")
|
|
563
562
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
563
|
+
# Slack
|
|
564
|
+
bot slackBot type "slack" token SLACK_TOKEN:
|
|
565
|
+
on "message" (msg):
|
|
566
|
+
msg.reply("Hi from Slack!")
|
|
567
|
+
|
|
568
|
+
# Telegram
|
|
569
|
+
bot tgBot type "telegram" token TG_TOKEN:
|
|
570
|
+
on "message" (msg):
|
|
571
|
+
msg.reply("Hi from Telegram!")
|
|
572
|
+
|
|
573
|
+
# LINE
|
|
574
|
+
bot lineBot type "line" token LINE_TOKEN:
|
|
575
|
+
on "message" (event):
|
|
576
|
+
log event
|
|
567
577
|
```
|
|
568
578
|
|
|
569
|
-
Compiles to
|
|
579
|
+
Compiles to the right SDK per platform and per target:
|
|
570
580
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
581
|
+
| Platform | Node.js / Bun | Python |
|
|
582
|
+
|----------|---------------|--------|
|
|
583
|
+
| Discord | discord.js | discord.py |
|
|
584
|
+
| Slack | @slack/bolt | slack_bolt |
|
|
585
|
+
| Telegram | node-telegram-bot-api | python-telegram-bot |
|
|
586
|
+
| LINE | @line/bot-sdk | linebot |
|
|
587
|
+
|
|
588
|
+
## Page Generation (HTML)
|
|
589
|
+
|
|
590
|
+
```python
|
|
591
|
+
page "index.html":
|
|
592
|
+
title "My App"
|
|
593
|
+
style "styles.css"
|
|
594
|
+
div "container":
|
|
595
|
+
h1 "Hello World"
|
|
596
|
+
p "Welcome"
|
|
597
|
+
a "https://example.com" "Click here"
|
|
598
|
+
script "app.js"
|
|
575
599
|
```
|
|
576
600
|
|
|
577
|
-
|
|
601
|
+
Generates a complete HTML file with proper head/body structure.
|
|
602
|
+
|
|
603
|
+
## CLI Apps
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
cli myTool "A useful tool":
|
|
607
|
+
arg "name" str "Your name"
|
|
608
|
+
arg "count" int "How many times"
|
|
609
|
+
flag "v" "verbose" "Verbose output"
|
|
610
|
+
run (args):
|
|
611
|
+
log "Hello {args.name}"
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Compiles to `process.argv` parser (Node.js/Bun) or `argparse` (Python).
|
|
615
|
+
|
|
616
|
+
## Email
|
|
617
|
+
|
|
618
|
+
```python
|
|
619
|
+
mail "smtp.gmail.com" 587:
|
|
620
|
+
user env.MAIL_USER
|
|
621
|
+
pass env.MAIL_PASS
|
|
622
|
+
# Usage: mail.send("to@email.com", "Subject", "Body")
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Compiles to `nodemailer` (Node.js/Bun) or `smtplib` (Python).
|
|
626
|
+
|
|
627
|
+
## GraphQL
|
|
628
|
+
|
|
629
|
+
Add GraphQL inside any server block — auto-generates schema from `schema` declarations:
|
|
630
|
+
|
|
631
|
+
```python
|
|
632
|
+
server app port 3000:
|
|
633
|
+
schema User:
|
|
634
|
+
id auto
|
|
635
|
+
name str
|
|
636
|
+
email str
|
|
637
|
+
graphql "/graphql"
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Desktop Apps
|
|
641
|
+
|
|
642
|
+
```python
|
|
643
|
+
desktop myApp:
|
|
644
|
+
title "My Desktop App"
|
|
645
|
+
size 1024 768
|
|
646
|
+
load "index.html"
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
Compiles to Electron (Node.js/Bun) or pywebview (Python).
|
|
650
|
+
|
|
651
|
+
## Mobile Screens
|
|
652
|
+
|
|
653
|
+
```python
|
|
654
|
+
screen Home:
|
|
655
|
+
text "Hello World"
|
|
656
|
+
button "Click Me"
|
|
657
|
+
input "Enter your name"
|
|
658
|
+
image "logo.png"
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
Compiles to React Native (Node.js/Bun) or Kivy (Python).
|
|
578
662
|
|
|
579
663
|
## Scheduled Tasks & Events
|
|
580
664
|
|
|
@@ -582,11 +666,14 @@ The `on "message"` event maps to `messageCreate` (discord.js) / `on_message` (di
|
|
|
582
666
|
every "5m":
|
|
583
667
|
log "cleanup running"
|
|
584
668
|
|
|
669
|
+
every "*/5 * * * *":
|
|
670
|
+
log "cron every 5 minutes"
|
|
671
|
+
|
|
585
672
|
watch User.create (event):
|
|
586
673
|
log "new user: {event.data.name}"
|
|
587
674
|
```
|
|
588
675
|
|
|
589
|
-
Intervals: `"30s"`, `"5m"`, `"1h"`, `"1d"`.
|
|
676
|
+
Intervals: `"30s"`, `"5m"`, `"1h"`, `"1d"`. Cron expressions auto-detected.
|
|
590
677
|
`watch` connects to `crud` events automatically.
|
|
591
678
|
|
|
592
679
|
## Environment Variables
|
package/SPEC.naide
CHANGED
|
@@ -446,3 +446,77 @@ any val = data?.nested?.value ?? "default"
|
|
|
446
446
|
# ---- スプレッド ----
|
|
447
447
|
list combined = [...items, 4, 5, 6]
|
|
448
448
|
map merged = {...config, debug: true}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# ---- ページ生成 (HTML) ----
|
|
452
|
+
page "index.html":
|
|
453
|
+
title "My App"
|
|
454
|
+
style "styles.css"
|
|
455
|
+
div "container":
|
|
456
|
+
h1 "Hello World"
|
|
457
|
+
p "Welcome to NAIDE"
|
|
458
|
+
a "https://example.com" "Click here"
|
|
459
|
+
script "app.js"
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
# ---- CLI アプリ ----
|
|
463
|
+
cli myTool "A useful tool":
|
|
464
|
+
arg "name" str "Your name"
|
|
465
|
+
arg "count" int "Number of times"
|
|
466
|
+
flag "v" "verbose" "Verbose output"
|
|
467
|
+
run (args):
|
|
468
|
+
log "Hello {args.name}"
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
# ---- メール送信 ----
|
|
472
|
+
mail "smtp.gmail.com" 587:
|
|
473
|
+
user env.MAIL_USER
|
|
474
|
+
pass env.MAIL_PASS
|
|
475
|
+
# usage: mail.send("to@email.com", "Subject", "Body")
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
# ---- Cron / スケジュール ----
|
|
479
|
+
every "5s":
|
|
480
|
+
log "every 5 seconds"
|
|
481
|
+
|
|
482
|
+
every "*/5 * * * *":
|
|
483
|
+
log "cron: every 5 minutes"
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
# ---- GraphQL (サーバー内) ----
|
|
487
|
+
server app port 3000:
|
|
488
|
+
graphql "/graphql"
|
|
489
|
+
# schema定義からGraphQLスキーマを自動生成
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# ---- マルチプラットフォーム Bot ----
|
|
493
|
+
bot myBot token "TOKEN":
|
|
494
|
+
on "message" (msg):
|
|
495
|
+
msg.reply("Hello!")
|
|
496
|
+
|
|
497
|
+
bot slackBot type "slack" token "xoxb-xxx":
|
|
498
|
+
on "message" (msg):
|
|
499
|
+
msg.reply("Hi from Slack!")
|
|
500
|
+
|
|
501
|
+
bot tgBot type "telegram" token "123:ABC":
|
|
502
|
+
on "message" (msg):
|
|
503
|
+
msg.reply("Hi from Telegram!")
|
|
504
|
+
|
|
505
|
+
bot lineBot type "line" token "LINE_TOKEN":
|
|
506
|
+
on "message" (event):
|
|
507
|
+
log event
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
# ---- デスクトップアプリ ----
|
|
511
|
+
desktop myApp:
|
|
512
|
+
title "My Desktop App"
|
|
513
|
+
size 1024 768
|
|
514
|
+
load "index.html"
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# ---- モバイル / スクリーン ----
|
|
518
|
+
screen Home:
|
|
519
|
+
text "Hello World"
|
|
520
|
+
button "Click Me"
|
|
521
|
+
input "Enter your name"
|
|
522
|
+
image "logo.png"
|
package/lsp/server.js
CHANGED
|
@@ -282,12 +282,13 @@ function getCompletions() {
|
|
|
282
282
|
'try', 'fail', 'ensure', 'server', 'model', 'schema', 'use', 'pub', 'mut',
|
|
283
283
|
'log', 'typeof', 'instanceof', 'not', 'and', 'or', 'break', 'continue',
|
|
284
284
|
'throw', 'new', 'await', 'test', 'assert', 'queue', 'job', 'db', 'env',
|
|
285
|
-
'get', 'post', 'put', 'del', 'patch',
|
|
285
|
+
'get', 'post', 'put', 'del', 'patch', 'every', 'watch',
|
|
286
286
|
];
|
|
287
287
|
const types = ['str', 'int', 'num', 'bool', 'list', 'map', 'any', 'json', 'void'];
|
|
288
288
|
const features = [
|
|
289
289
|
'cors', 'auth', 'crud', 'limit', 'cookie', 'session', 'static', 'ws', 'sse',
|
|
290
290
|
'cache', 'view', 'upload', 'group', 'validate', 'openapi', 'error', 'mid', 'prompt',
|
|
291
|
+
'page', 'cli', 'mail', 'graphql', 'desktop', 'screen',
|
|
291
292
|
];
|
|
292
293
|
const builtins = [
|
|
293
294
|
{ label: 'uuid()', detail: 'Generate UUID v4', insertText: 'uuid()' },
|
|
@@ -306,6 +307,12 @@ function getCompletions() {
|
|
|
306
307
|
{ label: 'createSpy(obj, method)', detail: 'Spy on method', insertText: 'createSpy(' },
|
|
307
308
|
{ label: 'registerPlugin(name, setup)', detail: 'Register plugin', insertText: 'registerPlugin(' },
|
|
308
309
|
{ label: 'usePlugin(name)', detail: 'Use registered plugin', insertText: 'usePlugin(' },
|
|
310
|
+
{ label: 'page "file.html":', detail: 'Generate HTML page', insertText: 'page ' },
|
|
311
|
+
{ label: 'cli name "desc":', detail: 'CLI application', insertText: 'cli ' },
|
|
312
|
+
{ label: 'mail "host" port:', detail: 'Email config', insertText: 'mail ' },
|
|
313
|
+
{ label: 'graphql "/path"', detail: 'GraphQL endpoint', insertText: 'graphql ' },
|
|
314
|
+
{ label: 'desktop name:', detail: 'Desktop app', insertText: 'desktop ' },
|
|
315
|
+
{ label: 'screen Name:', detail: 'Mobile screen', insertText: 'screen ' },
|
|
309
316
|
];
|
|
310
317
|
|
|
311
318
|
return [
|
|
@@ -331,6 +338,13 @@ const HOVER_DOCS = {
|
|
|
331
338
|
'ensure': '**ensure** — Finally block (always runs)\n```naide\ntry:\n risky()\nensure:\n cleanup()\n```',
|
|
332
339
|
'test': '**test** — Test case\n```naide\ntest "math": assert 1 + 1 == 2\n```',
|
|
333
340
|
'queue': '**queue** — Async job queue\n```naide\nqueue tasks:\n job "send" (data): log data\n```',
|
|
341
|
+
'page': '**page** — Generate HTML page\n```naide\npage "index.html":\n title "My App"\n h1 "Hello"\n div "container":\n p "Welcome"\n```',
|
|
342
|
+
'cli': '**cli** — CLI application\n```naide\ncli myTool "description":\n arg "name" str "Your name"\n flag "v" "verbose" "Verbose output"\n run (args):\n log args.name\n```',
|
|
343
|
+
'mail': '**mail** — Email sending\n```naide\nmail "smtp.gmail.com" 587:\n user "me@gmail.com"\n pass env.MAIL_PASS\n```',
|
|
344
|
+
'graphql': '**graphql** — GraphQL endpoint (inside server)\n```naide\nserver app port 3000:\n graphql "/graphql"\n```',
|
|
345
|
+
'desktop': '**desktop** — Desktop app (Electron/pywebview)\n```naide\ndesktop myApp:\n title "My App"\n size 1024 768\n load "index.html"\n```',
|
|
346
|
+
'screen': '**screen** — Mobile screen (React Native/Kivy)\n```naide\nscreen Home:\n text "Hello World"\n button "Click Me"\n input "Enter name"\n```',
|
|
347
|
+
'every': '**every** — Scheduled task / cron\n```naide\nevery "5s":\n log "tick"\nevery "*/5 * * * *":\n log "cron"\n```',
|
|
334
348
|
};
|
|
335
349
|
|
|
336
350
|
function getHover(params) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naider",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "NAIDE - Node AI Development Environment. AI-specialized language with built-in server, auth, DB, SQL, AI/LLM, WebSocket, Discord
|
|
3
|
+
"version": "1.12.0",
|
|
4
|
+
"description": "NAIDE - Node AI Development Environment. AI-specialized language with built-in server, auth, DB, SQL, AI/LLM, WebSocket, bots (Discord/Slack/Telegram/LINE), CLI, HTML, email, GraphQL, desktop, mobile, and more — transpiles to Node.js/Python/Bun.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.js",
|
|
@@ -43,7 +43,20 @@
|
|
|
43
43
|
"backend",
|
|
44
44
|
"server",
|
|
45
45
|
"discord",
|
|
46
|
-
"discord-bot"
|
|
46
|
+
"discord-bot",
|
|
47
|
+
"slack",
|
|
48
|
+
"telegram",
|
|
49
|
+
"line",
|
|
50
|
+
"graphql",
|
|
51
|
+
"cli",
|
|
52
|
+
"email",
|
|
53
|
+
"desktop",
|
|
54
|
+
"mobile",
|
|
55
|
+
"electron",
|
|
56
|
+
"react-native",
|
|
57
|
+
"cron",
|
|
58
|
+
"python",
|
|
59
|
+
"bun"
|
|
47
60
|
],
|
|
48
61
|
"author": "pirikari.sena@gmail.com",
|
|
49
62
|
"license": "MIT",
|
package/src/generator-bun.js
CHANGED
|
@@ -77,6 +77,8 @@ export class BunGenerator extends Generator {
|
|
|
77
77
|
this.visitBunGroup(child);
|
|
78
78
|
} else if (child.type === 'SchemaDecl') {
|
|
79
79
|
this.visitSchema(child);
|
|
80
|
+
} else if (child.type === 'GraphqlDecl') {
|
|
81
|
+
this.visitBunGraphql(child);
|
|
80
82
|
} else {
|
|
81
83
|
this.visitStatement(child);
|
|
82
84
|
}
|
|
@@ -234,6 +236,18 @@ export class BunGenerator extends Generator {
|
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
for (const route of this.routes) {
|
|
239
|
+
if (route.__graphql) {
|
|
240
|
+
const gqlPath = JSON.stringify(route.__graphqlPath);
|
|
241
|
+
this.emit(`if (method === 'POST' && path === ${gqlPath}) {`);
|
|
242
|
+
this.indent++;
|
|
243
|
+
this.emit(`const { query, variables } = await req.json();`);
|
|
244
|
+
this.emit(`const result = await graphql({ schema: __graphqlSchema, source: query, rootValue: __graphqlRoot, variableValues: variables });`);
|
|
245
|
+
this.emit(`return new Response(JSON.stringify(result), { headers: { 'Content-Type': 'application/json' } });`);
|
|
246
|
+
this.indent--;
|
|
247
|
+
this.emit(`}`);
|
|
248
|
+
this.emitRaw('');
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
237
251
|
this.emitBunRoute(route);
|
|
238
252
|
}
|
|
239
253
|
|
|
@@ -498,6 +512,81 @@ export class BunGenerator extends Generator {
|
|
|
498
512
|
}
|
|
499
513
|
}
|
|
500
514
|
|
|
515
|
+
visitBunGraphql(node) {
|
|
516
|
+
const path = this.stringValue(node.path);
|
|
517
|
+
this.emit(`import { buildSchema, graphql } from 'graphql';`);
|
|
518
|
+
this.emitRaw('');
|
|
519
|
+
|
|
520
|
+
const schemaNames = [...this.schemas.keys()];
|
|
521
|
+
if (schemaNames.length > 0) {
|
|
522
|
+
let schemaDef = '';
|
|
523
|
+
for (const name of schemaNames) {
|
|
524
|
+
const schema = this.schemas.get(name);
|
|
525
|
+
const fields = schema.fields.map(f => {
|
|
526
|
+
const gqlType = this.toGraphqlType(f.type);
|
|
527
|
+
return ` ${f.name}: ${gqlType}`;
|
|
528
|
+
}).join('\\n');
|
|
529
|
+
schemaDef += `type ${name} {\\n${fields}\\n}\\n`;
|
|
530
|
+
}
|
|
531
|
+
schemaDef += `type Query {\\n`;
|
|
532
|
+
for (const name of schemaNames) {
|
|
533
|
+
schemaDef += ` ${name.toLowerCase()}s: [${name}]\\n`;
|
|
534
|
+
schemaDef += ` ${name.toLowerCase()}(id: ID): ${name}\\n`;
|
|
535
|
+
}
|
|
536
|
+
schemaDef += `}`;
|
|
537
|
+
|
|
538
|
+
this.emit(`const __graphqlSchema = buildSchema(\`${schemaDef}\`);`);
|
|
539
|
+
this.emit(`const __graphqlRoot = {`);
|
|
540
|
+
this.indent++;
|
|
541
|
+
for (const name of schemaNames) {
|
|
542
|
+
this.emit(`${name.toLowerCase()}s: () => ${name}Store.getAll(),`);
|
|
543
|
+
this.emit(`${name.toLowerCase()}: ({ id }) => ${name}Store.getById(id),`);
|
|
544
|
+
}
|
|
545
|
+
this.indent--;
|
|
546
|
+
this.emit(`};`);
|
|
547
|
+
} else {
|
|
548
|
+
this.emit(`const __graphqlSchema = buildSchema(\`type Query { hello: String }\`);`);
|
|
549
|
+
this.emit(`const __graphqlRoot = { hello: () => 'Hello from NAIDE GraphQL' };`);
|
|
550
|
+
}
|
|
551
|
+
this.emitRaw('');
|
|
552
|
+
|
|
553
|
+
const rawPath = this.rawString(node.path);
|
|
554
|
+
this.routes.push({
|
|
555
|
+
type: 'Route', method: 'post', path: node.path, prefix: '',
|
|
556
|
+
body: [], __graphql: true, __graphqlPath: rawPath,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
visitPage(node) {
|
|
561
|
+
const filename = this.rawString(node.filename);
|
|
562
|
+
const headParts = [];
|
|
563
|
+
const bodyParts = [];
|
|
564
|
+
let pageTitle = 'NAIDE Page';
|
|
565
|
+
|
|
566
|
+
for (const el of node.elements) {
|
|
567
|
+
this.classifyPageElement(el, headParts, bodyParts, (t) => { pageTitle = t; });
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const htmlLines = [
|
|
571
|
+
'<!DOCTYPE html>',
|
|
572
|
+
'<html lang="en">',
|
|
573
|
+
'<head>',
|
|
574
|
+
'<meta charset="UTF-8">',
|
|
575
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
|
576
|
+
`<title>${pageTitle}</title>`,
|
|
577
|
+
...headParts,
|
|
578
|
+
'</head>',
|
|
579
|
+
'<body>',
|
|
580
|
+
...bodyParts,
|
|
581
|
+
'</body>',
|
|
582
|
+
'</html>',
|
|
583
|
+
];
|
|
584
|
+
const html = htmlLines.join('\\n');
|
|
585
|
+
this.emit(`await Bun.write(${JSON.stringify(filename)}, \`${html}\`);`);
|
|
586
|
+
this.emit(`console.log('Generated: ${filename}');`);
|
|
587
|
+
this.emitRaw('');
|
|
588
|
+
}
|
|
589
|
+
|
|
501
590
|
visitTest(node) {
|
|
502
591
|
this.hasTests = true;
|
|
503
592
|
const name = this.stringValue(node.name);
|