tjs-lang 0.2.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/CONTEXT.md +594 -0
- package/LICENSE +190 -0
- package/README.md +220 -0
- package/bin/benchmarks.ts +351 -0
- package/bin/dev.ts +205 -0
- package/bin/docs.js +170 -0
- package/bin/install-cursor.sh +71 -0
- package/bin/install-vscode.sh +71 -0
- package/bin/select-local-models.d.ts +1 -0
- package/bin/select-local-models.js +28 -0
- package/bin/select-local-models.ts +31 -0
- package/demo/autocomplete.test.ts +232 -0
- package/demo/docs.json +186 -0
- package/demo/examples.test.ts +598 -0
- package/demo/index.html +91 -0
- package/demo/src/autocomplete.ts +482 -0
- package/demo/src/capabilities.ts +859 -0
- package/demo/src/demo-nav.ts +2097 -0
- package/demo/src/examples.test.ts +161 -0
- package/demo/src/examples.ts +476 -0
- package/demo/src/imports.test.ts +196 -0
- package/demo/src/imports.ts +421 -0
- package/demo/src/index.ts +639 -0
- package/demo/src/module-store.ts +635 -0
- package/demo/src/module-sw.ts +132 -0
- package/demo/src/playground.ts +949 -0
- package/demo/src/service-host.ts +389 -0
- package/demo/src/settings.ts +440 -0
- package/demo/src/style.ts +280 -0
- package/demo/src/tjs-playground.ts +1605 -0
- package/demo/src/ts-examples.ts +478 -0
- package/demo/src/ts-playground.ts +1092 -0
- package/demo/static/favicon.svg +30 -0
- package/demo/static/photo-1.jpg +0 -0
- package/demo/static/photo-2.jpg +0 -0
- package/demo/static/texts/ai-history.txt +9 -0
- package/demo/static/texts/coffee-origins.txt +9 -0
- package/demo/static/texts/renewable-energy.txt +9 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +37 -0
- package/dist/tjs-batteries.js +4 -0
- package/dist/tjs-batteries.js.map +15 -0
- package/dist/tjs-full.js +256 -0
- package/dist/tjs-full.js.map +37 -0
- package/dist/tjs-transpiler.js +220 -0
- package/dist/tjs-transpiler.js.map +21 -0
- package/dist/tjs-vm.js +4 -0
- package/dist/tjs-vm.js.map +14 -0
- package/docs/CNAME +1 -0
- package/docs/favicon.svg +30 -0
- package/docs/index.html +91 -0
- package/docs/index.js +10468 -0
- package/docs/index.js.map +92 -0
- package/docs/photo-1.jpg +0 -0
- package/docs/photo-1.webp +0 -0
- package/docs/photo-2.jpg +0 -0
- package/docs/photo-2.webp +0 -0
- package/docs/texts/ai-history.txt +9 -0
- package/docs/texts/coffee-origins.txt +9 -0
- package/docs/texts/renewable-energy.txt +9 -0
- package/docs/tjs-lang.svg +31 -0
- package/docs/tosijs-agent.svg +31 -0
- package/editors/README.md +325 -0
- package/editors/ace/ajs-mode.js +328 -0
- package/editors/ace/ajs-mode.ts +269 -0
- package/editors/ajs-syntax.ts +212 -0
- package/editors/build-grammars.ts +510 -0
- package/editors/codemirror/ajs-language.js +287 -0
- package/editors/codemirror/ajs-language.ts +1447 -0
- package/editors/codemirror/autocomplete.test.ts +531 -0
- package/editors/codemirror/component.ts +404 -0
- package/editors/monaco/ajs-monarch.js +243 -0
- package/editors/monaco/ajs-monarch.ts +225 -0
- package/editors/tjs-syntax.ts +115 -0
- package/editors/vscode/language-configuration.json +37 -0
- package/editors/vscode/package.json +65 -0
- package/editors/vscode/syntaxes/ajs-injection.tmLanguage.json +107 -0
- package/editors/vscode/syntaxes/ajs.tmLanguage.json +252 -0
- package/editors/vscode/syntaxes/tjs.tmLanguage.json +333 -0
- package/package.json +83 -0
- package/src/cli/commands/check.ts +41 -0
- package/src/cli/commands/convert.ts +133 -0
- package/src/cli/commands/emit.ts +260 -0
- package/src/cli/commands/run.ts +68 -0
- package/src/cli/commands/test.ts +194 -0
- package/src/cli/commands/types.ts +20 -0
- package/src/cli/create-app.ts +236 -0
- package/src/cli/playground.ts +250 -0
- package/src/cli/tjs.ts +166 -0
- package/src/cli/tjsx.ts +160 -0
- package/tjs-lang.svg +31 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that all TJS examples in demo-nav transpile without errors
|
|
3
|
+
*
|
|
4
|
+
* Note: We inline the examples here rather than importing from demo-nav.ts
|
|
5
|
+
* because demo-nav.ts imports browser-only dependencies (tosijs, tosijs-ui).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test, expect } from 'bun:test'
|
|
9
|
+
import { tjs } from '../../src/lang'
|
|
10
|
+
|
|
11
|
+
// Extract just the example code strings from demo-nav.ts
|
|
12
|
+
// This avoids importing browser-only dependencies
|
|
13
|
+
const tjsExamples = [
|
|
14
|
+
{
|
|
15
|
+
name: 'TJS Grammar',
|
|
16
|
+
code: `/*
|
|
17
|
+
TJS Grammar demonstrates the type syntax.
|
|
18
|
+
Types are specified by example values.
|
|
19
|
+
*/
|
|
20
|
+
function greet(name: 'World') -> '' {
|
|
21
|
+
return \`Hello, \${name}!\`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Call it
|
|
25
|
+
greet('TJS')
|
|
26
|
+
|
|
27
|
+
test('greet returns a greeting') {
|
|
28
|
+
expect(greet('Alice')).toBe('Hello, Alice!')
|
|
29
|
+
}
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Date Formatting (with import)',
|
|
34
|
+
code: `import { format, parseISO, addDays } from 'date-fns'
|
|
35
|
+
|
|
36
|
+
function formatDate(isoDate: '2024-01-15', pattern: 'PPP') -> '' {
|
|
37
|
+
const date = parseISO(isoDate)
|
|
38
|
+
return format(date, pattern)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function addBusinessDays(isoDate: '2024-01-15', days: 5) -> '' {
|
|
42
|
+
const date = parseISO(isoDate)
|
|
43
|
+
const result = addDays(date, days)
|
|
44
|
+
return format(result, 'yyyy-MM-dd')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
formatDate('2024-03-15', 'MMMM do, yyyy')
|
|
48
|
+
`,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Lodash Utilities (with import)',
|
|
52
|
+
code: `import { groupBy, sortBy, uniqBy, chunk } from 'lodash-es'
|
|
53
|
+
|
|
54
|
+
function groupUsers(users: [{ name: '', dept: '' }], key: 'dept')
|
|
55
|
+
-> { [key: '']: [{ name: '', dept: '' }] } {
|
|
56
|
+
return groupBy(users, key)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sortByAge(users: [{ name: '', age: 0 }]) -> [{ name: '', age: 0 }] {
|
|
60
|
+
return sortBy(users, ['age'])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function uniqueByEmail(users: [{ email: '', name: '' }]) -> [{ email: '', name: '' }] {
|
|
64
|
+
return uniqBy(users, 'email')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Note: nested array types like [['']] aren't supported yet, so we omit return type
|
|
68
|
+
function paginate(items: [''], pageSize: 10) {
|
|
69
|
+
return chunk(items, pageSize)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
test('groupUsers groups by department') {
|
|
73
|
+
const users = [
|
|
74
|
+
{ name: 'Alice', dept: 'eng' },
|
|
75
|
+
{ name: 'Bob', dept: 'sales' },
|
|
76
|
+
{ name: 'Carol', dept: 'eng' }
|
|
77
|
+
]
|
|
78
|
+
const grouped = groupUsers(users, 'dept')
|
|
79
|
+
expect(grouped.eng.length).toBe(2)
|
|
80
|
+
expect(grouped.sales.length).toBe(1)
|
|
81
|
+
}
|
|
82
|
+
`,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'Full-Stack Demo: User Service',
|
|
86
|
+
code: `function createUser(user: { name: '', email: '' }) -> { id: 0, name: '', email: '', created: '' } {
|
|
87
|
+
return {
|
|
88
|
+
id: Math.floor(Math.random() * 10000),
|
|
89
|
+
name: user.name,
|
|
90
|
+
email: user.email,
|
|
91
|
+
created: new Date().toISOString()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Note: union types like "Type | null" in return position aren't supported yet
|
|
96
|
+
function getUser(id: 0) -> { id: 0, name: '', email: '' } {
|
|
97
|
+
return { id, name: 'Test User', email: 'test@example.com' }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
test('createUser returns user with id') {
|
|
101
|
+
const user = createUser({ name: 'Alice', email: 'alice@test.com' })
|
|
102
|
+
expect(typeof user.id).toBe('number')
|
|
103
|
+
expect(user.name).toBe('Alice')
|
|
104
|
+
}
|
|
105
|
+
`,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'The Universal Endpoint',
|
|
109
|
+
code: `function endpoint(request: { path: '', method: 'GET' }) -> { status: 200, body: '' } {
|
|
110
|
+
if (request.method === 'GET') {
|
|
111
|
+
return { status: 200, body: 'Hello from TJS!' }
|
|
112
|
+
}
|
|
113
|
+
return { status: 405, body: 'Method not allowed' }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
test('GET returns 200') {
|
|
117
|
+
const res = endpoint({ path: '/', method: 'GET' })
|
|
118
|
+
expect(res.status).toBe(200)
|
|
119
|
+
}
|
|
120
|
+
`,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'Inline Tests: Test Private Functions',
|
|
124
|
+
code: `// Private helper - not exported
|
|
125
|
+
function validateEmail(email: '') -> true {
|
|
126
|
+
return email.includes('@') && email.includes('.')
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Public API
|
|
130
|
+
function processUser(user: { email: '' }) -> { valid: true } {
|
|
131
|
+
return { valid: validateEmail(user.email) }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// We can test private functions directly!
|
|
135
|
+
test('validateEmail checks for @ and .') {
|
|
136
|
+
expect(validateEmail('test@example.com')).toBe(true)
|
|
137
|
+
expect(validateEmail('invalid')).toBe(false)
|
|
138
|
+
}
|
|
139
|
+
`,
|
|
140
|
+
},
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
describe('TJS Examples', () => {
|
|
144
|
+
for (const example of tjsExamples) {
|
|
145
|
+
test(`"${example.name}" transpiles without error`, () => {
|
|
146
|
+
// Skip examples that are intentionally broken (if any)
|
|
147
|
+
if (
|
|
148
|
+
example.name.toLowerCase().includes('error') ||
|
|
149
|
+
example.name.toLowerCase().includes('broken')
|
|
150
|
+
) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
expect(() => {
|
|
155
|
+
// Skip test execution - these examples have test blocks that require
|
|
156
|
+
// imports which can't run in transpile-time evaluation context
|
|
157
|
+
tjs(example.code, { runTests: false })
|
|
158
|
+
}).not.toThrow()
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
})
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* examples.ts - Example AsyncJS snippets for the playground
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface Example {
|
|
6
|
+
name: string
|
|
7
|
+
description: string
|
|
8
|
+
code: string
|
|
9
|
+
requiresApi?: boolean // Needs LLM API key
|
|
10
|
+
group?: 'featured' | 'basics' | 'api' | 'llm' | 'advanced' // Navigation group
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const examples: Example[] = [
|
|
14
|
+
{
|
|
15
|
+
name: 'Hello World',
|
|
16
|
+
description: 'Simple greeting with template',
|
|
17
|
+
group: 'basics',
|
|
18
|
+
code: `function greet({ name = 'World' }) {
|
|
19
|
+
let message = template({ tmpl: 'Hello, {{name}}!', vars: { name } })
|
|
20
|
+
return { message }
|
|
21
|
+
}`,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'Math Operations',
|
|
25
|
+
description: 'Basic arithmetic and Math built-ins',
|
|
26
|
+
group: 'basics',
|
|
27
|
+
code: `function calculate({ a = 10, b = 5 }) {
|
|
28
|
+
let sum = a + b
|
|
29
|
+
let product = a * b
|
|
30
|
+
let power = a ** b
|
|
31
|
+
let sqrt = Math.sqrt(a)
|
|
32
|
+
let max = Math.max(a, b)
|
|
33
|
+
let rounded = Math.floor(a / b)
|
|
34
|
+
return { sum, product, power, sqrt, max, rounded }
|
|
35
|
+
}`,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Conditional Logic',
|
|
39
|
+
description: 'If/else branching',
|
|
40
|
+
group: 'basics',
|
|
41
|
+
code: `function checkAge({ age = 25 }) {
|
|
42
|
+
if (age >= 18) {
|
|
43
|
+
let status = 'adult'
|
|
44
|
+
return { status, canVote: true }
|
|
45
|
+
} else {
|
|
46
|
+
let status = 'minor'
|
|
47
|
+
return { status, canVote: false }
|
|
48
|
+
}
|
|
49
|
+
}`,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Loop & Filter',
|
|
53
|
+
description: 'Process arrays with array methods',
|
|
54
|
+
group: 'basics',
|
|
55
|
+
code: `function processNumbers({ numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }) {
|
|
56
|
+
let evens = numbers.filter(x => x % 2 == 0)
|
|
57
|
+
let doubled = evens.map(x => x * 2)
|
|
58
|
+
let total = doubled.reduce((acc, x) => acc + x, 0)
|
|
59
|
+
return { evens, doubled, total }
|
|
60
|
+
}`,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'String Processing',
|
|
64
|
+
description: 'Work with text',
|
|
65
|
+
group: 'basics',
|
|
66
|
+
code: `function processText({ text = 'Hello World' }) {
|
|
67
|
+
let upper = text.toUpperCase()
|
|
68
|
+
let lower = text.toLowerCase()
|
|
69
|
+
let words = text.split(' ')
|
|
70
|
+
let wordCount = words.length
|
|
71
|
+
return { upper, lower, words, wordCount }
|
|
72
|
+
}`,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'JSON Processing',
|
|
76
|
+
description: 'Parse and stringify JSON',
|
|
77
|
+
group: 'basics',
|
|
78
|
+
code: `function jsonRoundTrip({ data = { name: 'Alice', age: 30 } }) {
|
|
79
|
+
let jsonStr = JSON.stringify(data)
|
|
80
|
+
let parsed = JSON.parse(jsonStr)
|
|
81
|
+
let name = parsed.name
|
|
82
|
+
return { jsonStr, parsed, name }
|
|
83
|
+
}`,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'Error Handling',
|
|
87
|
+
description: 'Try/catch with Error()',
|
|
88
|
+
group: 'basics',
|
|
89
|
+
code: `function safeDivide({ a = 10, b = 0 }) {
|
|
90
|
+
try {
|
|
91
|
+
if (b == 0) {
|
|
92
|
+
Error('Division by zero!')
|
|
93
|
+
}
|
|
94
|
+
let result = a / b
|
|
95
|
+
return { result }
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return { error: err }
|
|
98
|
+
}
|
|
99
|
+
}`,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'Weather API',
|
|
103
|
+
description: 'Fetch weather data (no API key needed)',
|
|
104
|
+
group: 'api',
|
|
105
|
+
code: `function getWeather({ lat = 37.7749, lon = -122.4194 }) {
|
|
106
|
+
let url = \`https://api.open-meteo.com/v1/forecast?latitude=\${lat}&longitude=\${lon}¤t_weather=true\`
|
|
107
|
+
let response = httpFetch({ url, cache: 1800 })
|
|
108
|
+
let weather = response.current_weather
|
|
109
|
+
return { weather }
|
|
110
|
+
}`,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'iTunes Search',
|
|
114
|
+
description: 'Search Apple iTunes catalog',
|
|
115
|
+
group: 'api',
|
|
116
|
+
code: `function searchMusic({ query = 'Beatles', limit = 5 }) {
|
|
117
|
+
let url = \`https://itunes.apple.com/search?term=\${query}&limit=\${limit}&media=music\`
|
|
118
|
+
let response = httpFetch({ url, cache: 3600 })
|
|
119
|
+
let tracks = response.results.map(x => ({
|
|
120
|
+
artist: x.artistName,
|
|
121
|
+
track: x.trackName,
|
|
122
|
+
album: x.collectionName
|
|
123
|
+
}))
|
|
124
|
+
return { tracks }
|
|
125
|
+
}`,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'GitHub Repos',
|
|
129
|
+
description: 'Search GitHub repositories',
|
|
130
|
+
group: 'api',
|
|
131
|
+
code: `function searchRepos({ query = 'tosijs', perPage = 5 }) {
|
|
132
|
+
let url = \`https://api.github.com/search/repositories?q=\${query}&per_page=\${perPage}&sort=stars\`
|
|
133
|
+
let response = httpFetch({ url, cache: 300 })
|
|
134
|
+
let repos = response.items.map(x => ({
|
|
135
|
+
name: x.full_name,
|
|
136
|
+
stars: x.stargazers_count,
|
|
137
|
+
description: x.description
|
|
138
|
+
}))
|
|
139
|
+
return { repos }
|
|
140
|
+
}`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'LLM Chat',
|
|
144
|
+
description: 'Chat with AI (requires llm capability)',
|
|
145
|
+
group: 'llm',
|
|
146
|
+
requiresApi: true,
|
|
147
|
+
code: `function chat({ message = 'What is the capital of France?' }) {
|
|
148
|
+
// Requires llm.predict capability to be configured
|
|
149
|
+
let response = llmPredict({ prompt: message })
|
|
150
|
+
return { response }
|
|
151
|
+
}`,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'LLM Summarizer',
|
|
155
|
+
description: 'Fetch and summarize text (requires llm capability)',
|
|
156
|
+
group: 'llm',
|
|
157
|
+
requiresApi: true,
|
|
158
|
+
code: `function summarize({ source = 'coffee-origins' }) {
|
|
159
|
+
// Fetch text from our sample documents
|
|
160
|
+
// Options: 'coffee-origins', 'ai-history', 'renewable-energy'
|
|
161
|
+
let url = \`/texts/\${source}.txt\`
|
|
162
|
+
let text = httpFetch({ url })
|
|
163
|
+
|
|
164
|
+
let prompt = \`Summarize the following text in 2-3 sentences:\n\n\${text}\`
|
|
165
|
+
let summary = llmPredict({ prompt })
|
|
166
|
+
return { source, summary }
|
|
167
|
+
}`,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'LLM Structured Output',
|
|
171
|
+
description: 'Get structured JSON from LLM (requires llm capability)',
|
|
172
|
+
group: 'llm',
|
|
173
|
+
requiresApi: true,
|
|
174
|
+
code: `function extractInfo({ text = 'John Smith is a 35-year-old software engineer from San Francisco who loves hiking and photography.' }) {
|
|
175
|
+
// Schema.response builds responseFormat from an example
|
|
176
|
+
let schema = Schema.response('person_info', {
|
|
177
|
+
name: '',
|
|
178
|
+
age: 0,
|
|
179
|
+
occupation: '',
|
|
180
|
+
location: '',
|
|
181
|
+
hobbies: ['']
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
let prompt = \`Extract person info from this text: \${text}\`
|
|
185
|
+
let response = llmPredict({ prompt, options: { responseFormat: schema } })
|
|
186
|
+
let person = JSON.parse(response)
|
|
187
|
+
return { person }
|
|
188
|
+
}`,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'LLM + API Data',
|
|
192
|
+
description: 'LLM analyzes API data (requires llm capability)',
|
|
193
|
+
group: 'featured',
|
|
194
|
+
requiresApi: true,
|
|
195
|
+
code: `function findCovers({ song = 'Yesterday', artist = 'Beatles' }) {
|
|
196
|
+
// Search iTunes for the song
|
|
197
|
+
let query = song + ' ' + artist
|
|
198
|
+
let url = \`https://itunes.apple.com/search?term=\${query}&limit=25&media=music\`
|
|
199
|
+
let response = httpFetch({ url, cache: 3600 })
|
|
200
|
+
|
|
201
|
+
// Format results for LLM analysis
|
|
202
|
+
let results = response.results || []
|
|
203
|
+
let tracks = results.map(x => \`"\${x.trackName}" by \${x.artistName} (\${x.collectionName})\`)
|
|
204
|
+
let trackList = tracks.join('\\n')
|
|
205
|
+
|
|
206
|
+
// Schema.response from example - much cleaner!
|
|
207
|
+
let schema = Schema.response('cover_versions', {
|
|
208
|
+
covers: [{ track: '', artist: '', album: '' }]
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
let prompt = \`Search results for "\${song}" by \${artist}:
|
|
212
|
+
|
|
213
|
+
\${trackList}
|
|
214
|
+
|
|
215
|
+
List cover versions (tracks NOT by \${artist}).\`
|
|
216
|
+
|
|
217
|
+
let llmResponse = llmPredict({ prompt, options: { responseFormat: schema } })
|
|
218
|
+
let parsed = JSON.parse(llmResponse)
|
|
219
|
+
return { originalArtist: artist, song, covers: parsed.covers }
|
|
220
|
+
}`,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'LLM with Tool',
|
|
224
|
+
description: 'LLM uses a calculator tool (requires llm capability)',
|
|
225
|
+
group: 'advanced',
|
|
226
|
+
requiresApi: true,
|
|
227
|
+
code: `function mathAssistant({ question = 'What is 23 * 47 + 156?' }) {
|
|
228
|
+
// First, ask LLM to extract the calculation
|
|
229
|
+
let extractPrompt = \`Extract the math expression from this question. Return ONLY the expression, nothing else.
|
|
230
|
+
Question: \${question}\`
|
|
231
|
+
let expression = llmPredict({ prompt: extractPrompt })
|
|
232
|
+
|
|
233
|
+
// Evaluate the expression (simple eval simulation)
|
|
234
|
+
let calcPrompt = \`Calculate: \${expression}
|
|
235
|
+
Return ONLY the numeric result.\`
|
|
236
|
+
let calcResult = llmPredict({ prompt: calcPrompt })
|
|
237
|
+
|
|
238
|
+
// Format the answer
|
|
239
|
+
let answerPrompt = \`The user asked: "\${question}"
|
|
240
|
+
The calculated result is: \${calcResult}
|
|
241
|
+
Write a brief, friendly response with the answer.\`
|
|
242
|
+
let answer = llmPredict({ prompt: answerPrompt })
|
|
243
|
+
|
|
244
|
+
return { question, expression: expression.trim(), result: calcResult.trim(), answer }
|
|
245
|
+
}`,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'Multi-Agent Pipeline',
|
|
249
|
+
description: 'Two agents collaborate on a task (requires llm capability)',
|
|
250
|
+
group: 'advanced',
|
|
251
|
+
requiresApi: true,
|
|
252
|
+
code: `function collaborativeWriting({ topic = 'the future of renewable energy' }) {
|
|
253
|
+
// Agent 1: Research Agent - generates key points
|
|
254
|
+
let researchPrompt = \`You are a research agent. Generate 3 key facts or points about: \${topic}
|
|
255
|
+
Format as a numbered list. Be concise.\`
|
|
256
|
+
let research = llmPredict({ prompt: researchPrompt })
|
|
257
|
+
|
|
258
|
+
// Agent 2: Writer Agent - creates content from research
|
|
259
|
+
let writerPrompt = \`You are a writer agent. Using these research points:
|
|
260
|
+
|
|
261
|
+
\${research}
|
|
262
|
+
|
|
263
|
+
Write a short, engaging paragraph (2-3 sentences) about \${topic}.
|
|
264
|
+
Make it informative and accessible.\`
|
|
265
|
+
let article = llmPredict({ prompt: writerPrompt })
|
|
266
|
+
|
|
267
|
+
// Agent 3: Editor Agent - reviews and improves
|
|
268
|
+
let editorPrompt = \`You are an editor agent. Review this draft:
|
|
269
|
+
|
|
270
|
+
"\${article}"
|
|
271
|
+
|
|
272
|
+
Suggest one specific improvement. Then provide the improved version.
|
|
273
|
+
Format: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"\`
|
|
274
|
+
let edited = llmPredict({ prompt: editorPrompt })
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
topic,
|
|
278
|
+
researchPoints: research,
|
|
279
|
+
firstDraft: article,
|
|
280
|
+
editedVersion: edited
|
|
281
|
+
}
|
|
282
|
+
}`,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'Fuel Limits',
|
|
286
|
+
description: 'Demonstrates safe termination - try different fuel amounts!',
|
|
287
|
+
group: 'featured',
|
|
288
|
+
code: `function infiniteLoop({ limit = 1000000 }) {
|
|
289
|
+
// This will run out of fuel before completing
|
|
290
|
+
let counter = 0
|
|
291
|
+
let i = 0
|
|
292
|
+
while (i < limit) {
|
|
293
|
+
counter = counter + 1
|
|
294
|
+
i = i + 1
|
|
295
|
+
}
|
|
296
|
+
return { counter }
|
|
297
|
+
}`,
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: 'Vision: OCR',
|
|
301
|
+
description: 'Extract text from an image (requires vision model)',
|
|
302
|
+
group: 'advanced',
|
|
303
|
+
requiresApi: true,
|
|
304
|
+
code: `function extractText({ imageUrl = '/photo-2.jpg' }) {
|
|
305
|
+
// Fetch image as data URL for vision model
|
|
306
|
+
let image = httpFetch({ url: imageUrl, responseType: 'dataUrl' })
|
|
307
|
+
|
|
308
|
+
// Use Schema.response for structured output
|
|
309
|
+
let schema = Schema.response('ocr_result', {
|
|
310
|
+
text: '',
|
|
311
|
+
items: [{ description: '', amount: '' }]
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
let result = llmVision({
|
|
315
|
+
prompt: 'Extract all text from this image. If it is a receipt, list the items and amounts.',
|
|
316
|
+
images: [image],
|
|
317
|
+
responseFormat: schema
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
let parsed = JSON.parse(result.content)
|
|
321
|
+
return { imageUrl, extracted: parsed }
|
|
322
|
+
}`,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'Vision: Classification',
|
|
326
|
+
description: 'Classify and describe an image (requires vision model)',
|
|
327
|
+
group: 'advanced',
|
|
328
|
+
requiresApi: true,
|
|
329
|
+
code: `function classifyImage({ imageUrl = '/photo-1.jpg' }) {
|
|
330
|
+
// Fetch image as data URL
|
|
331
|
+
let image = httpFetch({ url: imageUrl, responseType: 'dataUrl' })
|
|
332
|
+
|
|
333
|
+
// Schema for classification result
|
|
334
|
+
let schema = Schema.response('image_classification', {
|
|
335
|
+
category: '',
|
|
336
|
+
subject: '',
|
|
337
|
+
description: '',
|
|
338
|
+
tags: [''],
|
|
339
|
+
confidence: ''
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
let result = llmVision({
|
|
343
|
+
prompt: 'Classify this image. Identify the main subject, provide a brief description, and list relevant tags.',
|
|
344
|
+
images: [image],
|
|
345
|
+
responseFormat: schema
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
let parsed = JSON.parse(result.content)
|
|
349
|
+
return { imageUrl, classification: parsed }
|
|
350
|
+
}`,
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: 'LLM Code Solver',
|
|
354
|
+
description:
|
|
355
|
+
'LLM writes and runs code to solve a problem (requires llm capability)',
|
|
356
|
+
group: 'advanced',
|
|
357
|
+
requiresApi: true,
|
|
358
|
+
code: `function solveWithCode({ problem = 'Calculate the 10th Fibonacci number' }) {
|
|
359
|
+
// System prompt with AsyncJS rules and example
|
|
360
|
+
let systemContext = \`You write AsyncJS code. AsyncJS is a JavaScript subset.
|
|
361
|
+
|
|
362
|
+
RULES:
|
|
363
|
+
- NO: async, await, new, class, this, var, for loops
|
|
364
|
+
- Use let for variables, while for loops
|
|
365
|
+
- Return an object: return { result }
|
|
366
|
+
|
|
367
|
+
EXAMPLE (factorial):
|
|
368
|
+
function solve() {
|
|
369
|
+
let result = 1
|
|
370
|
+
let i = 5
|
|
371
|
+
while (i > 1) {
|
|
372
|
+
result = result * i
|
|
373
|
+
i = i - 1
|
|
374
|
+
}
|
|
375
|
+
return { result }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
Return ONLY the function code, nothing else.\`
|
|
379
|
+
|
|
380
|
+
let prompt = \`\${systemContext}
|
|
381
|
+
|
|
382
|
+
Write a function called "solve" that: \${problem}\`
|
|
383
|
+
|
|
384
|
+
let response = llmPredict({ prompt })
|
|
385
|
+
|
|
386
|
+
// Clean up code - remove markdown fences, fix escapes, extract function
|
|
387
|
+
let code = response
|
|
388
|
+
code = code.replace(/\`\`\`(?:javascript|js|asyncjs)?\\n?/g, '')
|
|
389
|
+
code = code.replace(/\\n?\`\`\`/g, '')
|
|
390
|
+
code = code.replace(/\\\\n/g, '\\n')
|
|
391
|
+
code = code.replace(/\\\\t/g, '\\t')
|
|
392
|
+
code = code.replace(/\\\\"/g, '"')
|
|
393
|
+
code = code.trim()
|
|
394
|
+
|
|
395
|
+
// Try to extract just the function if there's extra text
|
|
396
|
+
let funcMatch = code.match(/function\\s+solve\\s*\\([^)]*\\)\\s*\\{[\\s\\S]*\\}/)
|
|
397
|
+
if (funcMatch) {
|
|
398
|
+
code = funcMatch[0]
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Validate it looks like a function before running
|
|
402
|
+
if (!code.startsWith('function')) {
|
|
403
|
+
return {
|
|
404
|
+
problem,
|
|
405
|
+
error: 'LLM did not generate valid code',
|
|
406
|
+
rawResponse: response
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Execute the generated code
|
|
411
|
+
let output = runCode({ code, args: {} })
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
problem,
|
|
415
|
+
generatedCode: code,
|
|
416
|
+
result: output.result
|
|
417
|
+
}
|
|
418
|
+
}`,
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'LLM Code Generator',
|
|
422
|
+
description:
|
|
423
|
+
'LLM writes AsyncJS code from a description (requires llm capability)',
|
|
424
|
+
group: 'advanced',
|
|
425
|
+
requiresApi: true,
|
|
426
|
+
code: `function generateCode({ task = 'Calculate the factorial of n' }) {
|
|
427
|
+
// System prompt with AsyncJS rules and complete example
|
|
428
|
+
let systemContext = \`You write AsyncJS code. AsyncJS is a subset of JavaScript.
|
|
429
|
+
|
|
430
|
+
RULES:
|
|
431
|
+
- Types by example: fn(n: 5) means required number param with example value 5
|
|
432
|
+
- NO: async, await, new, class, this, var, for, generator functions (function*)
|
|
433
|
+
- Use let for variables, while for loops
|
|
434
|
+
- Return an object: return { result }
|
|
435
|
+
|
|
436
|
+
EXAMPLE - calculating sum of 1 to n:
|
|
437
|
+
function sumTo(n: 10) {
|
|
438
|
+
let sum = 0
|
|
439
|
+
let i = 1
|
|
440
|
+
while (i <= n) {
|
|
441
|
+
sum = sum + i
|
|
442
|
+
i = i + 1
|
|
443
|
+
}
|
|
444
|
+
return { result: sum }
|
|
445
|
+
}\`
|
|
446
|
+
|
|
447
|
+
let schema = Schema.response('generated_code', {
|
|
448
|
+
code: '',
|
|
449
|
+
description: ''
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
let prompt = \`\${systemContext}
|
|
453
|
+
|
|
454
|
+
Write an AsyncJS function for: \${task}
|
|
455
|
+
|
|
456
|
+
Return ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).\`
|
|
457
|
+
|
|
458
|
+
let response = llmPredict({ prompt, options: { responseFormat: schema } })
|
|
459
|
+
let result = JSON.parse(response)
|
|
460
|
+
|
|
461
|
+
// Clean up any markdown fences and fix escaped newlines
|
|
462
|
+
let code = result.code
|
|
463
|
+
code = code.replace(/\`\`\`(?:javascript|js)?\\n?/g, '')
|
|
464
|
+
code = code.replace(/\\n?\`\`\`/g, '')
|
|
465
|
+
code = code.replace(/\\\\n/g, '\\n')
|
|
466
|
+
code = code.replace(/\\\\t/g, '\\t')
|
|
467
|
+
code = code.trim()
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
task,
|
|
471
|
+
code,
|
|
472
|
+
description: result.description
|
|
473
|
+
}
|
|
474
|
+
}`,
|
|
475
|
+
},
|
|
476
|
+
]
|