future-lang 0.3.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.
Files changed (47) hide show
  1. package/ARCHITECTURE.md +424 -0
  2. package/MIGRATION.md +365 -0
  3. package/README.md +370 -0
  4. package/ROADMAP.md +263 -0
  5. package/examples/adult.future +8 -0
  6. package/examples/api.future +11 -0
  7. package/examples/assistant.future +8 -0
  8. package/examples/browser-demo.html +164 -0
  9. package/examples/greet.future +7 -0
  10. package/examples/hello.future +1 -0
  11. package/examples/math.future +8 -0
  12. package/examples/mini-app.html +301 -0
  13. package/examples/smarthome.future +10 -0
  14. package/future-browser.js +102 -0
  15. package/future-playground.html +650 -0
  16. package/package.json +27 -0
  17. package/runtime/ai.js +92 -0
  18. package/runtime/browser.js +458 -0
  19. package/runtime/device.js +36 -0
  20. package/runtime/home.js +19 -0
  21. package/runtime/http.js +32 -0
  22. package/runtime/index.js +403 -0
  23. package/runtime/lsp-metadata.js +104 -0
  24. package/runtime/math.js +16 -0
  25. package/runtime/memory.js +61 -0
  26. package/runtime/mqtt.js +49 -0
  27. package/runtime/providers/anthropic.js +59 -0
  28. package/runtime/providers/index.js +93 -0
  29. package/runtime/providers/openai-compat.js +85 -0
  30. package/runtime/providers/util.js +70 -0
  31. package/runtime/rag/chunker.js +65 -0
  32. package/runtime/rag/pipeline.js +86 -0
  33. package/runtime/rag/vector-store.js +119 -0
  34. package/runtime/rag.js +94 -0
  35. package/runtime/schedule.js +77 -0
  36. package/runtime/system.js +101 -0
  37. package/runtime/tts.js +38 -0
  38. package/runtime/vision.js +85 -0
  39. package/server.js +42 -0
  40. package/src/ast.js +202 -0
  41. package/src/cli.js +391 -0
  42. package/src/errors.js +21 -0
  43. package/src/formatter.js +48 -0
  44. package/src/generator.js +457 -0
  45. package/src/index.js +48 -0
  46. package/src/lexer.js +248 -0
  47. package/src/parser.js +469 -0
@@ -0,0 +1,164 @@
1
+ <!DOCTYPE html>
2
+ <html lang="pt">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Future — Browser Demo</title>
6
+ <style>
7
+ body { font-family: monospace; max-width: 700px; margin: 2rem auto; padding: 0 1rem; background: #0d1117; color: #e6edf3; }
8
+ h1 { color: #58a6ff; }
9
+ h2 { color: #8b949e; font-size: 0.9rem; font-weight: normal; margin-top: 2rem; }
10
+ pre { background: #161b22; padding: 1rem; border-radius: 6px; border: 1px solid #30363d; overflow-x: auto; }
11
+ #output { background: #0d1117; border: 1px solid #30363d; border-radius: 6px;
12
+ padding: 1rem; min-height: 60px; white-space: pre-wrap; color: #3fb950; }
13
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.75rem; font-weight: bold; }
14
+ .demo { background: #2d1b00; color: #f0a500; }
15
+ .proxy { background: #0d2b00; color: #3fb950; }
16
+ code { font-family: monospace; }
17
+ </style>
18
+ </head>
19
+ <body>
20
+
21
+ <h1>Future Language — Browser Runtime</h1>
22
+
23
+ <!-- Step 1: load the Future runtime -->
24
+ <script type="module" src="../future-browser.js"></script>
25
+
26
+ <!-- Step 2: configure (runs after future-browser.js, same module tick) -->
27
+ <script type="module">
28
+ import Future from '../future-browser.js';
29
+
30
+ // ── Option A: Proxy mode (production — API key stays on your server) ────────
31
+ // Future.configure({ proxy: '/api/ai' })
32
+
33
+ // ── Option B: Demo mode (dev only — key visible in source) ──────────────────
34
+ // Future.configure({ provider: 'openai', apiKey: 'sk-...' })
35
+ // Future.configure({ provider: 'anthropic', apiKey: 'sk-ant-...' })
36
+ // Future.configure({ provider: 'ollama' }) // local, no key needed
37
+
38
+ // ── Redirect print output to the page ───────────────────────────────────────
39
+ const out = document.getElementById('output');
40
+ Future.runtime.print = (...args) => {
41
+ const line = args.join(' ');
42
+ console.log(line);
43
+ if (out) out.textContent += line + '\n';
44
+ };
45
+ </script>
46
+
47
+ <!-- ─── Example 1: Basic — no AI needed ──────────────────────────────────── -->
48
+ <h2>Example 1 — Basic (no AI required)</h2>
49
+ <pre><code>names = ["Alice", "Bob", "Carlos"]
50
+ for name in names
51
+ print "Olá, {name}!"
52
+ end
53
+
54
+ user = { name: "João" age: 30 }
55
+ if user.age >= 18
56
+ print "{user.name} é adulto"
57
+ end</code></pre>
58
+
59
+ <script type="future">
60
+ names = ["Alice", "Bob", "Carlos"]
61
+ for name in names
62
+ print "Olá, {name}!"
63
+ end
64
+
65
+ user = { name: "João" age: 30 }
66
+ if user.age >= 18
67
+ print "{user.name} é adulto"
68
+ end
69
+ </script>
70
+
71
+ <!-- ─── Example 2: Memory ─────────────────────────────────────────────────── -->
72
+ <h2>Example 2 — Memory (no AI required)</h2>
73
+ <pre><code>memory.set("visits", 1)
74
+ count = memory.get("visits")
75
+ print "Visitas: {count}"</code></pre>
76
+
77
+ <script type="future">
78
+ memory.set("visits", 1)
79
+ count = memory.get("visits")
80
+ print "Visitas: {count}"
81
+ </script>
82
+
83
+ <!-- ─── Example 3: AI ─────────────────────────────────────────────────────── -->
84
+ <h2>Example 3 — AI <span class="badge demo">requires API key or proxy</span></h2>
85
+ <pre><code>try
86
+ answer = ai.ask("What is 2 + 2? Reply with just the number.")
87
+ print "AI diz: {answer}"
88
+ catch err
89
+ print "AI não configurada — adiciona uma key ou proxy"
90
+ end</code></pre>
91
+
92
+ <script type="future">
93
+ try
94
+ answer = ai.ask("What is 2 + 2? Reply with just the number.")
95
+ print "AI diz: {answer}"
96
+ catch err
97
+ print "AI não configurada — adiciona uma key ou proxy"
98
+ end
99
+ </script>
100
+
101
+ <!-- ─── Example 4: Agent ──────────────────────────────────────────────────── -->
102
+ <h2>Example 4 — Agent <span class="badge demo">requires API key or proxy</span></h2>
103
+ <pre><code>agent tradutor
104
+ use ai
105
+ resultado = ai.ask("Traduz para português: {goal}")
106
+ return resultado
107
+ end
108
+
109
+ try
110
+ pt = tradutor("Good morning, how are you?")
111
+ print pt
112
+ catch err
113
+ print "Agent precisa de AI — adiciona uma key ou proxy"
114
+ end</code></pre>
115
+
116
+ <script type="future">
117
+ agent tradutor
118
+ use ai
119
+ resultado = ai.ask("Traduz para português: {goal}")
120
+ return resultado
121
+ end
122
+
123
+ try
124
+ pt = tradutor("Good morning, how are you?")
125
+ print pt
126
+ catch err
127
+ print "Agent precisa de AI — adiciona uma key ou proxy"
128
+ end
129
+ </script>
130
+
131
+ <!-- ─── Output ────────────────────────────────────────────────────────────── -->
132
+ <h2>Output</h2>
133
+ <div id="output"></div>
134
+
135
+ <!-- ─── Proxy server example ──────────────────────────────────────────────── -->
136
+ <h2>Proxy server example <span class="badge proxy">production</span></h2>
137
+ <pre><code>// server.js — guarda a tua key no servidor, nunca no browser
138
+ import express from 'express'
139
+ import { runtime } from 'future-lang/runtime'
140
+
141
+ const app = express()
142
+ app.use(express.json())
143
+
144
+ runtime.ai.configure('openai', process.env.OPENAI_KEY)
145
+
146
+ app.post('/api/ai/ask', async (req, res) => res.json({ text: await runtime.ai.ask(req.body.prompt) }))
147
+ app.post('/api/ai/chat', async (req, res) => res.json({ text: await runtime.ai.chat(req.body.messages) }))
148
+ app.post('/api/ai/embed', async (req, res) => res.json({ embedding: await runtime.ai.embed(req.body.text) }))
149
+ app.listen(3000)</code></pre>
150
+
151
+ <pre><code>&lt;!-- HTML — a key nunca aparece aqui --&gt;
152
+ &lt;script type="module" src="future-browser.js"&gt;&lt;/script&gt;
153
+ &lt;script type="module"&gt;
154
+ import Future from './future-browser.js'
155
+ Future.configure({ proxy: '/api/ai' })
156
+ &lt;/script&gt;
157
+
158
+ &lt;script type="future"&gt;
159
+ answer = ai.ask("Olá!")
160
+ print answer
161
+ &lt;/script&gt;</code></pre>
162
+
163
+ </body>
164
+ </html>
@@ -0,0 +1,7 @@
1
+ # Functions, string concatenation and calls
2
+ function greet(name)
3
+ print "Hello, " + name
4
+ end
5
+
6
+ greet("John")
7
+ greet("Ada")
@@ -0,0 +1 @@
1
+ print "Hello World"
@@ -0,0 +1,8 @@
1
+ # Functions with return values and arithmetic
2
+ function square(n)
3
+ return n * n
4
+ end
5
+
6
+ x = 5
7
+ print "x squared is:"
8
+ print square(x)
@@ -0,0 +1,301 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Future — mini app test</title>
6
+ </head>
7
+ <body>
8
+
9
+ <h1>Future runtime test</h1>
10
+ <pre id="out" style="background:#111;color:#0f0;padding:1rem;min-height:200px;white-space:pre-wrap"></pre>
11
+
12
+ <!-- ── 1. Load the runtime ──────────────────────────────────────────────────── -->
13
+ <script type="module" src="../future-browser.js"></script>
14
+
15
+ <!-- ── 2. Configure: redirect print + set AI key ────────────────────────────── -->
16
+ <script type="module">
17
+ import Future from '../future-browser.js';
18
+
19
+ const out = document.getElementById('out');
20
+ Future.runtime.print = (...args) => {
21
+ out.textContent += args.join(' ') + '\n';
22
+ };
23
+
24
+ // Uncomment to test AI (programs 10 and 11):
25
+ // Future.configure({ proxy: '/api/ai' });
26
+ // Future.configure({ provider: 'venice', apiKey: 'your-key-here' });
27
+ //
28
+ // Or set window.__env before this script and use system.env() from Future code:
29
+ window.__env = { VENICE_API_KEY: 'VENICE-INFERENCE-KEY-n9mnNP2DsEdj-M8osOKWgwpxou51VXtWlAlpasmdnl' }
30
+ </script>
31
+
32
+ <!-- ── Future programs ────────────────────────────────────────────────────────
33
+ Tip: string interpolation supports {variable} and {object.prop} only.
34
+ Function calls must be assigned to a variable first:
35
+ n = len(items)
36
+ print "Count: {n}" ✓
37
+ print "Count: {len(items)}" ✗ (stays as literal text)
38
+ ──────────────────────────────────────────────────────────────────────────── -->
39
+
40
+ <!-- Program 1: variables, strings, lists, for -->
41
+ <script type="future">
42
+ print "=== 1. Basic ==="
43
+ name = "Future"
44
+ version = 0.3
45
+ print "Language: {name} v{version}"
46
+
47
+ items = ["alpha", "beta", "gamma", "delta"]
48
+ n = len(items)
49
+ print "List has {n} items:"
50
+ for item in items
51
+ print " - {item}"
52
+ end
53
+ </script>
54
+
55
+ <!-- Program 2: math module -->
56
+ <script type="future">
57
+ print "=== 2. Math ==="
58
+ a = math.round(3.7)
59
+ b = math.floor(3.9)
60
+ c = math.ceil(3.1)
61
+ d = math.abs(-42)
62
+ e = math.sqrt(144)
63
+ f = math.pow(2, 10)
64
+ g = math.max(3, 9, 1, 7)
65
+ h = math.min(3, 9, 1, 7)
66
+ r = math.random()
67
+ print "round(3.7) = {a}"
68
+ print "floor(3.9) = {b}"
69
+ print "ceil(3.1) = {c}"
70
+ print "abs(-42) = {d}"
71
+ print "sqrt(144) = {e}"
72
+ print "pow(2,10) = {f}"
73
+ print "max(3,9,1,7) = {g}"
74
+ print "min(3,9,1,7) = {h}"
75
+ print "random() = {r}"
76
+ print "pi = {math.pi}"
77
+ print "e = {math.e}"
78
+ </script>
79
+
80
+ <!-- Program 3: while loop + len — use for to sum (no bracket index in Future) -->
81
+ <script type="future">
82
+ print "=== 3. While + for accumulator ==="
83
+ scores = [72, 85, 91, 60, 88, 95, 78]
84
+ n = len(scores)
85
+
86
+ # sum with for (Future has no array[i] index access yet)
87
+ total = 0
88
+ for s in scores
89
+ total = total + s
90
+ end
91
+
92
+ avg = math.round(total / n)
93
+ best = math.max(72, 85, 91, 60, 88, 95, 78)
94
+ worst = math.min(72, 85, 91, 60, 88, 95, 78)
95
+ print "Count: {n} Total: {total} Avg: {avg}"
96
+ print "Best: {best} Worst: {worst}"
97
+
98
+ # while loop counting down
99
+ i = 5
100
+ print "Countdown:"
101
+ while i > 0
102
+ print " {i}"
103
+ i = i - 1
104
+ end
105
+ print " go!"
106
+ </script>
107
+
108
+ <!-- Program 4: objects, null, conditionals -->
109
+ <script type="future">
110
+ print "=== 4. Objects + null ==="
111
+ user = { name: "Alice" age: 28 role: "admin" }
112
+ print "Name: {user.name} Age: {user.age} Role: {user.role}"
113
+
114
+ session = null
115
+ if session == null
116
+ print "No active session"
117
+ else
118
+ print "Session active"
119
+ end
120
+
121
+ if user.age >= 18
122
+ print "{user.name} is an adult"
123
+ end
124
+
125
+ # nested object access
126
+ product = { name: "Widget" price: 9.99 }
127
+ total = product.price * 3
128
+ rounded = math.round(total * 100) / 100
129
+ print "3x {product.name} = {rounded}"
130
+ </script>
131
+
132
+ <!-- Program 5: functions -->
133
+ <script type="future">
134
+ print "=== 5. Functions ==="
135
+
136
+ function greet(name)
137
+ return "Hello, {name}!"
138
+ end
139
+
140
+ function factorial(n)
141
+ if n <= 1
142
+ return 1
143
+ end
144
+ return n * factorial(n - 1)
145
+ end
146
+
147
+ function clamp(value, lo, hi)
148
+ if value < lo
149
+ return lo
150
+ end
151
+ if value > hi
152
+ return hi
153
+ end
154
+ return value
155
+ end
156
+
157
+ msg = greet("World")
158
+ print msg
159
+
160
+ f5 = factorial(5)
161
+ f10 = factorial(10)
162
+ print "5! = {f5}"
163
+ print "10! = {f10}"
164
+
165
+ c1 = clamp(150, 0, 100)
166
+ c2 = clamp(-10, 0, 100)
167
+ c3 = clamp(42, 0, 100)
168
+ print "clamp(150, 0, 100) = {c1}"
169
+ print "clamp(-10, 0, 100) = {c2}"
170
+ print "clamp(42, 0, 100) = {c3}"
171
+ </script>
172
+
173
+ <!-- Program 6: memory module -->
174
+ <script type="future">
175
+ print "=== 6. Memory ==="
176
+ memory.set("app", "FutureTest")
177
+ memory.set("version", "0.3.2")
178
+ memory.set("user", "Alice")
179
+ memory.set("score", 99)
180
+
181
+ app = memory.get("app")
182
+ ver = memory.get("version")
183
+ print "Running {app} v{ver}"
184
+
185
+ results = memory.search("ver")
186
+ n = len(results)
187
+ print "Keys matching 'ver': {n}"
188
+
189
+ memory.forget("user")
190
+ gone = memory.get("user")
191
+ if gone == null
192
+ print "user key deleted"
193
+ end
194
+
195
+ score = memory.get("score")
196
+ print "score = {score}"
197
+ </script>
198
+
199
+ <!-- Program 7: try / catch + custom error-safe function -->
200
+ <script type="future">
201
+ print "=== 7. Error handling ==="
202
+
203
+ function safeDivide(a, b)
204
+ if b == 0
205
+ return null
206
+ end
207
+ return a / b
208
+ end
209
+
210
+ r1 = safeDivide(10, 2)
211
+ r2 = safeDivide(10, 0)
212
+ print "10 / 2 = {r1}"
213
+ print "10 / 0 = {r2}"
214
+
215
+ try
216
+ bad = http.get("https://this-does-not-exist-xyz123.com/api")
217
+ print bad
218
+ catch err
219
+ print "Caught expected error: request failed"
220
+ end
221
+
222
+ print "Execution continues after catch"
223
+ </script>
224
+
225
+ <!-- Program 8: HTTP (live fetch) -->
226
+ <script type="future">
227
+ print "=== 8. HTTP ==="
228
+ todo = http.get("https://jsonplaceholder.typicode.com/todos/3")
229
+ print "Title: {todo.title}"
230
+ print "Completed: {todo.completed}"
231
+
232
+ user = http.get("https://jsonplaceholder.typicode.com/users/1")
233
+ print "User: {user.name}"
234
+ print "Email: {user.email}"
235
+ print "City: {user.address.city}"
236
+ </script>
237
+
238
+ <!-- Program 9: system.env -->
239
+ <script type="future">
240
+ print "=== 9. system.env ==="
241
+ # In the browser, reads from window.__env
242
+ # In Node.js CLI, reads from process.env
243
+ key = system.env("VENICE_API_KEY")
244
+ if key == null
245
+ print "VENICE_API_KEY not set"
246
+ print "Set window.__env = { VENICE_API_KEY: '...' } before loading the page"
247
+ else
248
+ n = len(key)
249
+ print "VENICE_API_KEY is set ({n} chars)"
250
+ end
251
+ </script>
252
+
253
+ <!-- Program 10: agent (no AI call — works without any key) -->
254
+ <script type="future">
255
+ print "=== 10. Agent (no AI) ==="
256
+
257
+ agent greet
258
+ msg = "Hello from agent! Goal: {goal}"
259
+ return msg
260
+ end
261
+
262
+ agent measure
263
+ n = len(goal)
264
+ return "Input has {n} characters"
265
+ end
266
+
267
+ agent double
268
+ result = goal + " | " + goal
269
+ return result
270
+ end
271
+
272
+ r1 = greet("test the runtime")
273
+ r2 = measure("Hello Future language!")
274
+ r3 = double("ping")
275
+ print r1
276
+ print r2
277
+ print r3
278
+ </script>
279
+
280
+ <!-- Program 11: AI (requires key or proxy — skips cleanly if not set) -->
281
+ <script type="future">
282
+ print "=== 11. AI ==="
283
+
284
+ # To enable: configure in the <script type="module"> block above
285
+ key = system.env("VENICE_API_KEY")
286
+ if key == null
287
+ print "AI skipped — no key configured"
288
+ print "Set window.__env.VENICE_API_KEY or Future.configure({ proxy: '/api/ai' })"
289
+ else
290
+ ai.configure("venice", key)
291
+ try
292
+ answer = ai.ask("Reply with exactly three words: Future is awesome")
293
+ print "AI says: {answer}"
294
+ catch err
295
+ print "AI error: {err}"
296
+ end
297
+ end
298
+ </script>
299
+
300
+ </body>
301
+ </html>
@@ -0,0 +1,10 @@
1
+ # Automação residencial sobre MQTT (loopback local, roda sem broker)
2
+ function aoReceber(mensagem)
3
+ print "Sensor disse: " + mensagem
4
+ end
5
+
6
+ mqtt.subscribe("casa/sala/temperatura", aoReceber)
7
+ mqtt.publish("casa/sala/temperatura", "23.5")
8
+
9
+ home.turnOn("luz_sala")
10
+ print "Luz da sala ligada."
@@ -0,0 +1,102 @@
1
+ // future-browser.js — Browser runtime for the Future programming language.
2
+ //
3
+ // Usage:
4
+ // <script type="module" src="future-browser.js"></script>
5
+ //
6
+ // <!-- Option A: Proxy mode (production — key stays on your server) -->
7
+ // <script>Future.configure({ proxy: '/api/ai' })</script>
8
+ //
9
+ // <!-- Option B: Demo mode (key visible in HTML — dev/demos only) -->
10
+ // <script>Future.configure({ provider: 'openai', apiKey: 'sk-...' })</script>
11
+ //
12
+ // <!-- Then write Future code anywhere on the page -->
13
+ // <script type="future">
14
+ // answer = ai.ask("Olá mundo")
15
+ // print answer
16
+ // </script>
17
+ //
18
+ // Proxy contract (for Option A):
19
+ // POST {proxy}/ask { prompt } → { text }
20
+ // POST {proxy}/chat { messages } → { text }
21
+ // POST {proxy}/stream { prompt } → SSE (OpenAI format)
22
+ // POST {proxy}/embed { text } → { embedding: number[] }
23
+
24
+ import { tokenize } from './src/lexer.js';
25
+ import { parse } from './src/parser.js';
26
+ import { generate } from './src/generator.js';
27
+ import { browserRuntime, setProxy, ai } from './runtime/browser.js';
28
+
29
+ // ─── Public API ───────────────────────────────────────────────────────────────
30
+
31
+ const Future = {
32
+ /**
33
+ * Configure the browser runtime.
34
+ *
35
+ * Proxy mode (production):
36
+ * Future.configure({ proxy: '/api/ai' })
37
+ *
38
+ * Demo mode (development):
39
+ * Future.configure({ provider: 'openai', apiKey: 'sk-...' })
40
+ * Future.configure({ provider: 'anthropic', apiKey: 'sk-ant-...' })
41
+ * Future.configure({ provider: 'ollama' }) // local, no key needed
42
+ */
43
+ configure(options = {}) {
44
+ if (options.proxy) {
45
+ setProxy(options.proxy);
46
+ }
47
+ if (options.provider || options.apiKey) {
48
+ ai.configure(options.provider ?? 'openai', options.apiKey ?? null, options.model ?? null);
49
+ }
50
+ },
51
+
52
+ /**
53
+ * Compile and run a Future source string.
54
+ * Returns a Promise that resolves when the program finishes.
55
+ */
56
+ async run(source) {
57
+ let js;
58
+ try {
59
+ js = generate(parse(tokenize(String(source))), { browserMode: true });
60
+ } catch (e) {
61
+ console.error('[Future] Compile error:', e.message);
62
+ throw e;
63
+ }
64
+ // Wrap in async IIFE; __rt is passed as parameter (not a global).
65
+ // __rt is always passed — even in SIMPLE mode — so __rt.print is available.
66
+ const fn = new Function('__rt', `return (async () => {\n${js}\n})()`);
67
+ return fn(browserRuntime);
68
+ },
69
+
70
+ /** Compile Future source to JavaScript without running it. */
71
+ compile(source) {
72
+ return generate(parse(tokenize(String(source))), { browserMode: true });
73
+ },
74
+
75
+ /** The underlying runtime object — useful for REPL or direct capability calls. */
76
+ runtime: browserRuntime,
77
+ };
78
+
79
+ // ─── <script type="future"> interceptor ──────────────────────────────────────
80
+
81
+ async function runScripts() {
82
+ const scripts = document.querySelectorAll('script[type="future"]');
83
+ for (const script of scripts) {
84
+ const source = script.textContent ?? '';
85
+ if (!source.trim()) continue;
86
+ try {
87
+ await Future.run(source);
88
+ } catch (e) {
89
+ console.error(`[Future] Runtime error in <script type="future">:`, e);
90
+ }
91
+ }
92
+ }
93
+
94
+ // Defer runScripts with setTimeout so all <script type="module"> blocks on the
95
+ // page (including configuration scripts that set proxy or override print) finish
96
+ // initialising before any <script type="future"> block runs.
97
+ setTimeout(runScripts, 0);
98
+
99
+ // ─── Expose globally ──────────────────────────────────────────────────────────
100
+
101
+ window.Future = Future;
102
+ export default Future;