naider 1.12.0 → 1.13.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 +51 -0
- package/SPEC.naide +44 -0
- package/lsp/server.js +6 -0
- package/package.json +1 -1
- package/src/generator-python.js +191 -0
- package/src/generator.js +214 -0
- package/src/parser.js +134 -1
- package/src/tokens.js +10 -0
- package/vscode-naide/syntaxes/naide.tmLanguage.json +1 -1
package/README.md
CHANGED
|
@@ -676,6 +676,57 @@ watch User.create (event):
|
|
|
676
676
|
Intervals: `"30s"`, `"5m"`, `"1h"`, `"1d"`. Cron expressions auto-detected.
|
|
677
677
|
`watch` connects to `crud` events automatically.
|
|
678
678
|
|
|
679
|
+
## OAuth (Social Login)
|
|
680
|
+
|
|
681
|
+
```python
|
|
682
|
+
oauth "google" env.CLIENT_ID env.CLIENT_SECRET:
|
|
683
|
+
callback "/auth/callback"
|
|
684
|
+
scope "email profile"
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
Supports Google, GitHub. Compiles to passport.js (Node.js) or authlib (Python).
|
|
688
|
+
|
|
689
|
+
## Payment (Stripe)
|
|
690
|
+
|
|
691
|
+
```python
|
|
692
|
+
pay "stripe" env.STRIPE_KEY:
|
|
693
|
+
webhook "/webhook"
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Usage: `pay.checkout([{name: "Item", price: 1000, qty: 1}])`. Compiles to Stripe SDK.
|
|
697
|
+
|
|
698
|
+
## Cloud Storage (S3/GCS)
|
|
699
|
+
|
|
700
|
+
```python
|
|
701
|
+
storage "s3" env.BUCKET env.AWS_KEY env.AWS_SECRET:
|
|
702
|
+
region "ap-northeast-1"
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
Usage: `storage.upload("key", data)`, `storage.download("key")`, `storage.remove("key")`. Compiles to AWS SDK (Node.js) or boto3 (Python).
|
|
706
|
+
|
|
707
|
+
## PDF Generation
|
|
708
|
+
|
|
709
|
+
```python
|
|
710
|
+
pdf "report.pdf":
|
|
711
|
+
title "Monthly Report"
|
|
712
|
+
h1 "Summary"
|
|
713
|
+
text "This is the content."
|
|
714
|
+
image "chart.png"
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Compiles to pdfkit (Node.js) or FPDF (Python).
|
|
718
|
+
|
|
719
|
+
## i18n (Internationalization)
|
|
720
|
+
|
|
721
|
+
```python
|
|
722
|
+
i18n "locales/":
|
|
723
|
+
default "en"
|
|
724
|
+
lang "en" "en.json"
|
|
725
|
+
lang "ja" "ja.json"
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Usage: `i18n.t("greeting.hello")`, `i18n.setLang("ja")`.
|
|
729
|
+
|
|
679
730
|
## Environment Variables
|
|
680
731
|
|
|
681
732
|
```python
|
package/SPEC.naide
CHANGED
|
@@ -520,3 +520,47 @@ screen Home:
|
|
|
520
520
|
button "Click Me"
|
|
521
521
|
input "Enter your name"
|
|
522
522
|
image "logo.png"
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
# ---- OAuth (ソーシャルログイン) ----
|
|
526
|
+
oauth "google" env.CLIENT_ID env.CLIENT_SECRET:
|
|
527
|
+
callback "/auth/callback"
|
|
528
|
+
scope "email profile"
|
|
529
|
+
|
|
530
|
+
oauth "github" env.GH_ID env.GH_SECRET:
|
|
531
|
+
callback "/auth/github/callback"
|
|
532
|
+
scope "user:email"
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
# ---- 決済 (Stripe) ----
|
|
536
|
+
pay "stripe" env.STRIPE_KEY:
|
|
537
|
+
webhook "/webhook"
|
|
538
|
+
success "/thanks"
|
|
539
|
+
cancel "/cancel"
|
|
540
|
+
# usage: pay.checkout([{name: "Item", price: 1000, qty: 1}])
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
# ---- ファイルストレージ (S3/GCS) ----
|
|
544
|
+
storage "s3" env.BUCKET env.AWS_KEY env.AWS_SECRET:
|
|
545
|
+
region "ap-northeast-1"
|
|
546
|
+
# usage: storage.upload("key", data)
|
|
547
|
+
# usage: storage.download("key")
|
|
548
|
+
# usage: storage.remove("key")
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# ---- PDF生成 ----
|
|
552
|
+
pdf "report.pdf":
|
|
553
|
+
title "Monthly Report"
|
|
554
|
+
h1 "Summary"
|
|
555
|
+
text "This is the report content."
|
|
556
|
+
line
|
|
557
|
+
image "chart.png"
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
# ---- i18n (多言語) ----
|
|
561
|
+
i18n "locales/":
|
|
562
|
+
default "en"
|
|
563
|
+
lang "en" "en.json"
|
|
564
|
+
lang "ja" "ja.json"
|
|
565
|
+
# usage: i18n.t("greeting.hello")
|
|
566
|
+
# usage: i18n.setLang("ja")
|
package/lsp/server.js
CHANGED
|
@@ -289,6 +289,7 @@ function getCompletions() {
|
|
|
289
289
|
'cors', 'auth', 'crud', 'limit', 'cookie', 'session', 'static', 'ws', 'sse',
|
|
290
290
|
'cache', 'view', 'upload', 'group', 'validate', 'openapi', 'error', 'mid', 'prompt',
|
|
291
291
|
'page', 'cli', 'mail', 'graphql', 'desktop', 'screen',
|
|
292
|
+
'oauth', 'pay', 'storage', 'pdf', 'i18n',
|
|
292
293
|
];
|
|
293
294
|
const builtins = [
|
|
294
295
|
{ label: 'uuid()', detail: 'Generate UUID v4', insertText: 'uuid()' },
|
|
@@ -345,6 +346,11 @@ const HOVER_DOCS = {
|
|
|
345
346
|
'desktop': '**desktop** — Desktop app (Electron/pywebview)\n```naide\ndesktop myApp:\n title "My App"\n size 1024 768\n load "index.html"\n```',
|
|
346
347
|
'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
348
|
'every': '**every** — Scheduled task / cron\n```naide\nevery "5s":\n log "tick"\nevery "*/5 * * * *":\n log "cron"\n```',
|
|
349
|
+
'oauth': '**oauth** — Social login (Google/GitHub)\n```naide\noauth "google" env.CLIENT_ID env.CLIENT_SECRET:\n callback "/auth/callback"\n scope "email profile"\n```',
|
|
350
|
+
'pay': '**pay** — Payment (Stripe)\n```naide\npay "stripe" env.STRIPE_KEY:\n webhook "/webhook"\n```\nUsage: `pay.checkout(items, successUrl, cancelUrl)`',
|
|
351
|
+
'storage': '**storage** — Cloud storage (S3/GCS)\n```naide\nstorage "s3" env.BUCKET env.KEY env.SECRET:\n region "ap-northeast-1"\n```\nUsage: `storage.upload(key, body)`, `storage.download(key)`',
|
|
352
|
+
'pdf': '**pdf** — PDF generation\n```naide\npdf "report.pdf":\n title "Report"\n text "Hello"\n```',
|
|
353
|
+
'i18n': '**i18n** — Internationalization\n```naide\ni18n "locales/":\n default "en"\n lang "en" "en.json"\n lang "ja" "ja.json"\n```\nUsage: `i18n.t("key")`, `i18n.setLang("ja")`',
|
|
348
354
|
};
|
|
349
355
|
|
|
350
356
|
function getHover(params) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naider",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
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": {
|
package/src/generator-python.js
CHANGED
|
@@ -148,6 +148,11 @@ export class PythonGenerator {
|
|
|
148
148
|
case 'MailConfig': return this.visitMail(node);
|
|
149
149
|
case 'DesktopApp': return this.visitDesktop(node);
|
|
150
150
|
case 'Screen': return this.visitScreen(node);
|
|
151
|
+
case 'OauthDecl': return this.visitOauth(node);
|
|
152
|
+
case 'PayDecl': return this.visitPay(node);
|
|
153
|
+
case 'StorageDecl': return this.visitStorage(node);
|
|
154
|
+
case 'PdfDecl': return this.visitPdf(node);
|
|
155
|
+
case 'I18nDecl': return this.visitI18n(node);
|
|
151
156
|
default:
|
|
152
157
|
this.emit(`# unknown: ${node.type}`);
|
|
153
158
|
}
|
|
@@ -1955,4 +1960,190 @@ export class PythonGenerator {
|
|
|
1955
1960
|
this.indent--;
|
|
1956
1961
|
this.emitRaw('');
|
|
1957
1962
|
}
|
|
1963
|
+
|
|
1964
|
+
// ===== OAuth =====
|
|
1965
|
+
visitOauth(node) {
|
|
1966
|
+
const provider = this.rawString(node.provider);
|
|
1967
|
+
const clientId = this.expr(node.clientId);
|
|
1968
|
+
const clientSecret = this.expr(node.clientSecret);
|
|
1969
|
+
const callback = node.callback ? this.rawString(node.callback) : '/auth/callback';
|
|
1970
|
+
const scope = node.scope ? this.rawString(node.scope) : 'email profile';
|
|
1971
|
+
|
|
1972
|
+
if (provider === 'google') {
|
|
1973
|
+
this.addFromImport('authlib.integrations.flask_client', 'OAuth');
|
|
1974
|
+
} else if (provider === 'github') {
|
|
1975
|
+
this.addFromImport('authlib.integrations.flask_client', 'OAuth');
|
|
1976
|
+
}
|
|
1977
|
+
this.emitRaw('');
|
|
1978
|
+
this.emit(`oauth = OAuth()`);
|
|
1979
|
+
this.emit(`oauth.register(`);
|
|
1980
|
+
this.indent++;
|
|
1981
|
+
this.emit(`name=${JSON.stringify(provider)},`);
|
|
1982
|
+
this.emit(`client_id=${clientId},`);
|
|
1983
|
+
this.emit(`client_secret=${clientSecret},`);
|
|
1984
|
+
if (provider === 'google') {
|
|
1985
|
+
this.emit(`server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',`);
|
|
1986
|
+
this.emit(`client_kwargs={'scope': ${JSON.stringify(scope)}},`);
|
|
1987
|
+
} else if (provider === 'github') {
|
|
1988
|
+
this.emit(`access_token_url='https://github.com/login/oauth/access_token',`);
|
|
1989
|
+
this.emit(`authorize_url='https://github.com/login/oauth/authorize',`);
|
|
1990
|
+
this.emit(`client_kwargs={'scope': ${JSON.stringify(scope)}},`);
|
|
1991
|
+
}
|
|
1992
|
+
this.indent--;
|
|
1993
|
+
this.emit(`)`);
|
|
1994
|
+
this.emitRaw('');
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// ===== Pay =====
|
|
1998
|
+
visitPay(node) {
|
|
1999
|
+
const provider = this.rawString(node.provider);
|
|
2000
|
+
const secretKey = this.expr(node.secretKey);
|
|
2001
|
+
|
|
2002
|
+
if (provider === 'stripe') {
|
|
2003
|
+
this.addImport('stripe');
|
|
2004
|
+
this.emitRaw('');
|
|
2005
|
+
this.emit(`stripe.api_key = ${secretKey}`);
|
|
2006
|
+
this.emitRaw('');
|
|
2007
|
+
this.emit(`class Pay:`);
|
|
2008
|
+
this.indent++;
|
|
2009
|
+
this.emit(`@staticmethod`);
|
|
2010
|
+
this.emit(`def checkout(items, success_url=None, cancel_url=None):`);
|
|
2011
|
+
this.indent++;
|
|
2012
|
+
this.emit(`line_items = [{'price_data': {'currency': 'usd', 'product_data': {'name': i['name']}, 'unit_amount': i['price']}, 'quantity': i.get('qty', 1)} for i in items]`);
|
|
2013
|
+
this.emit(`return stripe.checkout.Session.create(line_items=line_items, mode='payment', success_url=success_url or 'http://localhost:3000/success', cancel_url=cancel_url or 'http://localhost:3000/cancel')`);
|
|
2014
|
+
this.indent--;
|
|
2015
|
+
this.indent--;
|
|
2016
|
+
this.emit(`pay = Pay()`);
|
|
2017
|
+
} else {
|
|
2018
|
+
this.emit(`# ${provider} payment integration`);
|
|
2019
|
+
this.emit(`pay = None`);
|
|
2020
|
+
}
|
|
2021
|
+
this.emitRaw('');
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// ===== Storage =====
|
|
2025
|
+
visitStorage(node) {
|
|
2026
|
+
const provider = this.rawString(node.provider);
|
|
2027
|
+
const bucket = this.expr(node.bucket);
|
|
2028
|
+
const accessKey = this.expr(node.accessKey);
|
|
2029
|
+
const secretKey = this.expr(node.secretKey);
|
|
2030
|
+
const region = node.region ? this.rawString(node.region) : 'us-east-1';
|
|
2031
|
+
|
|
2032
|
+
if (provider === 's3') {
|
|
2033
|
+
this.addImport('boto3');
|
|
2034
|
+
this.emitRaw('');
|
|
2035
|
+
this.emit(`__s3 = boto3.client('s3', region_name=${JSON.stringify(region)}, aws_access_key_id=${accessKey}, aws_secret_access_key=${secretKey})`);
|
|
2036
|
+
this.emitRaw('');
|
|
2037
|
+
this.emit(`class Storage:`);
|
|
2038
|
+
this.indent++;
|
|
2039
|
+
this.emit(`@staticmethod`);
|
|
2040
|
+
this.emit(`def upload(key, body):`);
|
|
2041
|
+
this.indent++;
|
|
2042
|
+
this.emit(`__s3.put_object(Bucket=${bucket}, Key=key, Body=body)`);
|
|
2043
|
+
this.indent--;
|
|
2044
|
+
this.emit(`@staticmethod`);
|
|
2045
|
+
this.emit(`def download(key):`);
|
|
2046
|
+
this.indent++;
|
|
2047
|
+
this.emit(`return __s3.get_object(Bucket=${bucket}, Key=key)['Body'].read()`);
|
|
2048
|
+
this.indent--;
|
|
2049
|
+
this.emit(`@staticmethod`);
|
|
2050
|
+
this.emit(`def remove(key):`);
|
|
2051
|
+
this.indent++;
|
|
2052
|
+
this.emit(`__s3.delete_object(Bucket=${bucket}, Key=key)`);
|
|
2053
|
+
this.indent--;
|
|
2054
|
+
this.indent--;
|
|
2055
|
+
this.emit(`storage = Storage()`);
|
|
2056
|
+
} else if (provider === 'gcs') {
|
|
2057
|
+
this.addFromImport('google.cloud', 'storage as gcs_storage');
|
|
2058
|
+
this.emit(`__gcs = gcs_storage.Client()`);
|
|
2059
|
+
this.emit(`__bucket = __gcs.bucket(${bucket})`);
|
|
2060
|
+
this.emit(`class Storage:`);
|
|
2061
|
+
this.indent++;
|
|
2062
|
+
this.emit(`@staticmethod`);
|
|
2063
|
+
this.emit(`def upload(key, body): __bucket.blob(key).upload_from_string(body)`);
|
|
2064
|
+
this.emit(`@staticmethod`);
|
|
2065
|
+
this.emit(`def download(key): return __bucket.blob(key).download_as_bytes()`);
|
|
2066
|
+
this.emit(`@staticmethod`);
|
|
2067
|
+
this.emit(`def remove(key): __bucket.blob(key).delete()`);
|
|
2068
|
+
this.indent--;
|
|
2069
|
+
this.emit(`storage = Storage()`);
|
|
2070
|
+
}
|
|
2071
|
+
this.emitRaw('');
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
// ===== PDF =====
|
|
2075
|
+
visitPdf(node) {
|
|
2076
|
+
const filename = this.rawString(node.filename);
|
|
2077
|
+
this.addFromImport('fpdf', 'FPDF');
|
|
2078
|
+
this.emitRaw('');
|
|
2079
|
+
this.emit(`__pdf = FPDF()`);
|
|
2080
|
+
this.emit(`__pdf.add_page()`);
|
|
2081
|
+
this.emit(`__pdf.set_auto_page_break(auto=True, margin=15)`);
|
|
2082
|
+
|
|
2083
|
+
for (const el of node.elements) {
|
|
2084
|
+
const tag = el.tag;
|
|
2085
|
+
const arg0 = el.args[0] ? this.rawString(el.args[0]) : '';
|
|
2086
|
+
if (tag === 'title') {
|
|
2087
|
+
this.emit(`__pdf.set_font('Helvetica', 'B', 24)`);
|
|
2088
|
+
this.emit(`__pdf.cell(0, 15, ${JSON.stringify(arg0)}, ln=True)`);
|
|
2089
|
+
} else if (tag === 'h1' || tag === 'h2' || tag === 'heading') {
|
|
2090
|
+
const size = tag === 'h1' ? 20 : 16;
|
|
2091
|
+
this.emit(`__pdf.set_font('Helvetica', 'B', ${size})`);
|
|
2092
|
+
this.emit(`__pdf.cell(0, 12, ${JSON.stringify(arg0)}, ln=True)`);
|
|
2093
|
+
} else if (tag === 'text' || tag === 'p') {
|
|
2094
|
+
this.emit(`__pdf.set_font('Helvetica', '', 12)`);
|
|
2095
|
+
this.emit(`__pdf.multi_cell(0, 8, ${JSON.stringify(arg0)})`);
|
|
2096
|
+
} else if (tag === 'image') {
|
|
2097
|
+
this.emit(`__pdf.image(${JSON.stringify(arg0)}, w=100)`);
|
|
2098
|
+
} else if (tag === 'line') {
|
|
2099
|
+
this.emit(`__pdf.line(10, __pdf.get_y(), 200, __pdf.get_y())`);
|
|
2100
|
+
} else {
|
|
2101
|
+
this.emit(`__pdf.set_font('Helvetica', '', 12)`);
|
|
2102
|
+
this.emit(`__pdf.cell(0, 8, ${JSON.stringify(arg0)}, ln=True)`);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
this.emit(`__pdf.output(${JSON.stringify(filename)})`);
|
|
2107
|
+
this.emit(`print(f"Generated: ${filename}")`);
|
|
2108
|
+
this.emitRaw('');
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// ===== i18n =====
|
|
2112
|
+
visitI18n(node) {
|
|
2113
|
+
const dir = this.rawString(node.dir);
|
|
2114
|
+
const defaultLang = node.defaultLang ? this.rawString(node.defaultLang) : 'en';
|
|
2115
|
+
|
|
2116
|
+
this.addImport('json');
|
|
2117
|
+
this.addImport('os');
|
|
2118
|
+
this.emitRaw('');
|
|
2119
|
+
this.emit(`__i18n_data = {}`);
|
|
2120
|
+
for (const lang of node.langs) {
|
|
2121
|
+
const code = this.rawString(lang.code);
|
|
2122
|
+
const file = this.rawString(lang.file);
|
|
2123
|
+
this.emit(`with open(os.path.join(${JSON.stringify(dir)}, ${JSON.stringify(file)})) as f:`);
|
|
2124
|
+
this.indent++;
|
|
2125
|
+
this.emit(`__i18n_data[${JSON.stringify(code)}] = json.load(f)`);
|
|
2126
|
+
this.indent--;
|
|
2127
|
+
}
|
|
2128
|
+
this.emit(`__i18n_lang = ${JSON.stringify(defaultLang)}`);
|
|
2129
|
+
this.emitRaw('');
|
|
2130
|
+
this.emit(`class I18n:`);
|
|
2131
|
+
this.indent++;
|
|
2132
|
+
this.emit(`@staticmethod`);
|
|
2133
|
+
this.emit(`def t(key, **params):`);
|
|
2134
|
+
this.indent++;
|
|
2135
|
+
this.emit(`data = __i18n_data.get(__i18n_lang, {})`);
|
|
2136
|
+
this.emit(`for k in key.split('.'): data = data.get(k, key) if isinstance(data, dict) else key`);
|
|
2137
|
+
this.emit(`text = str(data)`);
|
|
2138
|
+
this.emit(`for k, v in params.items(): text = text.replace('{' + k + '}', str(v))`);
|
|
2139
|
+
this.emit(`return text`);
|
|
2140
|
+
this.indent--;
|
|
2141
|
+
this.emit(`@staticmethod`);
|
|
2142
|
+
this.emit(`def set_lang(code): global __i18n_lang; __i18n_lang = code`);
|
|
2143
|
+
this.emit(`@staticmethod`);
|
|
2144
|
+
this.emit(`def get_lang(): return __i18n_lang`);
|
|
2145
|
+
this.indent--;
|
|
2146
|
+
this.emit(`i18n = I18n()`);
|
|
2147
|
+
this.emitRaw('');
|
|
2148
|
+
}
|
|
1958
2149
|
}
|
package/src/generator.js
CHANGED
|
@@ -148,6 +148,11 @@ export class Generator {
|
|
|
148
148
|
case 'MailConfig': return this.visitMail(node);
|
|
149
149
|
case 'DesktopApp': return this.visitDesktop(node);
|
|
150
150
|
case 'Screen': return this.visitScreen(node);
|
|
151
|
+
case 'OauthDecl': return this.visitOauth(node);
|
|
152
|
+
case 'PayDecl': return this.visitPay(node);
|
|
153
|
+
case 'StorageDecl': return this.visitStorage(node);
|
|
154
|
+
case 'PdfDecl': return this.visitPdf(node);
|
|
155
|
+
case 'I18nDecl': return this.visitI18n(node);
|
|
151
156
|
default:
|
|
152
157
|
this.emit(`/* unknown: ${node.type} */`);
|
|
153
158
|
}
|
|
@@ -1848,4 +1853,213 @@ export class Generator {
|
|
|
1848
1853
|
this.indent = savedIndent;
|
|
1849
1854
|
return result;
|
|
1850
1855
|
}
|
|
1856
|
+
|
|
1857
|
+
// ===== OAuth =====
|
|
1858
|
+
|
|
1859
|
+
visitOauth(node) {
|
|
1860
|
+
const provider = this.rawPageString(node.provider);
|
|
1861
|
+
const clientId = this.expr(node.clientId);
|
|
1862
|
+
const clientSecret = this.expr(node.clientSecret);
|
|
1863
|
+
const callback = node.callback ? this.rawPageString(node.callback) : '/auth/callback';
|
|
1864
|
+
const scope = node.scope ? this.rawPageString(node.scope) : 'email profile';
|
|
1865
|
+
|
|
1866
|
+
if (provider === 'google') {
|
|
1867
|
+
this.emit(`import passport from 'passport';`);
|
|
1868
|
+
this.emit(`import { Strategy as GoogleStrategy } from 'passport-google-oauth20';`);
|
|
1869
|
+
this.emitRaw('');
|
|
1870
|
+
this.emit(`passport.use(new GoogleStrategy({`);
|
|
1871
|
+
this.indent++;
|
|
1872
|
+
this.emit(`clientID: ${clientId},`);
|
|
1873
|
+
this.emit(`clientSecret: ${clientSecret},`);
|
|
1874
|
+
this.emit(`callbackURL: ${JSON.stringify(callback)},`);
|
|
1875
|
+
this.indent--;
|
|
1876
|
+
this.emit(`}, (accessToken, refreshToken, profile, done) => done(null, profile)));`);
|
|
1877
|
+
} else if (provider === 'github') {
|
|
1878
|
+
this.emit(`import passport from 'passport';`);
|
|
1879
|
+
this.emit(`import { Strategy as GitHubStrategy } from 'passport-github2';`);
|
|
1880
|
+
this.emitRaw('');
|
|
1881
|
+
this.emit(`passport.use(new GitHubStrategy({`);
|
|
1882
|
+
this.indent++;
|
|
1883
|
+
this.emit(`clientID: ${clientId},`);
|
|
1884
|
+
this.emit(`clientSecret: ${clientSecret},`);
|
|
1885
|
+
this.emit(`callbackURL: ${JSON.stringify(callback)},`);
|
|
1886
|
+
this.indent--;
|
|
1887
|
+
this.emit(`}, (accessToken, refreshToken, profile, done) => done(null, profile)));`);
|
|
1888
|
+
} else {
|
|
1889
|
+
this.emit(`import passport from 'passport';`);
|
|
1890
|
+
this.emit(`// Configure ${provider} OAuth strategy`);
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
this.emit(`passport.serializeUser((user, done) => done(null, user));`);
|
|
1894
|
+
this.emit(`passport.deserializeUser((user, done) => done(null, user));`);
|
|
1895
|
+
this.emitRaw('');
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// ===== Pay (Stripe) =====
|
|
1899
|
+
|
|
1900
|
+
visitPay(node) {
|
|
1901
|
+
const provider = this.rawPageString(node.provider);
|
|
1902
|
+
const secretKey = this.expr(node.secretKey);
|
|
1903
|
+
const webhook = node.webhook ? this.rawPageString(node.webhook) : '/webhook';
|
|
1904
|
+
|
|
1905
|
+
if (provider === 'stripe') {
|
|
1906
|
+
this.emit(`import Stripe from 'stripe';`);
|
|
1907
|
+
this.emit(`const stripe = new Stripe(${secretKey});`);
|
|
1908
|
+
this.emitRaw('');
|
|
1909
|
+
this.emit(`const pay = {`);
|
|
1910
|
+
this.indent++;
|
|
1911
|
+
this.emit(`async checkout(items, successUrl, cancelUrl) {`);
|
|
1912
|
+
this.indent++;
|
|
1913
|
+
this.emit(`return stripe.checkout.sessions.create({`);
|
|
1914
|
+
this.indent++;
|
|
1915
|
+
this.emit(`line_items: items.map(i => ({ price_data: { currency: 'usd', product_data: { name: i.name }, unit_amount: i.price }, quantity: i.qty || 1 })),`);
|
|
1916
|
+
this.emit(`mode: 'payment',`);
|
|
1917
|
+
this.emit(`success_url: successUrl || ${node.successUrl ? this.expr(node.successUrl) : "'http://localhost:3000/success'"},`);
|
|
1918
|
+
this.emit(`cancel_url: cancelUrl || ${node.cancelUrl ? this.expr(node.cancelUrl) : "'http://localhost:3000/cancel'"},`);
|
|
1919
|
+
this.indent--;
|
|
1920
|
+
this.emit(`});`);
|
|
1921
|
+
this.indent--;
|
|
1922
|
+
this.emit(`},`);
|
|
1923
|
+
this.emit(`async verify(body, sig) {`);
|
|
1924
|
+
this.indent++;
|
|
1925
|
+
this.emit(`return stripe.webhooks.constructEvent(body, sig, ${secretKey});`);
|
|
1926
|
+
this.indent--;
|
|
1927
|
+
this.emit(`},`);
|
|
1928
|
+
this.indent--;
|
|
1929
|
+
this.emit(`};`);
|
|
1930
|
+
} else {
|
|
1931
|
+
this.emit(`// ${provider} payment integration`);
|
|
1932
|
+
this.emit(`const pay = {};`);
|
|
1933
|
+
}
|
|
1934
|
+
this.emitRaw('');
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
// ===== Storage (S3) =====
|
|
1938
|
+
|
|
1939
|
+
visitStorage(node) {
|
|
1940
|
+
const provider = this.rawPageString(node.provider);
|
|
1941
|
+
const bucket = this.expr(node.bucket);
|
|
1942
|
+
const accessKey = this.expr(node.accessKey);
|
|
1943
|
+
const secretKey = this.expr(node.secretKey);
|
|
1944
|
+
const region = node.region ? this.rawPageString(node.region) : 'us-east-1';
|
|
1945
|
+
|
|
1946
|
+
if (provider === 's3') {
|
|
1947
|
+
this.emit(`import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';`);
|
|
1948
|
+
this.emitRaw('');
|
|
1949
|
+
this.emit(`const __s3 = new S3Client({`);
|
|
1950
|
+
this.indent++;
|
|
1951
|
+
this.emit(`region: ${JSON.stringify(region)},`);
|
|
1952
|
+
this.emit(`credentials: { accessKeyId: ${accessKey}, secretAccessKey: ${secretKey} },`);
|
|
1953
|
+
this.indent--;
|
|
1954
|
+
this.emit(`});`);
|
|
1955
|
+
this.emitRaw('');
|
|
1956
|
+
this.emit(`const storage = {`);
|
|
1957
|
+
this.indent++;
|
|
1958
|
+
this.emit(`async upload(key, body, contentType = 'application/octet-stream') {`);
|
|
1959
|
+
this.indent++;
|
|
1960
|
+
this.emit(`return __s3.send(new PutObjectCommand({ Bucket: ${bucket}, Key: key, Body: body, ContentType: contentType }));`);
|
|
1961
|
+
this.indent--;
|
|
1962
|
+
this.emit(`},`);
|
|
1963
|
+
this.emit(`async download(key) {`);
|
|
1964
|
+
this.indent++;
|
|
1965
|
+
this.emit(`const res = await __s3.send(new GetObjectCommand({ Bucket: ${bucket}, Key: key }));`);
|
|
1966
|
+
this.emit(`return res.Body;`);
|
|
1967
|
+
this.indent--;
|
|
1968
|
+
this.emit(`},`);
|
|
1969
|
+
this.emit(`async remove(key) {`);
|
|
1970
|
+
this.indent++;
|
|
1971
|
+
this.emit(`return __s3.send(new DeleteObjectCommand({ Bucket: ${bucket}, Key: key }));`);
|
|
1972
|
+
this.indent--;
|
|
1973
|
+
this.emit(`},`);
|
|
1974
|
+
this.indent--;
|
|
1975
|
+
this.emit(`};`);
|
|
1976
|
+
} else if (provider === 'gcs') {
|
|
1977
|
+
this.emit(`import { Storage } from '@google-cloud/storage';`);
|
|
1978
|
+
this.emit(`const __gcs = new Storage();`);
|
|
1979
|
+
this.emit(`const __bucket = __gcs.bucket(${bucket});`);
|
|
1980
|
+
this.emit(`const storage = {`);
|
|
1981
|
+
this.indent++;
|
|
1982
|
+
this.emit(`async upload(key, body) { await __bucket.file(key).save(body); },`);
|
|
1983
|
+
this.emit(`async download(key) { const [buf] = await __bucket.file(key).download(); return buf; },`);
|
|
1984
|
+
this.emit(`async remove(key) { await __bucket.file(key).delete(); },`);
|
|
1985
|
+
this.indent--;
|
|
1986
|
+
this.emit(`};`);
|
|
1987
|
+
} else {
|
|
1988
|
+
this.emit(`// ${provider} storage integration`);
|
|
1989
|
+
this.emit(`const storage = {};`);
|
|
1990
|
+
}
|
|
1991
|
+
this.emitRaw('');
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// ===== PDF =====
|
|
1995
|
+
|
|
1996
|
+
visitPdf(node) {
|
|
1997
|
+
const filename = this.rawPageString(node.filename);
|
|
1998
|
+
this.emit(`import PDFDocument from 'pdfkit';`);
|
|
1999
|
+
this.emit(`import { createWriteStream } from 'fs';`);
|
|
2000
|
+
this.emitRaw('');
|
|
2001
|
+
this.emit(`const __pdf = new PDFDocument();`);
|
|
2002
|
+
this.emit(`__pdf.pipe(createWriteStream(${JSON.stringify(filename)}));`);
|
|
2003
|
+
|
|
2004
|
+
for (const el of node.elements) {
|
|
2005
|
+
const tag = el.tag;
|
|
2006
|
+
const arg0 = el.args[0] ? (el.args[0].raw !== undefined ? JSON.stringify(el.args[0].raw || el.args[0].parts?.map(p => p.value).join('')) : this.expr(el.args[0])) : '""';
|
|
2007
|
+
if (tag === 'title') {
|
|
2008
|
+
this.emit(`__pdf.fontSize(24).text(${arg0});`);
|
|
2009
|
+
this.emit(`__pdf.moveDown();`);
|
|
2010
|
+
} else if (tag === 'heading' || tag === 'h1' || tag === 'h2') {
|
|
2011
|
+
const size = tag === 'h1' ? 20 : 16;
|
|
2012
|
+
this.emit(`__pdf.fontSize(${size}).text(${arg0});`);
|
|
2013
|
+
this.emit(`__pdf.moveDown();`);
|
|
2014
|
+
} else if (tag === 'text' || tag === 'p') {
|
|
2015
|
+
this.emit(`__pdf.fontSize(12).text(${arg0});`);
|
|
2016
|
+
} else if (tag === 'image') {
|
|
2017
|
+
this.emit(`__pdf.image(${arg0}, { width: 300 });`);
|
|
2018
|
+
} else if (tag === 'line') {
|
|
2019
|
+
this.emit(`__pdf.moveTo(50, __pdf.y).lineTo(550, __pdf.y).stroke();`);
|
|
2020
|
+
this.emit(`__pdf.moveDown();`);
|
|
2021
|
+
} else if (tag === 'table') {
|
|
2022
|
+
this.emit(`// table: ${arg0}`);
|
|
2023
|
+
} else {
|
|
2024
|
+
this.emit(`__pdf.text(${arg0});`);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
this.emit(`__pdf.end();`);
|
|
2029
|
+
this.emit(`console.log('Generated: ${filename}');`);
|
|
2030
|
+
this.emitRaw('');
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
// ===== i18n =====
|
|
2034
|
+
|
|
2035
|
+
visitI18n(node) {
|
|
2036
|
+
const dir = this.rawPageString(node.dir);
|
|
2037
|
+
const defaultLang = node.defaultLang ? this.rawPageString(node.defaultLang) : 'en';
|
|
2038
|
+
|
|
2039
|
+
this.emit(`import { readFileSync } from 'fs';`);
|
|
2040
|
+
this.emit(`import { join } from 'path';`);
|
|
2041
|
+
this.emitRaw('');
|
|
2042
|
+
this.emit(`const __i18nData = {};`);
|
|
2043
|
+
for (const lang of node.langs) {
|
|
2044
|
+
const code = this.rawPageString(lang.code);
|
|
2045
|
+
const file = this.rawPageString(lang.file);
|
|
2046
|
+
this.emit(`__i18nData[${JSON.stringify(code)}] = JSON.parse(readFileSync(join(${JSON.stringify(dir)}, ${JSON.stringify(file)}), 'utf-8'));`);
|
|
2047
|
+
}
|
|
2048
|
+
this.emit(`let __i18nLang = ${JSON.stringify(defaultLang)};`);
|
|
2049
|
+
this.emitRaw('');
|
|
2050
|
+
this.emit(`const i18n = {`);
|
|
2051
|
+
this.indent++;
|
|
2052
|
+
this.emit(`t(key, params = {}) {`);
|
|
2053
|
+
this.indent++;
|
|
2054
|
+
this.emit(`let text = key.split('.').reduce((o, k) => o?.[k], __i18nData[__i18nLang]) || key;`);
|
|
2055
|
+
this.emit(`for (const [k, v] of Object.entries(params)) text = text.replace(new RegExp(\`{$\{k}}\`, 'g'), v);`);
|
|
2056
|
+
this.emit(`return text;`);
|
|
2057
|
+
this.indent--;
|
|
2058
|
+
this.emit(`},`);
|
|
2059
|
+
this.emit(`setLang(code) { __i18nLang = code; },`);
|
|
2060
|
+
this.emit(`getLang() { return __i18nLang; },`);
|
|
2061
|
+
this.indent--;
|
|
2062
|
+
this.emit(`};`);
|
|
2063
|
+
this.emitRaw('');
|
|
2064
|
+
}
|
|
1851
2065
|
}
|
package/src/parser.js
CHANGED
|
@@ -111,7 +111,9 @@ export class Parser {
|
|
|
111
111
|
type === T.TYPEOF || type === T.INSTANCEOF || type === T.ENSURE ||
|
|
112
112
|
type === T.MODEL || type === T.PROMPT ||
|
|
113
113
|
type === T.PAGE || type === T.CLI_APP || type === T.MAIL ||
|
|
114
|
-
type === T.GRAPHQL || type === T.DESKTOP || type === T.SCREEN
|
|
114
|
+
type === T.GRAPHQL || type === T.DESKTOP || type === T.SCREEN ||
|
|
115
|
+
type === T.OAUTH || type === T.PAY || type === T.STORAGE ||
|
|
116
|
+
type === T.PDF || type === T.I18N;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
expectPropertyName() {
|
|
@@ -173,6 +175,11 @@ export class Parser {
|
|
|
173
175
|
case T.MAIL: return this.parseMail();
|
|
174
176
|
case T.DESKTOP: return this.parseDesktop();
|
|
175
177
|
case T.SCREEN: return this.parseScreen();
|
|
178
|
+
case T.OAUTH: return this.parseOauth();
|
|
179
|
+
case T.PAY: return this.parsePay();
|
|
180
|
+
case T.STORAGE: return this.parseStorage();
|
|
181
|
+
case T.PDF: return this.parsePdf();
|
|
182
|
+
case T.I18N: return this.parseI18n();
|
|
176
183
|
case T.MODEL: return this.parseModel();
|
|
177
184
|
case T.ON: return this.parseOn();
|
|
178
185
|
case T.LOG: return this.parseLog();
|
|
@@ -1148,6 +1155,7 @@ export class Parser {
|
|
|
1148
1155
|
case T.PROMPT:
|
|
1149
1156
|
case T.PAGE: case T.CLI_APP: case T.MAIL:
|
|
1150
1157
|
case T.GRAPHQL: case T.DESKTOP: case T.SCREEN:
|
|
1158
|
+
case T.OAUTH: case T.PAY: case T.STORAGE: case T.PDF: case T.I18N:
|
|
1151
1159
|
case T.FROM: case T.AS: case T.IN:
|
|
1152
1160
|
this.advance();
|
|
1153
1161
|
return new ASTNode('Identifier', { name: tok.value });
|
|
@@ -1935,4 +1943,129 @@ export class Parser {
|
|
|
1935
1943
|
}
|
|
1936
1944
|
return new ASTNode('ScreenElement', { tag, args, body });
|
|
1937
1945
|
}
|
|
1946
|
+
|
|
1947
|
+
// oauth "google" clientId clientSecret:
|
|
1948
|
+
// callback "/auth/callback"
|
|
1949
|
+
// scope "email profile"
|
|
1950
|
+
parseOauth() {
|
|
1951
|
+
this.expect(T.OAUTH);
|
|
1952
|
+
const provider = this.parseString();
|
|
1953
|
+
const clientId = this.parseExpression();
|
|
1954
|
+
const clientSecret = this.parseExpression();
|
|
1955
|
+
this.expect(T.COLON);
|
|
1956
|
+
this.skipNewlines();
|
|
1957
|
+
this.expect(T.INDENT);
|
|
1958
|
+
let callback = null, scope = null;
|
|
1959
|
+
while (!this.at(T.DEDENT) && !this.at(T.EOF)) {
|
|
1960
|
+
this.skipNewlines();
|
|
1961
|
+
if (this.at(T.DEDENT) || this.at(T.EOF)) break;
|
|
1962
|
+
const kw = this.peek().value;
|
|
1963
|
+
if (kw === 'callback') { this.advance(); callback = this.parseString(); }
|
|
1964
|
+
else if (kw === 'scope') { this.advance(); scope = this.parseString(); }
|
|
1965
|
+
else { this.advance(); }
|
|
1966
|
+
this.skipNewlines();
|
|
1967
|
+
}
|
|
1968
|
+
if (this.at(T.DEDENT)) this.advance();
|
|
1969
|
+
return new ASTNode('OauthDecl', { provider, clientId, clientSecret, callback, scope });
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// pay "stripe" secretKey:
|
|
1973
|
+
// webhook "/webhook"
|
|
1974
|
+
parsePay() {
|
|
1975
|
+
this.expect(T.PAY);
|
|
1976
|
+
const provider = this.parseString();
|
|
1977
|
+
const secretKey = this.parseExpression();
|
|
1978
|
+
this.expect(T.COLON);
|
|
1979
|
+
this.skipNewlines();
|
|
1980
|
+
this.expect(T.INDENT);
|
|
1981
|
+
let webhook = null, successUrl = null, cancelUrl = null;
|
|
1982
|
+
while (!this.at(T.DEDENT) && !this.at(T.EOF)) {
|
|
1983
|
+
this.skipNewlines();
|
|
1984
|
+
if (this.at(T.DEDENT) || this.at(T.EOF)) break;
|
|
1985
|
+
const kw = this.peek().value;
|
|
1986
|
+
if (kw === 'webhook') { this.advance(); webhook = this.parseString(); }
|
|
1987
|
+
else if (kw === 'success') { this.advance(); successUrl = this.parseString(); }
|
|
1988
|
+
else if (kw === 'cancel') { this.advance(); cancelUrl = this.parseString(); }
|
|
1989
|
+
else { this.advance(); }
|
|
1990
|
+
this.skipNewlines();
|
|
1991
|
+
}
|
|
1992
|
+
if (this.at(T.DEDENT)) this.advance();
|
|
1993
|
+
return new ASTNode('PayDecl', { provider, secretKey, webhook, successUrl, cancelUrl });
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// storage "s3" bucket accessKey secretKey:
|
|
1997
|
+
// region "ap-northeast-1"
|
|
1998
|
+
parseStorage() {
|
|
1999
|
+
this.expect(T.STORAGE);
|
|
2000
|
+
const provider = this.parseString();
|
|
2001
|
+
const bucket = this.parseExpression();
|
|
2002
|
+
const accessKey = this.parseExpression();
|
|
2003
|
+
const secretKey = this.parseExpression();
|
|
2004
|
+
this.expect(T.COLON);
|
|
2005
|
+
this.skipNewlines();
|
|
2006
|
+
this.expect(T.INDENT);
|
|
2007
|
+
let region = null;
|
|
2008
|
+
while (!this.at(T.DEDENT) && !this.at(T.EOF)) {
|
|
2009
|
+
this.skipNewlines();
|
|
2010
|
+
if (this.at(T.DEDENT) || this.at(T.EOF)) break;
|
|
2011
|
+
const kw = this.peek().value;
|
|
2012
|
+
if (kw === 'region') { this.advance(); region = this.parseString(); }
|
|
2013
|
+
else { this.advance(); }
|
|
2014
|
+
this.skipNewlines();
|
|
2015
|
+
}
|
|
2016
|
+
if (this.at(T.DEDENT)) this.advance();
|
|
2017
|
+
return new ASTNode('StorageDecl', { provider, bucket, accessKey, secretKey, region });
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// pdf "output.pdf":
|
|
2021
|
+
// title "My Document"
|
|
2022
|
+
// text "Hello World"
|
|
2023
|
+
// table data
|
|
2024
|
+
parsePdf() {
|
|
2025
|
+
this.expect(T.PDF);
|
|
2026
|
+
const filename = this.parseString();
|
|
2027
|
+
this.expect(T.COLON);
|
|
2028
|
+
this.skipNewlines();
|
|
2029
|
+
this.expect(T.INDENT);
|
|
2030
|
+
const elements = [];
|
|
2031
|
+
while (!this.at(T.DEDENT) && !this.at(T.EOF)) {
|
|
2032
|
+
this.skipNewlines();
|
|
2033
|
+
if (this.at(T.DEDENT) || this.at(T.EOF)) break;
|
|
2034
|
+
const tag = this.advance().value;
|
|
2035
|
+
const args = [];
|
|
2036
|
+
while (this.at(T.STRING)) args.push(this.parseString());
|
|
2037
|
+
if (args.length === 0 && !this.at(T.NEWLINE) && !this.at(T.DEDENT) && !this.at(T.EOF)) {
|
|
2038
|
+
args.push(this.parseExpression());
|
|
2039
|
+
}
|
|
2040
|
+
elements.push({ tag, args });
|
|
2041
|
+
this.skipNewlines();
|
|
2042
|
+
}
|
|
2043
|
+
if (this.at(T.DEDENT)) this.advance();
|
|
2044
|
+
return new ASTNode('PdfDecl', { filename, elements });
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// i18n "locales/":
|
|
2048
|
+
// default "en"
|
|
2049
|
+
// lang "ja" "jp.json"
|
|
2050
|
+
// lang "en" "en.json"
|
|
2051
|
+
parseI18n() {
|
|
2052
|
+
this.expect(T.I18N);
|
|
2053
|
+
const dir = this.parseString();
|
|
2054
|
+
this.expect(T.COLON);
|
|
2055
|
+
this.skipNewlines();
|
|
2056
|
+
this.expect(T.INDENT);
|
|
2057
|
+
let defaultLang = null;
|
|
2058
|
+
const langs = [];
|
|
2059
|
+
while (!this.at(T.DEDENT) && !this.at(T.EOF)) {
|
|
2060
|
+
this.skipNewlines();
|
|
2061
|
+
if (this.at(T.DEDENT) || this.at(T.EOF)) break;
|
|
2062
|
+
const kw = this.peek().value;
|
|
2063
|
+
if (kw === 'default') { this.advance(); defaultLang = this.parseString(); }
|
|
2064
|
+
else if (kw === 'lang') { this.advance(); const code = this.parseString(); const file = this.parseString(); langs.push({ code, file }); }
|
|
2065
|
+
else { this.advance(); }
|
|
2066
|
+
this.skipNewlines();
|
|
2067
|
+
}
|
|
2068
|
+
if (this.at(T.DEDENT)) this.advance();
|
|
2069
|
+
return new ASTNode('I18nDecl', { dir, defaultLang, langs });
|
|
2070
|
+
}
|
|
1938
2071
|
}
|
package/src/tokens.js
CHANGED
|
@@ -93,6 +93,11 @@ export const T = {
|
|
|
93
93
|
GRAPHQL: 'GRAPHQL',
|
|
94
94
|
DESKTOP: 'DESKTOP',
|
|
95
95
|
SCREEN: 'SCREEN',
|
|
96
|
+
OAUTH: 'OAUTH',
|
|
97
|
+
PAY: 'PAY',
|
|
98
|
+
STORAGE: 'STORAGE',
|
|
99
|
+
PDF: 'PDF',
|
|
100
|
+
I18N: 'I18N',
|
|
96
101
|
|
|
97
102
|
// Operators
|
|
98
103
|
ASSIGN: 'ASSIGN',
|
|
@@ -214,6 +219,11 @@ export const KEYWORDS = {
|
|
|
214
219
|
'graphql': T.GRAPHQL,
|
|
215
220
|
'desktop': T.DESKTOP,
|
|
216
221
|
'screen': T.SCREEN,
|
|
222
|
+
'oauth': T.OAUTH,
|
|
223
|
+
'pay': T.PAY,
|
|
224
|
+
'storage': T.STORAGE,
|
|
225
|
+
'pdf': T.PDF,
|
|
226
|
+
'i18n': T.I18N,
|
|
217
227
|
'true': T.BOOL,
|
|
218
228
|
'false': T.BOOL,
|
|
219
229
|
'null': T.NULL,
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"name": "keyword.control.naide"
|
|
88
88
|
},
|
|
89
89
|
"keywords-server": {
|
|
90
|
-
"match": "\\b(server|bot|slash|get|post|put|del|patch|mid|cors|limit|auth|crud|static|ws|sse|group|upload|cookie|session|view|cache|validate|openapi|error|on|page|cli|mail|graphql|desktop|screen)\\b",
|
|
90
|
+
"match": "\\b(server|bot|slash|get|post|put|del|patch|mid|cors|limit|auth|crud|static|ws|sse|group|upload|cookie|session|view|cache|validate|openapi|error|on|page|cli|mail|graphql|desktop|screen|oauth|pay|storage|pdf|i18n)\\b",
|
|
91
91
|
"name": "keyword.other.naide"
|
|
92
92
|
},
|
|
93
93
|
"keywords-declaration": {
|