tova 0.1.1 → 0.2.2
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/LICENSE +1 -1
- package/README.md +2 -0
- package/bin/tova.js +811 -154
- package/package.json +8 -2
- package/src/analyzer/analyzer.js +297 -58
- package/src/analyzer/scope.js +38 -1
- package/src/analyzer/type-registry.js +72 -0
- package/src/analyzer/types.js +478 -0
- package/src/codegen/base-codegen.js +371 -0
- package/src/codegen/client-codegen.js +62 -10
- package/src/codegen/codegen.js +111 -2
- package/src/codegen/server-codegen.js +175 -3
- package/src/config/edit-toml.js +100 -0
- package/src/config/package-json.js +52 -0
- package/src/config/resolve.js +100 -0
- package/src/config/toml.js +209 -0
- package/src/lexer/lexer.js +2 -2
- package/src/lsp/server.js +284 -30
- package/src/parser/ast.js +105 -0
- package/src/parser/parser.js +202 -2
- package/src/runtime/ai.js +305 -0
- package/src/runtime/devtools.js +228 -0
- package/src/runtime/embedded.js +3 -1
- package/src/runtime/io.js +240 -0
- package/src/runtime/reactivity.js +264 -19
- package/src/runtime/ssr.js +196 -24
- package/src/runtime/table.js +522 -0
- package/src/stdlib/collections.js +245 -0
- package/src/stdlib/core.js +87 -0
- package/src/stdlib/datetime.js +88 -0
- package/src/stdlib/encoding.js +35 -0
- package/src/stdlib/functional.js +82 -0
- package/src/stdlib/inline.js +334 -67
- package/src/stdlib/math.js +93 -0
- package/src/stdlib/string.js +95 -0
- package/src/stdlib/url.js +33 -0
- package/src/stdlib/validation.js +29 -0
package/src/stdlib/string.js
CHANGED
|
@@ -98,3 +98,98 @@ export function camel_case(str) {
|
|
|
98
98
|
.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '')
|
|
99
99
|
.replace(/^[A-Z]/, c => c.toLowerCase());
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
export function index_of(s, sub) {
|
|
103
|
+
const i = s.indexOf(sub);
|
|
104
|
+
return i === -1 ? null : i;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function last_index_of(s, sub) {
|
|
108
|
+
const i = s.lastIndexOf(sub);
|
|
109
|
+
return i === -1 ? null : i;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function count_of(s, sub) {
|
|
113
|
+
if (!sub) return 0;
|
|
114
|
+
let c = 0, i = 0;
|
|
115
|
+
while ((i = s.indexOf(sub, i)) !== -1) { c++; i += sub.length; }
|
|
116
|
+
return c;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function reverse_str(s) {
|
|
120
|
+
return [...s].reverse().join('');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function substr(s, start, end) {
|
|
124
|
+
return end === undefined ? s.slice(start) : s.slice(start, end);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function kebab_case(s) {
|
|
128
|
+
return s
|
|
129
|
+
.replace(/[-\s]+/g, '-')
|
|
130
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
131
|
+
.toLowerCase()
|
|
132
|
+
.replace(/^-/, '');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function center(s, n, fill) {
|
|
136
|
+
if (s.length >= n) return s;
|
|
137
|
+
const f = fill || ' ';
|
|
138
|
+
const total = n - s.length;
|
|
139
|
+
const left = Math.floor(total / 2);
|
|
140
|
+
const right = total - left;
|
|
141
|
+
return f.repeat(Math.ceil(left / f.length)).slice(0, left) + s + f.repeat(Math.ceil(right / f.length)).slice(0, right);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Text Utilities ────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
export function truncate(s, n, suffix) {
|
|
147
|
+
const sf = suffix !== undefined ? suffix : '...';
|
|
148
|
+
return s.length <= n ? s : s.slice(0, n - sf.length) + sf;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function word_wrap(s, width) {
|
|
152
|
+
const ws = s.split(' ');
|
|
153
|
+
const lines = [];
|
|
154
|
+
let line = '';
|
|
155
|
+
for (const w of ws) {
|
|
156
|
+
if (line && (line.length + 1 + w.length) > width) {
|
|
157
|
+
lines.push(line);
|
|
158
|
+
line = w;
|
|
159
|
+
} else {
|
|
160
|
+
line = line ? line + ' ' + w : w;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (line) lines.push(line);
|
|
164
|
+
return lines.join('\n');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function dedent(s) {
|
|
168
|
+
const lines = s.split('\n');
|
|
169
|
+
const nonEmpty = lines.filter(l => l.trim().length > 0);
|
|
170
|
+
if (nonEmpty.length === 0) return s;
|
|
171
|
+
const indent = Math.min(...nonEmpty.map(l => l.match(/^(\s*)/)[1].length));
|
|
172
|
+
return lines.map(l => l.slice(indent)).join('\n');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function indent_str(s, n, ch) {
|
|
176
|
+
const prefix = (ch || ' ').repeat(n);
|
|
177
|
+
return s.split('\n').map(l => prefix + l).join('\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function slugify(s) {
|
|
181
|
+
return s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function escape_html(s) {
|
|
185
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function unescape_html(s) {
|
|
189
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function fmt(template, ...args) {
|
|
193
|
+
let i = 0;
|
|
194
|
+
return template.replace(/\{\}/g, () => i < args.length ? String(args[i++]) : '{}');
|
|
195
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Tova standard library — URL utilities
|
|
2
|
+
|
|
3
|
+
export function parse_url(s) {
|
|
4
|
+
try {
|
|
5
|
+
const u = new URL(s);
|
|
6
|
+
return { __tag: 'Ok', value: { protocol: u.protocol.replace(':', ''), host: u.host, pathname: u.pathname, search: u.search, hash: u.hash }, map(fn) { return { __tag: 'Ok', value: fn(this.value), map: this.map, unwrap() { return fn(this.value); }, isOk() { return true; }, isErr() { return false; } }; }, unwrap() { return this.value; }, isOk() { return true; }, isErr() { return false; } };
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return { __tag: 'Err', error: 'Invalid URL: ' + s, map(_) { return this; }, unwrap() { throw new Error(this.error); }, isOk() { return false; }, isErr() { return true; } };
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function build_url(parts) {
|
|
13
|
+
let url = (parts.protocol || 'https') + '://' + (parts.host || '');
|
|
14
|
+
url += parts.pathname || '/';
|
|
15
|
+
if (parts.search) url += (parts.search.startsWith('?') ? '' : '?') + parts.search;
|
|
16
|
+
if (parts.hash) url += (parts.hash.startsWith('#') ? '' : '#') + parts.hash;
|
|
17
|
+
return url;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parse_query(s) {
|
|
21
|
+
const r = {};
|
|
22
|
+
const qs = s.startsWith('?') ? s.slice(1) : s;
|
|
23
|
+
if (!qs) return r;
|
|
24
|
+
for (const pair of qs.split('&')) {
|
|
25
|
+
const [k, ...v] = pair.split('=');
|
|
26
|
+
r[decodeURIComponent(k)] = decodeURIComponent(v.join('='));
|
|
27
|
+
}
|
|
28
|
+
return r;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function build_query(obj) {
|
|
32
|
+
return Object.entries(obj).map(([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v)).join('&');
|
|
33
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Tova standard library — validation utilities
|
|
2
|
+
|
|
3
|
+
export function is_email(s) {
|
|
4
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function is_url(s) {
|
|
8
|
+
try { new URL(s); return true; } catch { return false; }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function is_numeric(s) {
|
|
12
|
+
return typeof s === 'string' && s.length > 0 && !isNaN(Number(s));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function is_alpha(s) {
|
|
16
|
+
return /^[a-zA-Z]+$/.test(s);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function is_alphanumeric(s) {
|
|
20
|
+
return /^[a-zA-Z0-9]+$/.test(s);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function is_uuid(s) {
|
|
24
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function is_hex(s) {
|
|
28
|
+
return /^[0-9a-fA-F]+$/.test(s);
|
|
29
|
+
}
|