@soederpop/luca 0.0.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/CLAUDE.md +71 -0
- package/README.md +78 -0
- package/bun.lock +2928 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-scaffolds.ts +154 -0
- package/commands/generate-api-docs.ts +114 -0
- package/commands/update-introspection.ts +67 -0
- package/docs/CLI.md +335 -0
- package/docs/README.md +88 -0
- package/docs/TABLE-OF-CONTENTS.md +157 -0
- package/docs/apis/clients/elevenlabs.md +84 -0
- package/docs/apis/clients/graph.md +56 -0
- package/docs/apis/clients/openai.md +69 -0
- package/docs/apis/clients/rest.md +41 -0
- package/docs/apis/clients/websocket.md +107 -0
- package/docs/apis/features/agi/assistant.md +471 -0
- package/docs/apis/features/agi/assistants-manager.md +154 -0
- package/docs/apis/features/agi/claude-code.md +602 -0
- package/docs/apis/features/agi/conversation-history.md +352 -0
- package/docs/apis/features/agi/conversation.md +333 -0
- package/docs/apis/features/agi/docs-reader.md +121 -0
- package/docs/apis/features/agi/openai-codex.md +318 -0
- package/docs/apis/features/agi/openapi.md +138 -0
- package/docs/apis/features/agi/semantic-search.md +387 -0
- package/docs/apis/features/agi/skills-library.md +216 -0
- package/docs/apis/features/node/container-link.md +133 -0
- package/docs/apis/features/node/content-db.md +313 -0
- package/docs/apis/features/node/disk-cache.md +379 -0
- package/docs/apis/features/node/dns.md +651 -0
- package/docs/apis/features/node/docker.md +705 -0
- package/docs/apis/features/node/downloader.md +81 -0
- package/docs/apis/features/node/esbuild.md +59 -0
- package/docs/apis/features/node/file-manager.md +182 -0
- package/docs/apis/features/node/fs.md +581 -0
- package/docs/apis/features/node/git.md +330 -0
- package/docs/apis/features/node/google-auth.md +174 -0
- package/docs/apis/features/node/google-calendar.md +187 -0
- package/docs/apis/features/node/google-docs.md +151 -0
- package/docs/apis/features/node/google-drive.md +225 -0
- package/docs/apis/features/node/google-sheets.md +179 -0
- package/docs/apis/features/node/grep.md +290 -0
- package/docs/apis/features/node/helpers.md +135 -0
- package/docs/apis/features/node/ink.md +334 -0
- package/docs/apis/features/node/ipc-socket.md +260 -0
- package/docs/apis/features/node/json-tree.md +86 -0
- package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
- package/docs/apis/features/node/networking.md +281 -0
- package/docs/apis/features/node/nlp.md +133 -0
- package/docs/apis/features/node/opener.md +97 -0
- package/docs/apis/features/node/os.md +118 -0
- package/docs/apis/features/node/package-finder.md +402 -0
- package/docs/apis/features/node/postgres.md +212 -0
- package/docs/apis/features/node/proc.md +430 -0
- package/docs/apis/features/node/process-manager.md +210 -0
- package/docs/apis/features/node/python.md +278 -0
- package/docs/apis/features/node/repl.md +88 -0
- package/docs/apis/features/node/runpod.md +673 -0
- package/docs/apis/features/node/secure-shell.md +169 -0
- package/docs/apis/features/node/semantic-search.md +401 -0
- package/docs/apis/features/node/sqlite.md +211 -0
- package/docs/apis/features/node/telegram.md +254 -0
- package/docs/apis/features/node/tts.md +118 -0
- package/docs/apis/features/node/ui.md +703 -0
- package/docs/apis/features/node/vault.md +64 -0
- package/docs/apis/features/node/vm.md +84 -0
- package/docs/apis/features/node/window-manager.md +337 -0
- package/docs/apis/features/node/yaml-tree.md +85 -0
- package/docs/apis/features/node/yaml.md +176 -0
- package/docs/apis/features/web/asset-loader.md +47 -0
- package/docs/apis/features/web/container-link.md +133 -0
- package/docs/apis/features/web/esbuild.md +59 -0
- package/docs/apis/features/web/helpers.md +135 -0
- package/docs/apis/features/web/network.md +30 -0
- package/docs/apis/features/web/speech.md +55 -0
- package/docs/apis/features/web/vault.md +64 -0
- package/docs/apis/features/web/vm.md +84 -0
- package/docs/apis/features/web/voice.md +67 -0
- package/docs/apis/servers/express.md +127 -0
- package/docs/apis/servers/mcp.md +213 -0
- package/docs/apis/servers/websocket.md +99 -0
- package/docs/documentation-audit.md +134 -0
- package/docs/examples/content-db.md +77 -0
- package/docs/examples/disk-cache.md +83 -0
- package/docs/examples/docker.md +101 -0
- package/docs/examples/downloader.md +70 -0
- package/docs/examples/esbuild.md +80 -0
- package/docs/examples/file-manager.md +82 -0
- package/docs/examples/fs.md +83 -0
- package/docs/examples/git.md +85 -0
- package/docs/examples/google-auth.md +88 -0
- package/docs/examples/google-calendar.md +94 -0
- package/docs/examples/google-docs.md +82 -0
- package/docs/examples/google-drive.md +96 -0
- package/docs/examples/google-sheets.md +95 -0
- package/docs/examples/grep.md +85 -0
- package/docs/examples/ink-blocks.md +75 -0
- package/docs/examples/ink-renderer.md +41 -0
- package/docs/examples/ink.md +103 -0
- package/docs/examples/ipc-socket.md +103 -0
- package/docs/examples/json-tree.md +91 -0
- package/docs/examples/launcher-app-command-listener.md +120 -0
- package/docs/examples/networking.md +58 -0
- package/docs/examples/nlp.md +91 -0
- package/docs/examples/opener.md +78 -0
- package/docs/examples/os.md +72 -0
- package/docs/examples/package-finder.md +89 -0
- package/docs/examples/port-exposer.md +89 -0
- package/docs/examples/postgres.md +91 -0
- package/docs/examples/proc.md +81 -0
- package/docs/examples/process-manager.md +79 -0
- package/docs/examples/python.md +91 -0
- package/docs/examples/repl.md +93 -0
- package/docs/examples/runpod.md +119 -0
- package/docs/examples/secure-shell.md +92 -0
- package/docs/examples/sqlite.md +86 -0
- package/docs/examples/telegram.md +77 -0
- package/docs/examples/tts.md +86 -0
- package/docs/examples/ui.md +80 -0
- package/docs/examples/vault.md +70 -0
- package/docs/examples/vm.md +86 -0
- package/docs/examples/window-manager.md +125 -0
- package/docs/examples/yaml-tree.md +93 -0
- package/docs/examples/yaml.md +104 -0
- package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
- package/docs/ideas/container-use-api.md +9 -0
- package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
- package/docs/ideas/feature-stacks.md +22 -0
- package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
- package/docs/ideas/mcp-design.md +9 -0
- package/docs/ideas/web-container-debugging-feature.md +13 -0
- package/docs/introspection-audit.md +49 -0
- package/docs/introspection.md +154 -0
- package/docs/mcp/readme.md +162 -0
- package/docs/models.ts +38 -0
- package/docs/philosophy.md +85 -0
- package/docs/principles.md +7 -0
- package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
- package/docs/prompts/mcp-test-easy-command.md +27 -0
- package/docs/reports/assistant-bugs.md +38 -0
- package/docs/reports/attach-pattern-usage.md +18 -0
- package/docs/reports/code-audit-results.md +391 -0
- package/docs/reports/introspection-audit-tasks.md +378 -0
- package/docs/reports/luca-mcp-improvements.md +128 -0
- package/docs/scaffolds/client.md +140 -0
- package/docs/scaffolds/command.md +106 -0
- package/docs/scaffolds/endpoint.md +176 -0
- package/docs/scaffolds/feature.md +148 -0
- package/docs/scaffolds/server.md +187 -0
- package/docs/tasks/web-container-helper-discovery.md +71 -0
- package/docs/todos.md +1 -0
- package/docs/tutorials/01-getting-started.md +106 -0
- package/docs/tutorials/02-container.md +210 -0
- package/docs/tutorials/03-scripts.md +194 -0
- package/docs/tutorials/04-features-overview.md +196 -0
- package/docs/tutorials/05-state-and-events.md +171 -0
- package/docs/tutorials/06-servers.md +157 -0
- package/docs/tutorials/07-endpoints.md +198 -0
- package/docs/tutorials/08-commands.md +171 -0
- package/docs/tutorials/09-clients.md +162 -0
- package/docs/tutorials/10-creating-features.md +198 -0
- package/docs/tutorials/11-contentbase.md +191 -0
- package/docs/tutorials/12-assistants.md +215 -0
- package/docs/tutorials/13-introspection.md +147 -0
- package/docs/tutorials/14-type-system.md +174 -0
- package/docs/tutorials/15-project-patterns.md +222 -0
- package/docs/tutorials/16-google-features.md +534 -0
- package/docs/tutorials/17-tui-blocks.md +530 -0
- package/docs/tutorials/18-semantic-search.md +334 -0
- package/index.ts +1 -0
- package/luca.console.ts +9 -0
- package/main.py +6 -0
- package/package.json +154 -0
- package/pyproject.toml +7 -0
- package/scripts/animations/chrome-glitch.ts +55 -0
- package/scripts/animations/index.ts +16 -0
- package/scripts/animations/neon-pulse.ts +64 -0
- package/scripts/animations/types.ts +6 -0
- package/scripts/build-web.ts +28 -0
- package/scripts/examples/ask-luca-expert.ts +42 -0
- package/scripts/examples/assistant-questions.ts +12 -0
- package/scripts/examples/excalidraw-expert.ts +75 -0
- package/scripts/examples/expert-chat.ts +0 -0
- package/scripts/examples/file-manager.ts +14 -0
- package/scripts/examples/ideas.ts +12 -0
- package/scripts/examples/interactive-chat.ts +20 -0
- package/scripts/examples/openai-tool-calls.ts +113 -0
- package/scripts/examples/opening-a-web-browser.ts +5 -0
- package/scripts/examples/telegram-bot.ts +79 -0
- package/scripts/examples/telegram-ink-ui.ts +302 -0
- package/scripts/examples/using-assistant-with-mcp.ts +560 -0
- package/scripts/examples/using-claude-code.ts +10 -0
- package/scripts/examples/using-contentdb.ts +35 -0
- package/scripts/examples/using-conversations.ts +35 -0
- package/scripts/examples/using-disk-cache.ts +10 -0
- package/scripts/examples/using-docker-shell.ts +75 -0
- package/scripts/examples/using-elevenlabs.ts +25 -0
- package/scripts/examples/using-google-calendar.ts +57 -0
- package/scripts/examples/using-google-docs.ts +74 -0
- package/scripts/examples/using-google-drive.ts +74 -0
- package/scripts/examples/using-google-sheets.ts +89 -0
- package/scripts/examples/using-nlp.ts +55 -0
- package/scripts/examples/using-ollama.ts +10 -0
- package/scripts/examples/using-openai-codex.ts +23 -0
- package/scripts/examples/using-postgres.ts +55 -0
- package/scripts/examples/using-runpod.ts +32 -0
- package/scripts/examples/using-tts.ts +40 -0
- package/scripts/examples/vm-loading-esm-modules.ts +16 -0
- package/scripts/scaffold.ts +391 -0
- package/scripts/scratch.ts +15 -0
- package/scripts/test-command-listener.ts +123 -0
- package/scripts/test-window-manager-lifecycle.ts +86 -0
- package/scripts/test-window-manager.ts +43 -0
- package/scripts/update-introspection-data.ts +58 -0
- package/src/agi/README.md +14 -0
- package/src/agi/container.server.ts +114 -0
- package/src/agi/endpoints/ask.ts +60 -0
- package/src/agi/endpoints/conversations/[id].ts +45 -0
- package/src/agi/endpoints/conversations.ts +31 -0
- package/src/agi/endpoints/experts.ts +37 -0
- package/src/agi/features/assistant.ts +767 -0
- package/src/agi/features/assistants-manager.ts +260 -0
- package/src/agi/features/claude-code.ts +1111 -0
- package/src/agi/features/conversation-history.ts +497 -0
- package/src/agi/features/conversation.ts +799 -0
- package/src/agi/features/openai-codex.ts +631 -0
- package/src/agi/features/openapi.ts +438 -0
- package/src/agi/features/skills-library.ts +425 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/token-counter.ts +122 -0
- package/src/browser.ts +25 -0
- package/src/bus.ts +100 -0
- package/src/cli/cli.ts +70 -0
- package/src/client.ts +461 -0
- package/src/clients/civitai/index.ts +541 -0
- package/src/clients/client-template.ts +41 -0
- package/src/clients/comfyui/index.ts +597 -0
- package/src/clients/elevenlabs/index.ts +291 -0
- package/src/clients/openai/index.ts +451 -0
- package/src/clients/supabase/index.ts +366 -0
- package/src/command.ts +164 -0
- package/src/commands/chat.ts +182 -0
- package/src/commands/console.ts +192 -0
- package/src/commands/describe.ts +433 -0
- package/src/commands/eval.ts +116 -0
- package/src/commands/help.ts +214 -0
- package/src/commands/index.ts +14 -0
- package/src/commands/mcp.ts +64 -0
- package/src/commands/prompt.ts +807 -0
- package/src/commands/run.ts +257 -0
- package/src/commands/sandbox-mcp.ts +439 -0
- package/src/commands/scaffold.ts +79 -0
- package/src/commands/serve.ts +172 -0
- package/src/container.ts +781 -0
- package/src/endpoint.ts +340 -0
- package/src/feature.ts +75 -0
- package/src/hash-object.ts +97 -0
- package/src/helper.ts +543 -0
- package/src/introspection/generated.agi.ts +23388 -0
- package/src/introspection/generated.node.ts +18899 -0
- package/src/introspection/generated.web.ts +2021 -0
- package/src/introspection/index.ts +256 -0
- package/src/introspection/scan.ts +912 -0
- package/src/node/container.ts +354 -0
- package/src/node/feature.ts +13 -0
- package/src/node/features/container-link.ts +558 -0
- package/src/node/features/content-db.ts +475 -0
- package/src/node/features/disk-cache.ts +382 -0
- package/src/node/features/dns.ts +655 -0
- package/src/node/features/docker.ts +912 -0
- package/src/node/features/downloader.ts +92 -0
- package/src/node/features/esbuild.ts +68 -0
- package/src/node/features/file-manager.ts +357 -0
- package/src/node/features/fs.ts +534 -0
- package/src/node/features/git.ts +492 -0
- package/src/node/features/google-auth.ts +502 -0
- package/src/node/features/google-calendar.ts +300 -0
- package/src/node/features/google-docs.ts +404 -0
- package/src/node/features/google-drive.ts +339 -0
- package/src/node/features/google-sheets.ts +279 -0
- package/src/node/features/grep.ts +406 -0
- package/src/node/features/helpers.ts +374 -0
- package/src/node/features/ink.ts +490 -0
- package/src/node/features/ipc-socket.ts +459 -0
- package/src/node/features/json-tree.ts +188 -0
- package/src/node/features/launcher-app-command-listener.ts +388 -0
- package/src/node/features/networking.ts +925 -0
- package/src/node/features/nlp.ts +211 -0
- package/src/node/features/opener.ts +166 -0
- package/src/node/features/os.ts +157 -0
- package/src/node/features/package-finder.ts +539 -0
- package/src/node/features/port-exposer.ts +342 -0
- package/src/node/features/postgres.ts +273 -0
- package/src/node/features/proc.ts +502 -0
- package/src/node/features/process-manager.ts +542 -0
- package/src/node/features/python.ts +444 -0
- package/src/node/features/repl.ts +194 -0
- package/src/node/features/runpod.ts +802 -0
- package/src/node/features/secure-shell.ts +248 -0
- package/src/node/features/semantic-search.ts +924 -0
- package/src/node/features/sqlite.ts +289 -0
- package/src/node/features/telegram.ts +342 -0
- package/src/node/features/tts.ts +184 -0
- package/src/node/features/ui.ts +857 -0
- package/src/node/features/vault.ts +164 -0
- package/src/node/features/vm.ts +312 -0
- package/src/node/features/window-manager.ts +804 -0
- package/src/node/features/yaml-tree.ts +149 -0
- package/src/node/features/yaml.ts +132 -0
- package/src/node.ts +70 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +199 -0
- package/src/scaffolds/generated.ts +1613 -0
- package/src/scaffolds/template.ts +37 -0
- package/src/schemas/base.ts +255 -0
- package/src/server.ts +135 -0
- package/src/servers/express.ts +209 -0
- package/src/servers/mcp.ts +805 -0
- package/src/servers/socket.ts +120 -0
- package/src/state.ts +101 -0
- package/src/web/clients/socket.ts +82 -0
- package/src/web/container.ts +74 -0
- package/src/web/extension.ts +30 -0
- package/src/web/feature.ts +12 -0
- package/src/web/features/asset-loader.ts +64 -0
- package/src/web/features/container-link.ts +385 -0
- package/src/web/features/esbuild.ts +79 -0
- package/src/web/features/helpers.ts +267 -0
- package/src/web/features/network.ts +61 -0
- package/src/web/features/speech.ts +87 -0
- package/src/web/features/vault.ts +189 -0
- package/src/web/features/vm.ts +78 -0
- package/src/web/features/voice-recognition.ts +129 -0
- package/src/web/shims/isomorphic-vm.ts +149 -0
- package/test/bus.test.ts +134 -0
- package/test/clients-servers.test.ts +216 -0
- package/test/container-link.test.ts +274 -0
- package/test/features.test.ts +160 -0
- package/test/integration.test.ts +787 -0
- package/test/node-container.test.ts +121 -0
- package/test/rate-limit.test.ts +272 -0
- package/test/semantic-search.test.ts +550 -0
- package/test/state.test.ts +121 -0
- package/test-integration/assistant.test.ts +138 -0
- package/test-integration/assistants-manager.test.ts +123 -0
- package/test-integration/claude-code.test.ts +98 -0
- package/test-integration/conversation-history.test.ts +205 -0
- package/test-integration/conversation.test.ts +137 -0
- package/test-integration/elevenlabs.test.ts +55 -0
- package/test-integration/google-services.test.ts +80 -0
- package/test-integration/helpers.ts +89 -0
- package/test-integration/openai-codex.test.ts +93 -0
- package/test-integration/runpod.test.ts +58 -0
- package/test-integration/server-endpoints.test.ts +97 -0
- package/test-integration/skills-library.test.ts +157 -0
- package/test-integration/telegram.test.ts +46 -0
- package/tsconfig.json +58 -0
- package/uv.lock +8 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Feature, features } from '../feature.js'
|
|
2
|
+
import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
|
|
3
|
+
import fetch from 'cross-fetch'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A feature that provides file downloading capabilities from URLs.
|
|
7
|
+
*
|
|
8
|
+
* The Downloader feature allows you to fetch files from remote URLs and save them
|
|
9
|
+
* to the local filesystem. It handles the network request, buffering, and file writing
|
|
10
|
+
* operations automatically.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Enable the downloader feature
|
|
15
|
+
* const downloader = container.feature('downloader')
|
|
16
|
+
*
|
|
17
|
+
* // Download a file
|
|
18
|
+
* const localPath = await downloader.download(
|
|
19
|
+
* 'https://example.com/image.jpg',
|
|
20
|
+
* 'downloads/image.jpg'
|
|
21
|
+
* )
|
|
22
|
+
* console.log(`File saved to: ${localPath}`)
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @extends Feature
|
|
26
|
+
*/
|
|
27
|
+
export class Downloader extends Feature {
|
|
28
|
+
/**
|
|
29
|
+
* The shortcut path for accessing this feature through the container.
|
|
30
|
+
*
|
|
31
|
+
* @static
|
|
32
|
+
* @readonly
|
|
33
|
+
* @type {string}
|
|
34
|
+
*/
|
|
35
|
+
static override shortcut = 'features.downloader' as const
|
|
36
|
+
static override stateSchema = FeatureStateSchema
|
|
37
|
+
static override optionsSchema = FeatureOptionsSchema
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Downloads a file from a URL and saves it to the specified local path.
|
|
41
|
+
*
|
|
42
|
+
* This method fetches the file from the provided URL, converts it to a buffer,
|
|
43
|
+
* and writes it to the filesystem at the target path. The target path is resolved
|
|
44
|
+
* relative to the container's configured paths.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} url - The URL to download the file from. Must be a valid HTTP/HTTPS URL.
|
|
47
|
+
* @param {string} targetPath - The local file path where the downloaded file should be saved.
|
|
48
|
+
* This path will be resolved relative to the container's base path.
|
|
49
|
+
*
|
|
50
|
+
* @returns {Promise<string>} A promise that resolves to the absolute path of the saved file.
|
|
51
|
+
*
|
|
52
|
+
* @throws {Error} Throws an error if the URL is invalid or unreachable.
|
|
53
|
+
* @throws {Error} Throws an error if the target directory doesn't exist or is not writable.
|
|
54
|
+
* @throws {Error} Throws an error if the network request fails or times out.
|
|
55
|
+
* @throws {Error} Throws an error if there's insufficient disk space to save the file.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // Download an image file
|
|
60
|
+
* const imagePath = await downloader.download(
|
|
61
|
+
* 'https://example.com/photo.jpg',
|
|
62
|
+
* 'images/downloaded-photo.jpg'
|
|
63
|
+
* )
|
|
64
|
+
*
|
|
65
|
+
* // Download a document
|
|
66
|
+
* const docPath = await downloader.download(
|
|
67
|
+
* 'https://api.example.com/files/document.pdf',
|
|
68
|
+
* 'documents/report.pdf'
|
|
69
|
+
* )
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @since 1.0.0
|
|
73
|
+
*/
|
|
74
|
+
async download(url: string, targetPath: string) {
|
|
75
|
+
const buffer = await fetch(url).then(res => res.arrayBuffer())
|
|
76
|
+
await this.container.fs.writeFileAsync(
|
|
77
|
+
this.container.paths.resolve(targetPath),
|
|
78
|
+
Buffer.from(buffer)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return this.container.paths.resolve(targetPath)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Registers the Downloader feature with the features registry.
|
|
88
|
+
* This makes the feature available for use in containers via `container.use('downloader')`.
|
|
89
|
+
*
|
|
90
|
+
* @type {typeof Downloader}
|
|
91
|
+
*/
|
|
92
|
+
export default features.register('downloader', Downloader)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild-wasm'
|
|
2
|
+
import { Feature, features } from '../feature.js'
|
|
3
|
+
import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
|
|
4
|
+
import { NodeContainer } from '../container.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A Feature for compiling typescript / esm modules, etc to JavaScript
|
|
8
|
+
* that the container can run at runtime. Uses esbuild for fast, reliable
|
|
9
|
+
* TypeScript/ESM transformation with full format support (esm, cjs, iife).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const esbuild = container.feature('esbuild')
|
|
14
|
+
* const result = esbuild.transformSync('const x: number = 1')
|
|
15
|
+
* console.log(result.code) // 'const x = 1;\n'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export class ESBuild extends Feature {
|
|
19
|
+
static override shortcut = 'features.esbuild' as const
|
|
20
|
+
static override stateSchema = FeatureStateSchema
|
|
21
|
+
static override optionsSchema = FeatureOptionsSchema
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Attaches the ESBuild feature to a NodeContainer instance.
|
|
25
|
+
*
|
|
26
|
+
* @param c - The NodeContainer to attach to
|
|
27
|
+
* @returns The container for method chaining
|
|
28
|
+
*/
|
|
29
|
+
static attach(c: NodeContainer) {
|
|
30
|
+
return c
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Transform code synchronously
|
|
35
|
+
* @param code - The code to transform
|
|
36
|
+
* @param options - The options to pass to esbuild
|
|
37
|
+
* @returns The transformed code
|
|
38
|
+
*/
|
|
39
|
+
transformSync(code: string, options?: esbuild.TransformOptions) {
|
|
40
|
+
return esbuild.transformSync(code, {
|
|
41
|
+
loader: 'ts',
|
|
42
|
+
format: 'esm',
|
|
43
|
+
target: 'es2020',
|
|
44
|
+
sourcemap: false,
|
|
45
|
+
minify: false,
|
|
46
|
+
...options
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Transform code asynchronously
|
|
52
|
+
* @param code - The code to transform
|
|
53
|
+
* @param options - The options to pass to esbuild
|
|
54
|
+
* @returns The transformed code
|
|
55
|
+
*/
|
|
56
|
+
async transform(code: string, options?: esbuild.TransformOptions) {
|
|
57
|
+
return esbuild.transform(code, {
|
|
58
|
+
loader: 'ts',
|
|
59
|
+
format: 'esm',
|
|
60
|
+
target: 'es2020',
|
|
61
|
+
sourcemap: false,
|
|
62
|
+
minify: false,
|
|
63
|
+
...options
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default features.register('esbuild', ESBuild)
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
|
|
3
|
+
import { State } from "../../state.js";
|
|
4
|
+
import { Feature, features } from "../feature.js";
|
|
5
|
+
import { parse, relative } from "path";
|
|
6
|
+
import { statSync } from "fs";
|
|
7
|
+
import micromatch from "micromatch";
|
|
8
|
+
import { castArray } from "lodash-es";
|
|
9
|
+
import chokidar from "chokidar";
|
|
10
|
+
import type { FSWatcher } from "chokidar";
|
|
11
|
+
|
|
12
|
+
type File = {
|
|
13
|
+
absolutePath: string;
|
|
14
|
+
relativePath: string;
|
|
15
|
+
relativeDirname: string;
|
|
16
|
+
dirname: string;
|
|
17
|
+
name: string;
|
|
18
|
+
extension: string;
|
|
19
|
+
size: number;
|
|
20
|
+
modifiedAt: Date;
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const FileManagerStateSchema = FeatureStateSchema.extend({
|
|
25
|
+
/** Whether the file manager has completed its initial scan */
|
|
26
|
+
started: z.boolean().optional().describe('Whether the file manager has completed its initial scan'),
|
|
27
|
+
/** Whether the file manager is currently scanning files */
|
|
28
|
+
starting: z.boolean().optional().describe('Whether the file manager is currently scanning files'),
|
|
29
|
+
/** Whether the file watcher is actively monitoring for changes */
|
|
30
|
+
watching: z.boolean().optional().describe('Whether the file watcher is actively monitoring for changes'),
|
|
31
|
+
/** Whether the initial scan failed */
|
|
32
|
+
failed: z.boolean().optional().describe('Whether the initial file scan failed'),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export const FileManagerOptionsSchema = FeatureOptionsSchema.extend({
|
|
36
|
+
/** Glob patterns to exclude from file scanning */
|
|
37
|
+
exclude: z.union([z.string(), z.array(z.string())]).optional().describe('Glob patterns to exclude from file scanning'),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export type FileManagerState = z.infer<typeof FileManagerStateSchema>
|
|
41
|
+
export type FileManagerOptions = z.infer<typeof FileManagerOptionsSchema>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The FileManager feature creates a database like index of all of the files in the project,
|
|
45
|
+
* and provides metadata about these files, and also provides a way to watch for changes to the files.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const fileManager = container.feature('fileManager')
|
|
50
|
+
* await fileManager.start()
|
|
51
|
+
*
|
|
52
|
+
* const fileIds = fileManager.fileIds
|
|
53
|
+
* const typescriptFiles = fileManager.matchFiles("**ts")
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export class FileManager<
|
|
57
|
+
T extends FileManagerState = FileManagerState,
|
|
58
|
+
K extends FileManagerOptions = FileManagerOptions
|
|
59
|
+
> extends Feature<T, K> {
|
|
60
|
+
|
|
61
|
+
static override shortcut = 'features.fileManager' as const
|
|
62
|
+
static override stateSchema = FileManagerStateSchema
|
|
63
|
+
static override optionsSchema = FileManagerOptionsSchema
|
|
64
|
+
|
|
65
|
+
files: State<Record<string, File>> = new State<Record<string, File>>({
|
|
66
|
+
initialState: {},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** Returns an array of all relative file paths indexed by the file manager. */
|
|
70
|
+
get fileIds() {
|
|
71
|
+
return Array.from(this.files.keys());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Returns an array of all file metadata objects indexed by the file manager. */
|
|
75
|
+
get fileObjects() {
|
|
76
|
+
return Array.from(this.files.values());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Matches the file IDs against the pattern(s) provided
|
|
81
|
+
* @param {string | string[]} patterns - The patterns to match against the file IDs
|
|
82
|
+
* @returns {string[]} The file IDs that match the patterns
|
|
83
|
+
*/
|
|
84
|
+
match(patterns: string | string[]) {
|
|
85
|
+
return micromatch(this.files.keys(), patterns);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Matches the file IDs against the pattern(s) provided and returns the file objects for each.
|
|
90
|
+
*
|
|
91
|
+
* @param {string | string[]} patterns - The patterns to match against the file IDs
|
|
92
|
+
* @returns {File[]} The file objects that match the patterns
|
|
93
|
+
*/
|
|
94
|
+
matchFiles(patterns: string | string[]) {
|
|
95
|
+
const fileIds = this.match(Array.isArray(patterns) ? patterns : [patterns]);
|
|
96
|
+
return fileIds.map((fileId) => this.files.get(fileId));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns the directory IDs for all of the files in the project.
|
|
101
|
+
*/
|
|
102
|
+
get directoryIds() {
|
|
103
|
+
return Array.from(
|
|
104
|
+
new Set(
|
|
105
|
+
this.files
|
|
106
|
+
.values()
|
|
107
|
+
.map((file) => this.container.paths.relative(file.dirname))
|
|
108
|
+
.filter(v => v.length)
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Returns an array of unique file extensions found across all indexed files. */
|
|
114
|
+
get uniqueExtensions() {
|
|
115
|
+
return Array.from(
|
|
116
|
+
new Set(
|
|
117
|
+
this.files.values().map((file) => file.extension)
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Whether the file manager has completed its initial scan. */
|
|
123
|
+
get isStarted() {
|
|
124
|
+
return !!this.state.get("started");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Whether the file manager is currently performing its initial scan. */
|
|
128
|
+
get isStarting() {
|
|
129
|
+
return !!this.state.get("starting");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Whether the file watcher is actively monitoring for changes. */
|
|
133
|
+
get isWatching() {
|
|
134
|
+
return !!this.state.get("watching");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Starts the file manager and scans the files in the project.
|
|
139
|
+
* @param {object} [options={}] - Options for the file manager
|
|
140
|
+
* @param {string | string[]} [options.exclude] - The patterns to exclude from the scan
|
|
141
|
+
* @returns {Promise<FileManager>} The file manager instance
|
|
142
|
+
*/
|
|
143
|
+
async start(options: { exclude?: string | string[] } = {}) {
|
|
144
|
+
if (this.isStarted) {
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (this.isStarting) {
|
|
149
|
+
await this.waitFor("started");
|
|
150
|
+
return this;
|
|
151
|
+
} else {
|
|
152
|
+
this.state.set("starting", true);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await this.scanFiles(options);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error(error);
|
|
159
|
+
this.state.set("failed", true);
|
|
160
|
+
} finally {
|
|
161
|
+
this.state.set("started", true);
|
|
162
|
+
this.state.set("starting", false);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Scans the files in the project and updates the file manager state.
|
|
170
|
+
* @param {object} [options={}] - Options for the file manager
|
|
171
|
+
* @param {string | string[]} [options.exclude] - The patterns to exclude from the scan
|
|
172
|
+
* @returns {Promise<FileManager>} The file manager instance
|
|
173
|
+
*/
|
|
174
|
+
async scanFiles(options: { exclude?: string | string[] } = {}) {
|
|
175
|
+
const { cwd, git, fs } = this.container;
|
|
176
|
+
|
|
177
|
+
const fileIds: string[] = [];
|
|
178
|
+
|
|
179
|
+
if (!Array.isArray(options.exclude)) {
|
|
180
|
+
options.exclude = [options.exclude!].filter((v) => v?.length);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const { exclude = ["dist", "node_modules", "out"] } = options;
|
|
184
|
+
|
|
185
|
+
exclude.push(...castArray(this.options.exclude!).filter((v) => v?.length));
|
|
186
|
+
|
|
187
|
+
exclude.push("node_modules");
|
|
188
|
+
exclude.push("out");
|
|
189
|
+
exclude.push("dist");
|
|
190
|
+
|
|
191
|
+
if (git.isRepo) {
|
|
192
|
+
const repoRoot = git.repoRoot;
|
|
193
|
+
const cwdRelative = repoRoot ? relative(repoRoot, cwd) : '';
|
|
194
|
+
const baseDir = cwdRelative || '';
|
|
195
|
+
|
|
196
|
+
const deleted = await git.lsFiles({ deleted: true, baseDir })
|
|
197
|
+
await git.lsFiles({ baseDir }).then((results) => fileIds.push(...results.filter((id:string) => !deleted.includes(id))));
|
|
198
|
+
await git
|
|
199
|
+
.lsFiles({ others: true, includeIgnored: true, exclude, baseDir })
|
|
200
|
+
.then((results) => fileIds.push(...results.filter((id:string) => !deleted.includes(id))));
|
|
201
|
+
|
|
202
|
+
// git ls-files returns paths relative to repo root; make them relative to cwd
|
|
203
|
+
if (cwdRelative) {
|
|
204
|
+
const prefix = cwdRelative + '/';
|
|
205
|
+
for (let i = 0; i < fileIds.length; i++) {
|
|
206
|
+
if (fileIds[i].startsWith(prefix)) {
|
|
207
|
+
fileIds[i] = fileIds[i].slice(prefix.length);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
await fs.walkAsync(cwd).then(({ files } : { files: string[] }) => fileIds.push(...files));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
fileIds.forEach((relativePath) => {
|
|
216
|
+
const absolutePath = this.container.paths.resolve(relativePath);
|
|
217
|
+
const { name, ext, dir } = parse(absolutePath);
|
|
218
|
+
|
|
219
|
+
let size = 0
|
|
220
|
+
let modifiedAt = new Date(0)
|
|
221
|
+
let createdAt = new Date(0)
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const stats = statSync(absolutePath);
|
|
225
|
+
size = stats.size;
|
|
226
|
+
modifiedAt = stats.mtime;
|
|
227
|
+
createdAt = stats.birthtime;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.files.set(relativePath, {
|
|
232
|
+
dirname: dir,
|
|
233
|
+
absolutePath,
|
|
234
|
+
relativePath,
|
|
235
|
+
relativeDirname: this.container.paths.relative(dir),
|
|
236
|
+
name,
|
|
237
|
+
extension: ext,
|
|
238
|
+
size,
|
|
239
|
+
modifiedAt,
|
|
240
|
+
createdAt,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
watcher: FSWatcher | null = null;
|
|
248
|
+
|
|
249
|
+
/** Returns the directories and files currently being watched by chokidar. */
|
|
250
|
+
get watchedFiles(): Record<string, string[]> {
|
|
251
|
+
return this.watcher?.getWatched() || {};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Watches the files in the project and updates the file manager state.
|
|
256
|
+
* @param {object} [options={}] - Options for the file manager
|
|
257
|
+
* @param {string | string[]} [options.exclude] - The patterns to exclude from the watch
|
|
258
|
+
* @returns {Promise<void>} The file manager instance
|
|
259
|
+
*/
|
|
260
|
+
async watch(options: { exclude?: string | string[] } = {}) {
|
|
261
|
+
if (this.isWatching) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!Array.isArray(options.exclude)) {
|
|
266
|
+
options.exclude = [options.exclude!].filter((v) => v?.length);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const {
|
|
270
|
+
exclude = [".git/**", "dist/**", "node_modules/**", "out/**", "build/**"],
|
|
271
|
+
} = options;
|
|
272
|
+
|
|
273
|
+
exclude.push(...castArray(this.options.exclude!).filter((v) => v?.length));
|
|
274
|
+
|
|
275
|
+
const { cwd } = this.container;
|
|
276
|
+
|
|
277
|
+
const watcher = chokidar.watch(
|
|
278
|
+
this.directoryIds.map(id => this.container.paths.resolve(id))
|
|
279
|
+
, {
|
|
280
|
+
ignoreInitial: true,
|
|
281
|
+
persistent: true,
|
|
282
|
+
ignored: [
|
|
283
|
+
'.git/**',
|
|
284
|
+
...[".git", "dist/**", "node_modules/**", "out/**", "build/**"],
|
|
285
|
+
...exclude,
|
|
286
|
+
].map((pattern) => micromatch.makeRe(pattern)).concat([
|
|
287
|
+
/\.git/,
|
|
288
|
+
/node_modules/
|
|
289
|
+
]),
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
watcher
|
|
293
|
+
.on("add", (path) => {
|
|
294
|
+
this.emit("file:change", {
|
|
295
|
+
type: "add",
|
|
296
|
+
path,
|
|
297
|
+
});
|
|
298
|
+
this.updateFile(path);
|
|
299
|
+
})
|
|
300
|
+
.on("change", (path) => {
|
|
301
|
+
this.updateFile(path);
|
|
302
|
+
this.emit("file:change", {
|
|
303
|
+
path,
|
|
304
|
+
type: "change",
|
|
305
|
+
});
|
|
306
|
+
})
|
|
307
|
+
.on("unlink", (path) => {
|
|
308
|
+
this.removeFile(path);
|
|
309
|
+
this.emit("file:change", {
|
|
310
|
+
type: "delete",
|
|
311
|
+
path,
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
watcher.on("ready", () => {
|
|
316
|
+
this.state.set("watching", true);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
this.watcher = watcher;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async stopWatching() {
|
|
323
|
+
if (!this.isWatching) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (this.watcher) {
|
|
328
|
+
this.watcher.close();
|
|
329
|
+
this.state.set("watching", false);
|
|
330
|
+
this.watcher = null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async updateFile(path: string) {
|
|
335
|
+
// Reuse the logic from the scanFiles method to update a single file
|
|
336
|
+
const absolutePath = this.container.paths.resolve(path);
|
|
337
|
+
const { name, ext, dir } = parse(absolutePath);
|
|
338
|
+
const stats = statSync(absolutePath);
|
|
339
|
+
|
|
340
|
+
this.files.set(path, {
|
|
341
|
+
dirname: dir,
|
|
342
|
+
absolutePath,
|
|
343
|
+
relativePath: path,
|
|
344
|
+
name,
|
|
345
|
+
extension: ext,
|
|
346
|
+
size: stats.size,
|
|
347
|
+
modifiedAt: stats.mtime,
|
|
348
|
+
createdAt: stats.birthtime,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async removeFile(path: string) {
|
|
353
|
+
this.files.delete(path);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export default features.register("fileManager", FileManager);
|