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
package/bin/docs.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* docs.js - Documentation extractor for agent-99
|
|
3
|
+
*
|
|
4
|
+
* Scans source files for /*# ... */ markdown blocks and .md files,
|
|
5
|
+
* outputs demo/docs.json for the documentation site.
|
|
6
|
+
*
|
|
7
|
+
* Adapted from tosijs-ui's docs.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync, writeFileSync, readdirSync, statSync } from 'fs'
|
|
11
|
+
import { join, extname, basename } from 'path'
|
|
12
|
+
|
|
13
|
+
const TRIM_REGEX = /^#+ |`/g
|
|
14
|
+
|
|
15
|
+
function metadata(content, filePath) {
|
|
16
|
+
let source = content.match(/<\!\-\-(\{.*\})\-\->|\/\*(\{.*\})\*\//)
|
|
17
|
+
let data = {}
|
|
18
|
+
if (source) {
|
|
19
|
+
try {
|
|
20
|
+
data = JSON.parse(source[1] || source[2])
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.error('bad metadata in doc', filePath)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return data
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Section order for navigation hierarchy
|
|
29
|
+
const SECTION_ORDER = {
|
|
30
|
+
home: 0,
|
|
31
|
+
meta: 1,
|
|
32
|
+
tjs: 2,
|
|
33
|
+
ajs: 3,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function hierarchicalSort(a, b) {
|
|
37
|
+
// First sort by section
|
|
38
|
+
const sectionA = SECTION_ORDER[a.section] ?? 99
|
|
39
|
+
const sectionB = SECTION_ORDER[b.section] ?? 99
|
|
40
|
+
if (sectionA !== sectionB) return sectionA - sectionB
|
|
41
|
+
|
|
42
|
+
// Then by group (docs before others)
|
|
43
|
+
const groupA = a.group || ''
|
|
44
|
+
const groupB = b.group || ''
|
|
45
|
+
if (groupA !== groupB) return groupA.localeCompare(groupB)
|
|
46
|
+
|
|
47
|
+
// Then by order
|
|
48
|
+
const orderA = a.order ?? 99
|
|
49
|
+
const orderB = b.order ?? 99
|
|
50
|
+
if (orderA !== orderB) return orderA - orderB
|
|
51
|
+
|
|
52
|
+
// Finally alphabetically by title
|
|
53
|
+
return a.title.localeCompare(b.title)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function findMarkdownFiles(dirs, ignore) {
|
|
57
|
+
let markdownFiles = []
|
|
58
|
+
|
|
59
|
+
function traverseDirectory(dir, ignore) {
|
|
60
|
+
const files = readdirSync(dir)
|
|
61
|
+
if (ignore.includes(basename(dir))) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
files.forEach((file) => {
|
|
66
|
+
const filePath = join(dir, file)
|
|
67
|
+
|
|
68
|
+
// Skip if in ignore list
|
|
69
|
+
if (ignore.includes(file)) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const stats = statSync(filePath)
|
|
74
|
+
|
|
75
|
+
if (stats.isDirectory()) {
|
|
76
|
+
traverseDirectory(filePath, ignore)
|
|
77
|
+
} else if (extname(file) === '.md') {
|
|
78
|
+
const content = readFileSync(filePath, 'utf8')
|
|
79
|
+
// Find the first heading line (skip metadata comments)
|
|
80
|
+
const lines = content.split('\n')
|
|
81
|
+
let titleLine = lines.find((line) => line.startsWith('#')) || lines[0]
|
|
82
|
+
markdownFiles.push({
|
|
83
|
+
text: content,
|
|
84
|
+
title: titleLine.replace(TRIM_REGEX, ''),
|
|
85
|
+
filename: file,
|
|
86
|
+
path: filePath,
|
|
87
|
+
...metadata(content, filePath),
|
|
88
|
+
})
|
|
89
|
+
} else if (['.ts', '.js'].includes(extname(file))) {
|
|
90
|
+
const content = readFileSync(filePath, 'utf8')
|
|
91
|
+
// Match /*# ... */ blocks (inline markdown documentation)
|
|
92
|
+
const docs = content.match(/\/\*#[\s\S]+?\*\//g) || []
|
|
93
|
+
if (docs.length) {
|
|
94
|
+
const markdown = docs.map((s) => s.substring(3, s.length - 2).trim())
|
|
95
|
+
const text = markdown.join('\n\n---\n\n')
|
|
96
|
+
// Use filename as title for source files (more descriptive than first heading)
|
|
97
|
+
const fileTitle = basename(file, extname(file))
|
|
98
|
+
markdownFiles.push({
|
|
99
|
+
text,
|
|
100
|
+
title: `${fileTitle} (inline docs)`,
|
|
101
|
+
filename: file,
|
|
102
|
+
path: filePath,
|
|
103
|
+
...metadata(content, filePath),
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
dirs.forEach((dir) => {
|
|
111
|
+
traverseDirectory(dir, ignore)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return markdownFiles.sort(hierarchicalSort)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function saveAsJSON(data, outputFilePath) {
|
|
118
|
+
const jsonData = JSON.stringify(data, null, 2)
|
|
119
|
+
writeFileSync(outputFilePath, jsonData, 'utf8')
|
|
120
|
+
console.log(`Generated ${outputFilePath} with ${data.length} documents`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Directories to ignore
|
|
124
|
+
const ignore = [
|
|
125
|
+
'node_modules',
|
|
126
|
+
'dist',
|
|
127
|
+
'docs',
|
|
128
|
+
'third-party',
|
|
129
|
+
'.git',
|
|
130
|
+
'.archive',
|
|
131
|
+
'editors',
|
|
132
|
+
'demo',
|
|
133
|
+
'bin',
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
// Directories to search
|
|
137
|
+
const directoriesToSearch = ['.']
|
|
138
|
+
|
|
139
|
+
// Dedupe by normalized path
|
|
140
|
+
function dedupeByPath(docs) {
|
|
141
|
+
const seen = new Map()
|
|
142
|
+
for (const doc of docs) {
|
|
143
|
+
// Normalize path to avoid duplicates from different starting points
|
|
144
|
+
const normalizedPath = doc.path.replace(/^\.\//, '')
|
|
145
|
+
if (!seen.has(normalizedPath)) {
|
|
146
|
+
seen.set(normalizedPath, doc)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return Array.from(seen.values())
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Find all documentation and dedupe
|
|
153
|
+
const markdownFiles = dedupeByPath(
|
|
154
|
+
findMarkdownFiles(directoriesToSearch, ignore)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
// Save to demo/docs.json
|
|
158
|
+
const outputPath = './demo/docs.json'
|
|
159
|
+
saveAsJSON(markdownFiles, outputPath)
|
|
160
|
+
|
|
161
|
+
// List what was found
|
|
162
|
+
console.log('\nDocuments found:')
|
|
163
|
+
markdownFiles.forEach((doc, i) => {
|
|
164
|
+
const section = doc.section ? `[${doc.section}]` : ''
|
|
165
|
+
const group = doc.group ? `/${doc.group}` : ''
|
|
166
|
+
const navTitle = doc.navTitle ? ` → "${doc.navTitle}"` : ''
|
|
167
|
+
console.log(
|
|
168
|
+
` ${i + 1}. ${section}${group} ${doc.title}${navTitle} (${doc.filename})`
|
|
169
|
+
)
|
|
170
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Install AsyncJS syntax highlighting for Cursor
|
|
4
|
+
#
|
|
5
|
+
# Usage: npx ajs-install-cursor
|
|
6
|
+
# or: ./node_modules/.bin/ajs-install-cursor
|
|
7
|
+
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
# Find the real package directory (resolve symlinks for npx compatibility)
|
|
11
|
+
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
|
12
|
+
# Follow symlinks to get the real path
|
|
13
|
+
while [ -L "$SCRIPT_PATH" ]; do
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
|
15
|
+
SCRIPT_PATH="$(readlink "$SCRIPT_PATH")"
|
|
16
|
+
# Handle relative symlinks
|
|
17
|
+
[[ $SCRIPT_PATH != /* ]] && SCRIPT_PATH="$SCRIPT_DIR/$SCRIPT_PATH"
|
|
18
|
+
done
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
|
20
|
+
PACKAGE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
21
|
+
EXTENSION_SRC="$PACKAGE_DIR/editors/vscode"
|
|
22
|
+
|
|
23
|
+
# Determine Cursor extensions directory
|
|
24
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
25
|
+
CURSOR_EXT_DIR="$HOME/.cursor/extensions"
|
|
26
|
+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
27
|
+
CURSOR_EXT_DIR="$HOME/.cursor/extensions"
|
|
28
|
+
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
|
|
29
|
+
CURSOR_EXT_DIR="$APPDATA/Cursor/User/extensions"
|
|
30
|
+
else
|
|
31
|
+
echo "Unknown OS: $OSTYPE"
|
|
32
|
+
echo "Please manually copy $EXTENSION_SRC to your Cursor extensions directory"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
TARGET_DIR="$CURSOR_EXT_DIR/tosijs-ajs-0.1.0"
|
|
37
|
+
|
|
38
|
+
echo "Installing AsyncJS syntax highlighting for Cursor..."
|
|
39
|
+
echo " Source: $EXTENSION_SRC"
|
|
40
|
+
echo " Target: $TARGET_DIR"
|
|
41
|
+
|
|
42
|
+
# Check source exists
|
|
43
|
+
if [ ! -d "$EXTENSION_SRC" ]; then
|
|
44
|
+
echo "Error: Extension source not found at $EXTENSION_SRC"
|
|
45
|
+
echo ""
|
|
46
|
+
echo "If you installed via npm/npx, try running from your project directory:"
|
|
47
|
+
echo " ./node_modules/tjs-lang/bin/install-cursor.sh"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Create extensions directory if needed
|
|
52
|
+
mkdir -p "$CURSOR_EXT_DIR"
|
|
53
|
+
|
|
54
|
+
# Remove old version if exists
|
|
55
|
+
if [ -d "$TARGET_DIR" ]; then
|
|
56
|
+
echo " Removing old version..."
|
|
57
|
+
rm -rf "$TARGET_DIR"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Copy extension
|
|
61
|
+
cp -r "$EXTENSION_SRC" "$TARGET_DIR"
|
|
62
|
+
|
|
63
|
+
echo ""
|
|
64
|
+
echo "Installation complete!"
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Please restart Cursor to enable AsyncJS syntax highlighting."
|
|
67
|
+
echo ""
|
|
68
|
+
echo "Features:"
|
|
69
|
+
echo " - Syntax highlighting for .ajs files"
|
|
70
|
+
echo " - Embedded highlighting in ajs\`...\` template literals"
|
|
71
|
+
echo " - Error highlighting for forbidden syntax (new, class, async, etc.)"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Install AsyncJS syntax highlighting for VS Code
|
|
4
|
+
#
|
|
5
|
+
# Usage: npx ajs-install-vscode
|
|
6
|
+
# or: ./node_modules/.bin/ajs-install-vscode
|
|
7
|
+
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
# Find the real package directory (resolve symlinks for npx compatibility)
|
|
11
|
+
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
|
12
|
+
# Follow symlinks to get the real path
|
|
13
|
+
while [ -L "$SCRIPT_PATH" ]; do
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
|
15
|
+
SCRIPT_PATH="$(readlink "$SCRIPT_PATH")"
|
|
16
|
+
# Handle relative symlinks
|
|
17
|
+
[[ $SCRIPT_PATH != /* ]] && SCRIPT_PATH="$SCRIPT_DIR/$SCRIPT_PATH"
|
|
18
|
+
done
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
|
20
|
+
PACKAGE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
21
|
+
EXTENSION_SRC="$PACKAGE_DIR/editors/vscode"
|
|
22
|
+
|
|
23
|
+
# Determine VS Code extensions directory
|
|
24
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
25
|
+
VSCODE_EXT_DIR="$HOME/.vscode/extensions"
|
|
26
|
+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
27
|
+
VSCODE_EXT_DIR="$HOME/.vscode/extensions"
|
|
28
|
+
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
|
|
29
|
+
VSCODE_EXT_DIR="$APPDATA/Code/User/extensions"
|
|
30
|
+
else
|
|
31
|
+
echo "Unknown OS: $OSTYPE"
|
|
32
|
+
echo "Please manually copy $EXTENSION_SRC to your VS Code extensions directory"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
TARGET_DIR="$VSCODE_EXT_DIR/tosijs-ajs-0.1.0"
|
|
37
|
+
|
|
38
|
+
echo "Installing AsyncJS syntax highlighting for VS Code..."
|
|
39
|
+
echo " Source: $EXTENSION_SRC"
|
|
40
|
+
echo " Target: $TARGET_DIR"
|
|
41
|
+
|
|
42
|
+
# Check source exists
|
|
43
|
+
if [ ! -d "$EXTENSION_SRC" ]; then
|
|
44
|
+
echo "Error: Extension source not found at $EXTENSION_SRC"
|
|
45
|
+
echo ""
|
|
46
|
+
echo "If you installed via npm/npx, try running from your project directory:"
|
|
47
|
+
echo " ./node_modules/tjs-lang/bin/install-vscode.sh"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Create extensions directory if needed
|
|
52
|
+
mkdir -p "$VSCODE_EXT_DIR"
|
|
53
|
+
|
|
54
|
+
# Remove old version if exists
|
|
55
|
+
if [ -d "$TARGET_DIR" ]; then
|
|
56
|
+
echo " Removing old version..."
|
|
57
|
+
rm -rf "$TARGET_DIR"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Copy extension
|
|
61
|
+
cp -r "$EXTENSION_SRC" "$TARGET_DIR"
|
|
62
|
+
|
|
63
|
+
echo ""
|
|
64
|
+
echo "Installation complete!"
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Please restart VS Code to enable AsyncJS syntax highlighting."
|
|
67
|
+
echo ""
|
|
68
|
+
echo "Features:"
|
|
69
|
+
echo " - Syntax highlighting for .ajs files"
|
|
70
|
+
echo " - Embedded highlighting in ajs\`...\` template literals"
|
|
71
|
+
echo " - Error highlighting for forbidden syntax (new, class, async, etc.)"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Run with: bun select-local-models.ts
|
|
2
|
+
import { LocalModels } from '../src/batteries/models'
|
|
3
|
+
async function selectModels() {
|
|
4
|
+
console.log('🔥 Auditing local models...')
|
|
5
|
+
const localModels = new LocalModels()
|
|
6
|
+
await localModels.audit()
|
|
7
|
+
console.log('\n✅ Audit complete. Default models selected:')
|
|
8
|
+
try {
|
|
9
|
+
console.log(` - LLM: ${localModels.getLLM().id}`)
|
|
10
|
+
} catch (e) {
|
|
11
|
+
console.log(` - LLM: Not available`)
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
console.log(` - Structured LLM: ${localModels.getStructuredLLM().id}`)
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.log(` - Structured LLM: Not available`)
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
console.log(
|
|
20
|
+
` - Embedding: ${localModels.getEmbedding().id} (Dim: ${
|
|
21
|
+
localModels.getEmbedding().dimension
|
|
22
|
+
})`
|
|
23
|
+
)
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.log(` - Embedding: Not available`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await selectModels()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Run with: bun select-local-models.ts
|
|
2
|
+
import { LocalModels } from '../src/batteries/models'
|
|
3
|
+
|
|
4
|
+
async function selectModels() {
|
|
5
|
+
console.log('🔥 Auditing local models...')
|
|
6
|
+
const localModels = new LocalModels()
|
|
7
|
+
await localModels.audit()
|
|
8
|
+
|
|
9
|
+
console.log('\n✅ Audit complete. Default models selected:')
|
|
10
|
+
try {
|
|
11
|
+
console.log(` - LLM: ${localModels.getLLM().id}`)
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.log(` - LLM: Not available`)
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
console.log(` - Structured LLM: ${localModels.getStructuredLLM().id}`)
|
|
17
|
+
} catch (e) {
|
|
18
|
+
console.log(` - Structured LLM: Not available`)
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
console.log(
|
|
22
|
+
` - Embedding: ${localModels.getEmbedding().id} (Dim: ${
|
|
23
|
+
localModels.getEmbedding().dimension
|
|
24
|
+
})`
|
|
25
|
+
)
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.log(` - Embedding: Not available`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await selectModels()
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for TJS autocompletion
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'bun:test'
|
|
6
|
+
import { getCompletions, getSignatureHelp } from './src/autocomplete'
|
|
7
|
+
|
|
8
|
+
describe('getCompletions', () => {
|
|
9
|
+
describe('keyword completions', () => {
|
|
10
|
+
it('should suggest function keyword', () => {
|
|
11
|
+
const completions = getCompletions({
|
|
12
|
+
source: 'func',
|
|
13
|
+
position: 4,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const funcCompletion = completions.find((c) => c.label === 'function')
|
|
17
|
+
expect(funcCompletion).toBeDefined()
|
|
18
|
+
expect(funcCompletion?.kind).toBe('keyword')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should suggest test block', () => {
|
|
22
|
+
const completions = getCompletions({
|
|
23
|
+
source: 'tes',
|
|
24
|
+
position: 3,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const testCompletion = completions.find((c) => c.label === 'test')
|
|
28
|
+
expect(testCompletion).toBeDefined()
|
|
29
|
+
expect(testCompletion?.snippet).toContain("test('")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should suggest unsafe block', () => {
|
|
33
|
+
const completions = getCompletions({
|
|
34
|
+
source: 'uns',
|
|
35
|
+
position: 3,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const unsafeCompletion = completions.find((c) => c.label === 'unsafe')
|
|
39
|
+
expect(unsafeCompletion).toBeDefined()
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('type completions', () => {
|
|
44
|
+
it('should suggest types after colon', () => {
|
|
45
|
+
const completions = getCompletions({
|
|
46
|
+
source: 'function foo(x: ',
|
|
47
|
+
position: 16,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
expect(completions.some((c) => c.label === "''")).toBe(true)
|
|
51
|
+
expect(completions.some((c) => c.label === '0')).toBe(true)
|
|
52
|
+
expect(completions.some((c) => c.label === 'true')).toBe(true)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should suggest types after return arrow', () => {
|
|
56
|
+
const completions = getCompletions({
|
|
57
|
+
source: 'function foo() -> ',
|
|
58
|
+
position: 18,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
expect(completions.some((c) => c.label === "''")).toBe(true)
|
|
62
|
+
expect(completions.some((c) => c.label === '[]')).toBeFalsy() // Not a valid type
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('function completions', () => {
|
|
67
|
+
it('should extract functions from source', () => {
|
|
68
|
+
const source = `
|
|
69
|
+
function greet(name) { return name }
|
|
70
|
+
function add(a, b) { return a + b }
|
|
71
|
+
`
|
|
72
|
+
const completions = getCompletions({
|
|
73
|
+
source,
|
|
74
|
+
position: source.length,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
expect(completions.some((c) => c.label === 'greet')).toBe(true)
|
|
78
|
+
expect(completions.some((c) => c.label === 'add')).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('should include runtime functions', () => {
|
|
82
|
+
const completions = getCompletions({
|
|
83
|
+
source: 'is',
|
|
84
|
+
position: 2,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const isError = completions.find((c) => c.label === 'isError')
|
|
88
|
+
expect(isError).toBeDefined()
|
|
89
|
+
expect(isError?.kind).toBe('function')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('variable completions', () => {
|
|
94
|
+
it('should extract declared variables', () => {
|
|
95
|
+
const source = `
|
|
96
|
+
const foo = 1
|
|
97
|
+
let bar = 2
|
|
98
|
+
f
|
|
99
|
+
`
|
|
100
|
+
const completions = getCompletions({
|
|
101
|
+
source,
|
|
102
|
+
position: source.length - 1,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
expect(completions.some((c) => c.label === 'foo')).toBe(true)
|
|
106
|
+
expect(completions.some((c) => c.label === 'bar')).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('metadata-based completions', () => {
|
|
111
|
+
it('should use __tjs metadata for function info', () => {
|
|
112
|
+
const completions = getCompletions({
|
|
113
|
+
source: 'gre',
|
|
114
|
+
position: 3,
|
|
115
|
+
metadata: {
|
|
116
|
+
greet: {
|
|
117
|
+
params: {
|
|
118
|
+
name: { type: 'string', required: true },
|
|
119
|
+
},
|
|
120
|
+
returns: { type: 'string' },
|
|
121
|
+
description: 'Greet someone',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const greet = completions.find((c) => c.label === 'greet')
|
|
127
|
+
expect(greet).toBeDefined()
|
|
128
|
+
expect(greet?.detail).toContain('name')
|
|
129
|
+
expect(greet?.detail).toContain('string')
|
|
130
|
+
expect(greet?.documentation).toBe('Greet someone')
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
describe('expect matchers', () => {
|
|
135
|
+
it('should suggest matchers after expect().', () => {
|
|
136
|
+
const completions = getCompletions({
|
|
137
|
+
source: 'expect(x).',
|
|
138
|
+
position: 10,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
expect(completions.some((c) => c.label === 'toBe')).toBe(true)
|
|
142
|
+
expect(completions.some((c) => c.label === 'toEqual')).toBe(true)
|
|
143
|
+
expect(completions.some((c) => c.label === 'toContain')).toBe(true)
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('filtering', () => {
|
|
148
|
+
it('should filter by prefix', () => {
|
|
149
|
+
const completions = getCompletions({
|
|
150
|
+
source: 'to',
|
|
151
|
+
position: 2,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Should not include function, let, etc.
|
|
155
|
+
expect(
|
|
156
|
+
completions.every((c) => c.label.toLowerCase().startsWith('to'))
|
|
157
|
+
).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should sort by relevance', () => {
|
|
161
|
+
const completions = getCompletions({
|
|
162
|
+
source: 'function foo() {}\nfoo',
|
|
163
|
+
position: 21,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// Function should come before keyword
|
|
167
|
+
const fooIndex = completions.findIndex((c) => c.label === 'foo')
|
|
168
|
+
const funcIndex = completions.findIndex((c) => c.label === 'function')
|
|
169
|
+
|
|
170
|
+
// Our foo function should appear (even if filtered out, the fn should be found)
|
|
171
|
+
expect(fooIndex).toBeGreaterThanOrEqual(0)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('getSignatureHelp', () => {
|
|
177
|
+
it('should provide signature for function call', () => {
|
|
178
|
+
const help = getSignatureHelp({
|
|
179
|
+
source: 'greet(',
|
|
180
|
+
position: 6,
|
|
181
|
+
metadata: {
|
|
182
|
+
greet: {
|
|
183
|
+
params: {
|
|
184
|
+
name: { type: 'string', required: true },
|
|
185
|
+
greeting: { type: 'string', required: false },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
expect(help).toBeDefined()
|
|
192
|
+
expect(help?.signature).toContain('greet')
|
|
193
|
+
expect(help?.signature).toContain('name')
|
|
194
|
+
expect(help?.activeParam).toBe(0)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('should track active parameter', () => {
|
|
198
|
+
const help = getSignatureHelp({
|
|
199
|
+
source: 'greet("Alice", ',
|
|
200
|
+
position: 15,
|
|
201
|
+
metadata: {
|
|
202
|
+
greet: {
|
|
203
|
+
params: {
|
|
204
|
+
name: { type: 'string', required: true },
|
|
205
|
+
greeting: { type: 'string', required: false },
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(help?.activeParam).toBe(1)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should return null outside function call', () => {
|
|
215
|
+
const help = getSignatureHelp({
|
|
216
|
+
source: 'const x = 1',
|
|
217
|
+
position: 11,
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
expect(help).toBeNull()
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should provide help for runtime functions', () => {
|
|
224
|
+
const help = getSignatureHelp({
|
|
225
|
+
source: 'isError(',
|
|
226
|
+
position: 8,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
expect(help).toBeDefined()
|
|
230
|
+
expect(help?.signature).toContain('isError')
|
|
231
|
+
})
|
|
232
|
+
})
|