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 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 bots, job queues, testing, and more — all with zero external dependencies.
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 Bot
548
+ ## Bots (Discord / Slack / Telegram / LINE)
549
549
 
550
- Built-in `bot` syntax for Discord bots — events, message handling, and slash commands with zero boilerplate:
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
- slash "ask" "Ask the AI":
565
- str answer = await ai.ask(interaction.options.getString("question"))
566
- interaction.reply(answer)
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 **discord.js** (Node.js/Bun) or **discord.py** (Python). Multi-target:
579
+ Compiles to the right SDK per platform and per target:
570
580
 
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)
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
- The `on "message"` event maps to `messageCreate` (discord.js) / `on_message` (discord.py). Slash commands are auto-registered on bot startup.
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.11.0",
4
- "description": "NAIDE - Node AI Development Environment. AI-specialized language with built-in server, auth, DB, SQL, AI/LLM, WebSocket, Discord Bot, and more — transpiles to Node.js.",
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",
@@ -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);