diff --git a/.gitignore b/.gitignore index 9b88697..7b13823 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,7 @@ website/src/zh/api/dart_node_react/ website/src/zh/api/dart_node_react_native/ website/src/zh/api/dart_node_ws/ website/src/zh/api/dart_node_better_sqlite3/ +website/src/zh/api/dart_node_sql_js/ website/src/zh/api/dart_node_mcp/ website/src/zh/api/dart_logging/ website/src/zh/api/reflux/ diff --git a/packages/dart_logging/README.md b/packages/dart_logging/README.md index 3022ee8..3f4953a 100644 --- a/packages/dart_logging/README.md +++ b/packages/dart_logging/README.md @@ -5,7 +5,7 @@ Pino-style structured logging with child loggers. Provides hierarchical logging ```yaml dependencies: - dart_logging: ^0.11.0-beta + dart_logging: ^0.13.0-beta ``` ## Quick Start @@ -81,9 +81,10 @@ This is useful for adding context that applies to a scope (like a request handle Create custom transports to send logs to different destinations: ```dart -void myTransport(LogEntry entry) { +void myTransport(LogMessage message, LogLevel minimumLogLevel) { + if (message.logLevel.index < minimumLogLevel.index) return; // Send to external service, file, etc. - print('${entry.level}: ${entry.message}'); + print('${message.logLevel}: ${message.message}'); } final context = createLoggingContext( @@ -119,4 +120,4 @@ void main() { ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_logging). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_logging). diff --git a/packages/dart_logging/README_zh.md b/packages/dart_logging/README_zh.md index b692868..e94d66d 100644 --- a/packages/dart_logging/README_zh.md +++ b/packages/dart_logging/README_zh.md @@ -5,7 +5,7 @@ Pino 风格的结构化日志,支持子日志器。提供具有自动上下文 ```yaml dependencies: - dart_logging: ^0.11.0-beta + dart_logging: ^0.13.0-beta ``` ## 快速开始 @@ -81,9 +81,9 @@ userLogger.info('Action'); // 同时包含 requestId 和 userId 创建自定义传输以将日志发送到不同目的地: ```dart -void myTransport(LogEntry entry) { +void myTransport(LogMessage message, LogLevel minimumLogLevel) { // 发送到外部服务、文件等 - print('${entry.level}: ${entry.message}'); + print('${message.logLevel}: ${message.message}'); } final context = createLoggingContext( @@ -119,4 +119,4 @@ void main() { ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_logging) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_logging) 上获取。 diff --git a/packages/dart_node_better_sqlite3/README.md b/packages/dart_node_better_sqlite3/README.md index 1882ef8..d844676 100644 --- a/packages/dart_node_better_sqlite3/README.md +++ b/packages/dart_node_better_sqlite3/README.md @@ -5,8 +5,8 @@ Typed Dart bindings for [better-sqlite3](https://github.com/WiseLibs/better-sqli ```yaml dependencies: - dart_node_better_sqlite3: ^0.11.0-beta - nadz: ^0.9.0 + dart_node_better_sqlite3: ^0.13.0-beta + nadz: ^0.0.7-beta ``` Also install the npm package: @@ -41,7 +41,10 @@ void main() { Error(:final error) => throw Exception(error), }; - final rows = query.all([]); + final rows = switch (query.all()) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), + }; print(rows); db.close(); @@ -93,22 +96,32 @@ final query = switch (db.prepare('SELECT * FROM users WHERE id = ?')) { }; // Get single row -final row = query.get([1]); +final row = switch (query.get([1])) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), +}; // Get all rows -final allRows = query.all([]); +final allRows = switch (query.all()) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), +}; ``` ### Transactions ```dart db.exec('BEGIN'); -try { - // Multiple operations... - db.exec('COMMIT'); -} catch (e) { - db.exec('ROLLBACK'); - rethrow; + +// Multiple operations... +final result = db.exec('INSERT INTO users (name) VALUES (?)'); + +switch (result) { + case Success(): + db.exec('COMMIT'); + case Error(:final error): + db.exec('ROLLBACK'); + throw Exception(error); } ``` @@ -124,4 +137,4 @@ node app.js ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3). diff --git a/packages/dart_node_better_sqlite3/README_zh.md b/packages/dart_node_better_sqlite3/README_zh.md index e67916b..3bfb955 100644 --- a/packages/dart_node_better_sqlite3/README_zh.md +++ b/packages/dart_node_better_sqlite3/README_zh.md @@ -5,8 +5,8 @@ ```yaml dependencies: - dart_node_better_sqlite3: ^0.11.0-beta - nadz: ^0.9.0 + dart_node_better_sqlite3: ^0.13.0-beta + nadz: ^0.0.7-beta ``` 通过 npm 安装: @@ -124,4 +124,4 @@ node app.js ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_better_sqlite3) 上获取。 diff --git a/packages/dart_node_core/README.md b/packages/dart_node_core/README.md index a7b4a82..fc64bf1 100644 --- a/packages/dart_node_core/README.md +++ b/packages/dart_node_core/README.md @@ -5,7 +5,7 @@ ```yaml dependencies: - dart_node_core: ^0.11.0-beta + dart_node_core: ^0.13.0-beta ``` ## Core Utilities @@ -90,4 +90,4 @@ void main() { ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_core). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_core). diff --git a/packages/dart_node_core/README_zh.md b/packages/dart_node_core/README_zh.md index 80e2821..fb25f44 100644 --- a/packages/dart_node_core/README_zh.md +++ b/packages/dart_node_core/README_zh.md @@ -5,7 +5,7 @@ ```yaml dependencies: - dart_node_core: ^0.11.0-beta + dart_node_core: ^0.13.0-beta ``` ## 核心工具 @@ -90,4 +90,4 @@ void main() { ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_core) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_core) 上获取。 diff --git a/packages/dart_node_express/README.md b/packages/dart_node_express/README.md index d075b82..cd0d00d 100644 --- a/packages/dart_node_express/README.md +++ b/packages/dart_node_express/README.md @@ -6,7 +6,7 @@ Type-safe Express.js bindings for Dart. Build HTTP servers and REST APIs entirel ```yaml dependencies: - dart_node_express: ^0.11.0-beta + dart_node_express: ^0.13.0-beta ``` Also install Express via npm: @@ -50,7 +50,7 @@ app.post('/users', handler((req, res) { })); app.put('/users/:id', handler((req, res) { - final id = req.params['id']; + final id = req.params['id'].toString(); res.jsonMap({'updated': id}); })); @@ -64,8 +64,8 @@ app.delete('/users/:id', handler((req, res) { ```dart app.get('/users/:userId/posts/:postId', handler((req, res) { - final userId = req.params['userId']; - final postId = req.params['postId']; + final userId = req.params['userId'].toString(); + final postId = req.params['postId'].toString(); res.jsonMap({ 'userId': userId, @@ -78,8 +78,8 @@ app.get('/users/:userId/posts/:postId', handler((req, res) { ```dart app.get('/search', handler((req, res) { - final query = req.query['q']; - final page = int.tryParse(req.query['page'] ?? '1') ?? 1; + final query = req.query['q']?.toString(); + final page = int.tryParse(req.query['page']?.toString() ?? '1') ?? 1; res.jsonMap({ 'query': query, @@ -98,7 +98,7 @@ app.post('/api/data', handler((req, res) { final body = req.body; // Headers - final contentType = req.headers['content-type']; + final contentType = req.headers['content-type']?.toString(); // URL path final path = req.path; @@ -188,6 +188,10 @@ app.get('/profile', handler((req, res) { Organize routes with the Router: ```dart +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'package:dart_node_express/dart_node_express.dart'; + Router createUserRouter() { final router = Router(); @@ -201,18 +205,28 @@ Router createUserRouter() { })); router.get('/:id', handler((req, res) { - res.jsonMap({'user': req.params['id']}); + res.jsonMap({'user': req.params['id'].toString()}); })); return router; } +// Mount a Router at a path. Express Routers are callable middleware, +// and `use` accepts a path (as JSAny?) plus a middleware JSFunction. +void mountRouter(ExpressApp app, String path, Router router) { + final routerFn = switch (router as JSAny) { + final JSFunction f => f, + _ => throw StateError('Router is not callable'), + }; + app.use(path.toJS, routerFn); +} + void main() { final app = express(); // Mount the router final router = createUserRouter(); - app.use('/api/users', router); + mountRouter(app, '/api/users', router); app.listen(3000); } @@ -312,7 +326,7 @@ void main() { })); // Mount routers - app.use('/api/users', createUserRouter()); + mountRouter(app, '/api/users', createUserRouter()); // Start server app.listen(3000, () { @@ -323,4 +337,4 @@ void main() { ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_express). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_express). diff --git a/packages/dart_node_express/README_zh.md b/packages/dart_node_express/README_zh.md index 5f89bf0..2d7ce5c 100644 --- a/packages/dart_node_express/README_zh.md +++ b/packages/dart_node_express/README_zh.md @@ -6,7 +6,7 @@ ```yaml dependencies: - dart_node_express: ^0.11.0-beta + dart_node_express: ^0.13.0-beta ``` 通过 npm 安装 Express: @@ -50,7 +50,7 @@ app.post('/users', handler((req, res) { })); app.put('/users/:id', handler((req, res) { - final id = req.params['id']; + final id = req.params['id'].toString(); res.jsonMap({'updated': id}); })); @@ -64,8 +64,8 @@ app.delete('/users/:id', handler((req, res) { ```dart app.get('/users/:userId/posts/:postId', handler((req, res) { - final userId = req.params['userId']; - final postId = req.params['postId']; + final userId = req.params['userId'].toString(); + final postId = req.params['postId'].toString(); res.jsonMap({ 'userId': userId, @@ -78,8 +78,8 @@ app.get('/users/:userId/posts/:postId', handler((req, res) { ```dart app.get('/search', handler((req, res) { - final query = req.query['q']; - final page = int.tryParse(req.query['page'] ?? '1') ?? 1; + final query = req.query['q'].toString(); + final page = int.tryParse(req.query['page']?.toString() ?? '1') ?? 1; res.jsonMap({ 'query': query, @@ -98,7 +98,7 @@ app.post('/api/data', handler((req, res) { final body = req.body; // 请求头 - final contentType = req.headers['content-type']; + final contentType = req.headers['content-type'].toString(); // URL 路径 final path = req.path; @@ -201,7 +201,7 @@ Router createUserRouter() { })); router.get('/:id', handler((req, res) { - res.jsonMap({'user': req.params['id']}); + res.jsonMap({'user': req.params['id'].toString()}); })); return router; @@ -212,7 +212,9 @@ void main() { // 挂载路由器 final router = createUserRouter(); - app.use('/api/users', router); + if (router case final JSFunction fn) { + app.use('/api/users'.toJS, fn); + } app.listen(3000); } @@ -312,7 +314,9 @@ void main() { })); // 挂载路由器 - app.use('/api/users', createUserRouter()); + if (createUserRouter() case final JSFunction fn) { + app.use('/api/users'.toJS, fn); + } // 启动服务器 app.listen(3000, () { @@ -323,4 +327,4 @@ void main() { ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_express) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_express) 上获取。 diff --git a/packages/dart_node_mcp/README.md b/packages/dart_node_mcp/README.md index 5b68630..8ee11e4 100644 --- a/packages/dart_node_mcp/README.md +++ b/packages/dart_node_mcp/README.md @@ -5,8 +5,8 @@ MCP (Model Context Protocol) server bindings for Dart on Node.js. Build AI tool ```yaml dependencies: - dart_node_mcp: ^0.11.0-beta - nadz: ^0.9.0 + dart_node_mcp: ^0.13.0-beta + nadz: ^0.0.7-beta ``` Also install the npm package: @@ -31,9 +31,17 @@ Future main() async { server.registerTool( 'echo', - (description: 'Echo input back', inputSchema: null), + ( + title: null, + description: 'Echo input back', + inputSchema: null, + outputSchema: null, + annotations: null, + ), (args, meta) async => ( - content: [(type: 'text', text: args['message'] as String)], + content: >[ + {'type': 'text', 'text': args['message'] as String}, + ], isError: false, ), ); @@ -65,6 +73,7 @@ Tools are functions that AI assistants can call. Register them with a name, desc server.registerTool( 'greet', ( + title: null, description: 'Greet a user by name', inputSchema: { 'type': 'object', @@ -73,11 +82,15 @@ server.registerTool( }, 'required': ['name'], }, + outputSchema: null, + annotations: null, ), (args, meta) async { final name = args['name'] as String; return ( - content: [(type: 'text', text: 'Hello, $name!')], + content: >[ + {'type': 'text', 'text': 'Hello, $name!'}, + ], isError: false, ); }, @@ -115,10 +128,10 @@ Add your MCP server to Claude Code: claude mcp add --transport stdio my-server -- node /path/to/server.js ``` -## Example: Too Many Cooks +## Built with dart_node_mcp: Too Many Cooks -The [Too Many Cooks](https://github.com/melbournedeveloper/too_many_cooks) MCP server is built with dart_node_mcp. It provides multi-agent coordination for AI assistants editing the same codebase. +[Too Many Cooks](https://tmc-mcp.dev) is an MCP server originally built with dart_node_mcp that provides multi-agent coordination for AI assistants editing the same codebase. It has since moved to its own home at [tmc-mcp.dev](https://tmc-mcp.dev) and is no longer part of this repository. ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_mcp). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_mcp). diff --git a/packages/dart_node_mcp/README_zh.md b/packages/dart_node_mcp/README_zh.md index f3f7019..ad2c5d7 100644 --- a/packages/dart_node_mcp/README_zh.md +++ b/packages/dart_node_mcp/README_zh.md @@ -5,8 +5,8 @@ ```yaml dependencies: - dart_node_mcp: ^0.11.0-beta - nadz: ^0.9.0 + dart_node_mcp: ^0.13.0-beta + nadz: ^0.0.7-beta ``` 通过 npm 安装: @@ -31,9 +31,17 @@ Future main() async { server.registerTool( 'echo', - (description: 'Echo input back', inputSchema: null), + ( + title: null, + description: 'Echo input back', + inputSchema: null, + outputSchema: null, + annotations: null, + ), (args, meta) async => ( - content: [(type: 'text', text: args['message'] as String)], + content: >[ + {'type': 'text', 'text': args['message'] as String}, + ], isError: false, ), ); @@ -65,6 +73,7 @@ final serverResult = McpServer.create((name: 'my-server', version: '1.0.0')); server.registerTool( 'greet', ( + title: null, description: 'Greet a user by name', inputSchema: { 'type': 'object', @@ -73,11 +82,15 @@ server.registerTool( }, 'required': ['name'], }, + outputSchema: null, + annotations: null, ), (args, meta) async { final name = args['name'] as String; return ( - content: [(type: 'text', text: 'Hello, $name!')], + content: >[ + {'type': 'text', 'text': 'Hello, $name!'}, + ], isError: false, ); }, @@ -115,10 +128,10 @@ node server.js claude mcp add --transport stdio my-server -- node /path/to/server.js ``` -## 示例:Too Many Cooks +## 使用 dart_node_mcp 构建:Too Many Cooks -[Too Many Cooks](https://github.com/melbournedeveloper/too_many_cooks) MCP 服务器是使用 dart_node_mcp 构建的。它为编辑同一代码库的 AI 助手提供多智能体协调功能。 +[Too Many Cooks](https://tmc-mcp.dev) 是一个最初使用 dart_node_mcp 构建的 MCP 服务器,为编辑同一代码库的 AI 助手提供多智能体协调功能。它现已迁移到独立站点 [tmc-mcp.dev](https://tmc-mcp.dev),不再是本仓库的一部分。 ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_mcp) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_mcp) 上获取。 diff --git a/packages/dart_node_react/README.md b/packages/dart_node_react/README.md index 4c3ca6e..063aab9 100644 --- a/packages/dart_node_react/README.md +++ b/packages/dart_node_react/README.md @@ -6,7 +6,7 @@ Type-safe React bindings for building web applications in Dart. If you know Reac ```yaml dependencies: - dart_node_react: ^0.11.0-beta + dart_node_react: ^0.13.0-beta ``` Also install React via npm: @@ -24,16 +24,17 @@ ReactElement app() { return div( className: 'app', children: [ - h1(children: [text('Hello, Dart!')]), - p(children: [text('Welcome to React with Dart.')]), + h1('Hello, Dart!'), + pEl('Welcome to React with Dart.'), ], ); } void main() { - final container = document.getElementById('root'); - final root = ReactDOM.createRoot(container); - root.render(app()); + final container = Document.getElementById('root'); + (container != null) + ? ReactDOM.createRoot(container).render(app()) + : throw StateError('Root element not found'); } ``` @@ -45,9 +46,7 @@ void main() { ReactElement greeting({required String name}) { return div( className: 'greeting', - children: [ - text('Hello, $name!'), - ], + child: pEl('Hello, $name!'), ); } @@ -69,8 +68,8 @@ ReactElement userCard({ avatarUrl != null ? img(src: avatarUrl, alt: name) : div(className: 'avatar-placeholder'), - h2(children: [text(name)]), - p(children: [text(email)]), + h2(name), + pEl(email), ], ); } @@ -87,14 +86,14 @@ ReactElement counter() { final count = useState(0); return div(children: [ - p(children: [text('Count: ${count.value}')]), + pEl('Count: ${count.value}'), button( - onClick: (_) => count.setWithUpdater((c) => c + 1), - children: [text('Increment')], + text: 'Increment', + onClick: () => count.setWithUpdater((c) => c + 1), ), button( - onClick: (_) => count.setWithUpdater((c) => c - 1), - children: [text('Decrement')], + text: 'Decrement', + onClick: () => count.setWithUpdater((c) => c - 1), ), ]); } @@ -123,7 +122,7 @@ ReactElement timer() { return () => timer.cancel(); }, []); // Empty deps = run once on mount - return p(children: [text('Seconds: ${seconds.value}')]); + return pEl('Seconds: ${seconds.value}'); } ``` @@ -141,18 +140,23 @@ useLayoutEffect(() { ### useRef ```dart +// A focusable DOM node exposed through a ref. +extension type FocusableElement._(JSObject _) implements JSObject { + external void focus(); +} + ReactElement focusInput() { - final inputRef = useRef(null); + final inputRef = useRef(); void handleClick() { inputRef.current?.focus(); } return div(children: [ - input(ref: inputRef, type: 'text'), + input(type: 'text', props: {'ref': inputRef.jsRef}), button( - onClick: (_) => handleClick(), - children: [text('Focus Input')], + text: 'Focus Input', + onClick: handleClick, ), ]); } @@ -171,7 +175,7 @@ ReactElement expensiveList({required List numbers}) { ); return div(children: [ - p(children: [text('Fibonacci of ${count.value} is $fib')]), + pEl('Fibonacci of ${count.value} is $fib'), ]); } ``` @@ -182,23 +186,31 @@ ReactElement expensiveList({required List numbers}) { ReactElement searchBox({required void Function(String) onSearch}) { final query = useState(''); - // Memoize the callback - final handleSubmit = useCallback( - () => onSearch(query.value), - [query.value, onSearch], - ); + void handleSubmit() => onSearch(query.value); + + // Memoize the callback to pass a stable reference to child components. + final memoizedSubmit = useCallback(handleSubmit, [query.value, onSearch]); return form( - onSubmit: (_) => handleSubmit(), - children: [ + {'onSubmit': memoizedSubmit}, + [ input( value: query.value, - onChange: (e) => query.set(e.target.value), + onChange: (e) => query.set(inputValue(e)), ), - button(type: 'submit', children: [text('Search')]), + button(text: 'Search', onClick: handleSubmit), ], ); } + +// Reads the current value from an input change event. +String inputValue(SyntheticEvent event) => switch (event.target) { + final JSObject target => switch (target['value']) { + final JSString value => value.toDart, + _ => '', + }, + _ => '', +}; ``` ### useDebugValue @@ -219,26 +231,25 @@ useDebugValue( ```dart // Divs and spans div(className: 'container', children: [...]) -span(className: 'highlight', children: [...]) +span('Highlighted text', className: 'highlight') // Headings -h1(children: [text('Title')]) -h2(children: [text('Subtitle')]) +h1('Title') +h2('Subtitle') // Paragraphs and text -p(children: [text('Some text')]) -text('Raw text content') +pEl('Some text') // Links -a(href: 'https://example.com', children: [text('Click me')]) +a(href: 'https://example.com', text: 'Click me') // Images img(src: '/image.png', alt: 'Description') // Forms -form(onSubmit: handleSubmit, children: [...]) +form({'onSubmit': handleSubmit}, [...]) input(type: 'text', value: value, onChange: handleChange) -button(type: 'submit', children: [text('Submit')]) +button(text: 'Submit', onClick: handleClick) ``` ### Lists @@ -249,14 +260,9 @@ ReactElement todoList({required List todos}) { className: 'todo-list', children: todos.map((todo) => li( - key: todo.id, - children: [ - input( - type: 'checkbox', - checked: todo.completed, - ), - text(todo.title), - ], + todo.title, + props: {'key': todo.id}, + className: todo.completed ? 'completed' : '', ) ).toList(), ); @@ -269,8 +275,8 @@ ReactElement todoList({required List todos}) { ReactElement userStatus({required User? user}) { return div(children: [ user != null - ? span(children: [text('Welcome, ${user.name}!')]) - : span(children: [text('Please log in')]), + ? span('Welcome, ${user.name}!') + : span('Please log in'), ]); } ``` @@ -279,18 +285,13 @@ ReactElement userStatus({required User? user}) { ```dart ReactElement interactiveButton() { - void handleClick(MouseEvent e) { - print('Button clicked at (${e.clientX}, ${e.clientY})'); - } - - void handleMouseEnter(MouseEvent e) { - print('Mouse entered'); + void handleClick() { + print('Button clicked'); } return button( + text: 'Click Me', onClick: handleClick, - onMouseEnter: handleMouseEnter, - children: [text('Hover and Click Me')], ); } ``` @@ -302,27 +303,26 @@ ReactElement loginForm() { final email = useState(''); final password = useState(''); - void handleSubmit(Event e) { - e.preventDefault(); + void handleSubmit() { print('Login: ${email.value} / ${password.value}'); } return form( - onSubmit: handleSubmit, - children: [ + {'onSubmit': (JSObject e) => SyntheticEvent.fromJs(e).preventDefault()}, + [ input( type: 'email', value: email.value, - onChange: (e) => email.set(e.target.value), + onChange: (e) => email.set(inputValue(e)), placeholder: 'Email', ), input( type: 'password', value: password.value, - onChange: (e) => password.set(e.target.value), + onChange: (e) => password.set(inputValue(e)), placeholder: 'Password', ), - button(type: 'submit', children: [text('Log In')]), + button(text: 'Log In', onClick: handleSubmit), ], ); } @@ -355,20 +355,32 @@ div( ## Complete Example ```dart +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; + import 'package:dart_node_react/dart_node_react.dart'; +// Reads the current value from an input change event. +String inputValue(SyntheticEvent event) => switch (event.target) { + final JSObject target => switch (target['value']) { + final JSString value => value.toDart, + _ => '', + }, + _ => '', +}; + ReactElement todoApp() { final todos = useState>([]); - final input = useState(''); + final newTodo = useState(''); void addTodo() { - if (input.value.trim().isEmpty) return; + if (newTodo.value.trim().isEmpty) return; todos.setWithUpdater((prev) => [ ...prev, - Todo(id: DateTime.now().toString(), title: input.value, completed: false), + Todo(id: DateTime.now().toString(), title: newTodo.value, completed: false), ]); - input.set(''); + newTodo.set(''); } void toggleTodo(String id) { @@ -382,37 +394,39 @@ ReactElement todoApp() { return div( className: 'todo-app', children: [ - h1(children: [text('Todo List')]), + h1('Todo List'), form( - onSubmit: (e) { - e.preventDefault(); - addTodo(); + { + 'onSubmit': (JSObject e) { + SyntheticEvent.fromJs(e).preventDefault(); + addTodo(); + }, }, - children: [ + [ input( - value: input.value, - onChange: (e) => input.set(e.target.value), + value: newTodo.value, + onChange: (e) => newTodo.set(inputValue(e)), placeholder: 'What needs to be done?', ), - button(type: 'submit', children: [text('Add')]), + button(text: 'Add', onClick: addTodo), ], ), ul( children: todos.value.map((todo) => li( - key: todo.id, + todo.title, + props: { + 'key': todo.id, + 'onClick': () => toggleTodo(todo.id), + }, className: todo.completed ? 'completed' : '', - onClick: (_) => toggleTodo(todo.id), - children: [text(todo.title)], ) ).toList(), ), - p(children: [ - text('${todos.value.where((t) => !t.completed).length} items left'), - ]), + pEl('${todos.value.where((t) => !t.completed).length} items left'), ], ); } @@ -426,11 +440,13 @@ class Todo { } void main() { - final root = ReactDOM.createRoot(document.getElementById('root')); - root.render(todoApp()); + final root = Document.getElementById('root'); + (root != null) + ? ReactDOM.createRoot(root).render(todoApp()) + : throw StateError('Root element not found'); } ``` ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_react). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_react). diff --git a/packages/dart_node_react/README_zh.md b/packages/dart_node_react/README_zh.md index b5ec51a..fc8df43 100644 --- a/packages/dart_node_react/README_zh.md +++ b/packages/dart_node_react/README_zh.md @@ -6,7 +6,7 @@ ```yaml dependencies: - dart_node_react: ^0.11.0-beta + dart_node_react: ^0.13.0-beta ``` 通过 npm 安装 React: @@ -18,22 +18,25 @@ npm install react react-dom ## 快速开始 ```dart +import 'dart:js_interop'; + import 'package:dart_node_react/dart_node_react.dart'; ReactElement app() { return div( className: 'app', children: [ - h1(children: [text('Hello, Dart!')]), - p(children: [text('Welcome to React with Dart.')]), + h1('Hello, Dart!'), + pEl('Welcome to React with Dart.'), ], ); } void main() { - final container = document.getElementById('root'); - final root = ReactDOM.createRoot(container); - root.render(app()); + final container = Document.getElementById('root'); + if (container case final JSObject c) { + createRoot(c).render(app()); + } } ``` @@ -46,7 +49,7 @@ ReactElement greeting({required String name}) { return div( className: 'greeting', children: [ - text('Hello, $name!'), + pEl('Hello, $name!'), ], ); } @@ -69,8 +72,8 @@ ReactElement userCard({ avatarUrl != null ? img(src: avatarUrl, alt: name) : div(className: 'avatar-placeholder'), - h2(children: [text(name)]), - p(children: [text(email)]), + h2(name), + pEl(email), ], ); } @@ -87,14 +90,14 @@ ReactElement counter() { final count = useState(0); return div(children: [ - p(children: [text('Count: ${count.value}')]), + pEl('Count: ${count.value}'), button( - onClick: (_) => count.setWithUpdater((c) => c + 1), - children: [text('Increment')], + text: 'Increment', + onClick: () => count.setWithUpdater((c) => c + 1), ), button( - onClick: (_) => count.setWithUpdater((c) => c - 1), - children: [text('Decrement')], + text: 'Decrement', + onClick: () => count.setWithUpdater((c) => c - 1), ), ]); } @@ -123,7 +126,7 @@ ReactElement timer() { return () => timer.cancel(); }, []); // 空依赖数组 = 仅在挂载时运行一次 - return p(children: [text('Seconds: ${seconds.value}')]); + return pEl('Seconds: ${seconds.value}'); } ``` @@ -142,17 +145,19 @@ useLayoutEffect(() { ```dart ReactElement focusInput() { - final inputRef = useRef(null); + final inputRef = useRef(null); void handleClick() { - inputRef.current?.focus(); + if (inputRef.jsRef.current case final JSObject node) { + node.callMethod('focus'.toJS); + } } return div(children: [ - input(ref: inputRef, type: 'text'), + input(type: 'text', props: {'ref': inputRef.jsRef}), button( - onClick: (_) => handleClick(), - children: [text('Focus Input')], + text: 'Focus Input', + onClick: handleClick, ), ]); } @@ -171,7 +176,7 @@ ReactElement expensiveList({required List numbers}) { ); return div(children: [ - p(children: [text('Fibonacci of ${count.value} is $fib')]), + pEl('Fibonacci of ${count.value} is $fib'), ]); } ``` @@ -188,16 +193,17 @@ ReactElement searchBox({required void Function(String) onSearch}) { [query.value, onSearch], ); - return form( - onSubmit: (_) => handleSubmit(), - children: [ - input( - value: query.value, - onChange: (e) => query.set(e.target.value), - ), - button(type: 'submit', children: [text('Search')]), - ], - ); + return form(null, [ + input( + value: query.value, + onChange: (e) { + if (e.target case final JSObject t) { + if (t['value'] case final JSString s) query.set(s.toDart); + } + }, + ), + button(text: 'Search', props: {'type': 'submit', 'onClick': handleSubmit}), + ]); } ``` @@ -219,26 +225,25 @@ useDebugValue( ```dart // Div 和 span div(className: 'container', children: [...]) -span(className: 'highlight', children: [...]) +span('highlight text', className: 'highlight') // 标题 -h1(children: [text('Title')]) -h2(children: [text('Subtitle')]) +h1('Title') +h2('Subtitle') // 段落和文本 -p(children: [text('Some text')]) -text('Raw text content') +pEl('Some text') // 链接 -a(href: 'https://example.com', children: [text('Click me')]) +a(href: 'https://example.com', text: 'Click me') // 图片 img(src: '/image.png', alt: 'Description') // 表单 -form(onSubmit: handleSubmit, children: [...]) +form(null, [...]) input(type: 'text', value: value, onChange: handleChange) -button(type: 'submit', children: [text('Submit')]) +button(text: 'Submit', props: {'type': 'submit'}) ``` ### 列表 @@ -247,18 +252,14 @@ button(type: 'submit', children: [text('Submit')]) ReactElement todoList({required List todos}) { return ul( className: 'todo-list', - children: todos.map((todo) => - li( - key: todo.id, - children: [ - input( - type: 'checkbox', - checked: todo.completed, + children: todos + .map( + (todo) => li( + todo.title, + props: {'key': todo.id}, ), - text(todo.title), - ], - ) - ).toList(), + ) + .toList(), ); } ``` @@ -269,8 +270,8 @@ ReactElement todoList({required List todos}) { ReactElement userStatus({required User? user}) { return div(children: [ user != null - ? span(children: [text('Welcome, ${user.name}!')]) - : span(children: [text('Please log in')]), + ? span('Welcome, ${user.name}!') + : span('Please log in'), ]); } ``` @@ -279,18 +280,13 @@ ReactElement userStatus({required User? user}) { ```dart ReactElement interactiveButton() { - void handleClick(MouseEvent e) { - print('Button clicked at (${e.clientX}, ${e.clientY})'); - } - - void handleMouseEnter(MouseEvent e) { - print('Mouse entered'); + void handleClick() { + print('Button clicked'); } return button( + text: 'Hover and Click Me', onClick: handleClick, - onMouseEnter: handleMouseEnter, - children: [text('Hover and Click Me')], ); } ``` @@ -302,29 +298,34 @@ ReactElement loginForm() { final email = useState(''); final password = useState(''); - void handleSubmit(Event e) { + void handleSubmit(SyntheticEvent e) { e.preventDefault(); print('Login: ${email.value} / ${password.value}'); } - return form( - onSubmit: handleSubmit, - children: [ - input( - type: 'email', - value: email.value, - onChange: (e) => email.set(e.target.value), - placeholder: 'Email', - ), - input( - type: 'password', - value: password.value, - onChange: (e) => password.set(e.target.value), - placeholder: 'Password', - ), - button(type: 'submit', children: [text('Log In')]), - ], - ); + String readValue(SyntheticEvent e) => switch (e.target) { + final JSObject t => switch (t['value']) { + final JSString s => s.toDart, + _ => '', + }, + _ => '', + }; + + return form({'onSubmit': handleSubmit}, [ + input( + type: 'email', + value: email.value, + onChange: (e) => email.set(readValue(e)), + placeholder: 'Email', + ), + input( + type: 'password', + value: password.value, + onChange: (e) => password.set(readValue(e)), + placeholder: 'Password', + ), + button(text: 'Log In', props: {'type': 'submit'}), + ]); } ``` @@ -355,20 +356,23 @@ div( ## 完整示例 ```dart +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; + import 'package:dart_node_react/dart_node_react.dart'; ReactElement todoApp() { final todos = useState>([]); - final input = useState(''); + final text = useState(''); void addTodo() { - if (input.value.trim().isEmpty) return; + if (text.value.trim().isEmpty) return; todos.setWithUpdater((prev) => [ ...prev, - Todo(id: DateTime.now().toString(), title: input.value, completed: false), + Todo(id: DateTime.now().toString(), title: text.value, completed: false), ]); - input.set(''); + text.set(''); } void toggleTodo(String id) { @@ -382,37 +386,42 @@ ReactElement todoApp() { return div( className: 'todo-app', children: [ - h1(children: [text('Todo List')]), + h1('Todo List'), - form( - onSubmit: (e) { + form({ + 'onSubmit': (SyntheticEvent e) { e.preventDefault(); addTodo(); }, - children: [ - input( - value: input.value, - onChange: (e) => input.set(e.target.value), - placeholder: 'What needs to be done?', - ), - button(type: 'submit', children: [text('Add')]), - ], - ), + }, [ + input( + value: text.value, + onChange: (e) { + if (e.target case final JSObject t) { + if (t['value'] case final JSString s) text.set(s.toDart); + } + }, + placeholder: 'What needs to be done?', + ), + button(text: 'Add', props: {'type': 'submit'}), + ]), ul( - children: todos.value.map((todo) => - li( - key: todo.id, - className: todo.completed ? 'completed' : '', - onClick: (_) => toggleTodo(todo.id), - children: [text(todo.title)], - ) - ).toList(), + children: todos.value + .map( + (todo) => li( + todo.title, + className: todo.completed ? 'completed' : '', + props: { + 'key': todo.id, + 'onClick': () => toggleTodo(todo.id), + }, + ), + ) + .toList(), ), - p(children: [ - text('${todos.value.where((t) => !t.completed).length} items left'), - ]), + pEl('${todos.value.where((t) => !t.completed).length} items left'), ], ); } @@ -426,11 +435,13 @@ class Todo { } void main() { - final root = ReactDOM.createRoot(document.getElementById('root')); - root.render(todoApp()); + final container = Document.getElementById('root'); + if (container case final JSObject c) { + createRoot(c).render(todoApp()); + } } ``` ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_react) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_react) 上获取。 diff --git a/packages/dart_node_react_native/README.md b/packages/dart_node_react_native/README.md index ab0ea05..851f4f4 100644 --- a/packages/dart_node_react_native/README.md +++ b/packages/dart_node_react_native/README.md @@ -5,8 +5,8 @@ ```yaml dependencies: - dart_node_react_native: ^0.11.0-beta - dart_node_react: ^0.11.0-beta # Required peer dependency + dart_node_react_native: ^0.13.0-beta + dart_node_react: ^0.13.0-beta # Required peer dependency ``` Set up your Expo project: @@ -152,14 +152,30 @@ scrollView( For efficient list rendering: ```dart -ReactElement userList({required List users}) { - return flatList( +ReactElement userList({required JSArray users}) { + ReactElement renderItem(JSObject info) { + final item = info['item']; + return switch (item) { + final JSObject user => userCard(user: user), + _ => view(), + }; + } + + JSAny? keyExtractor(JSObject item, JSNumber index) => + switch (item['id']) { + final JSString id => id, + _ => index, + }; + + return flatList( data: users, - keyExtractor: (user, _) => user.id, - renderItem: (info) => userCard(user: info.item), - ItemSeparatorComponent: () => view( - style: {'height': 1, 'backgroundColor': '#eee'}, - ), + keyExtractor: keyExtractor.toJS, + renderItem: renderItem.toJS, + props: { + 'ItemSeparatorComponent': () => view( + style: {'height': 1, 'backgroundColor': '#eee'}, + ), + }, ); } ``` @@ -169,15 +185,15 @@ ReactElement userList({required List users}) { For displaying images: ```dart -// Local image -image( - source: AssetSource('assets/logo.png'), +// Local image (provide the bundled asset via a require() call) +rnImage( + source: {'uri': 'asset:/logo.png'}, style: {'width': 100, 'height': 100}, ) // Remote image -image( - source: UriSource('https://example.com/image.jpg'), +rnImage( + source: {'uri': 'https://example.com/image.jpg'}, style: {'width': 200, 'height': 150}, resizeMode: 'cover', ) @@ -250,25 +266,26 @@ view( Use with React Navigation (via JS interop): ```dart -// Define screens -ReactElement homeScreen({required NavigationProps nav}) { +// Define screens. React Navigation passes a props object containing +// `navigation` and `route` — use extractScreenProps to read them safely. +ReactElement homeScreen(ScreenProps screen) { return view(children: [ text('Home Screen'), touchableOpacity( - onPress: () => nav.navigate('Details', {'id': 123}), - children: [text('Go to Details')])], + onPress: () => screen.navigation.navigate('Details', {'id': 123}), + children: [text('Go to Details')], ), ]); } -ReactElement detailsScreen({required NavigationProps nav}) { - final id = nav.route.params['id']; +ReactElement detailsScreen(ScreenProps screen) { + final id = screen.route.getParam('id'); return view(children: [ text('Details for $id'), touchableOpacity( - onPress: () => nav.goBack(), - children: [text('Go Back')])], + onPress: () => screen.navigation.goBack(), + children: [text('Go Back')], ), ]); } diff --git a/packages/dart_node_react_native/README_zh.md b/packages/dart_node_react_native/README_zh.md index c7caa84..a3730a4 100644 --- a/packages/dart_node_react_native/README_zh.md +++ b/packages/dart_node_react_native/README_zh.md @@ -5,8 +5,8 @@ ```yaml dependencies: - dart_node_react_native: ^0.11.0-beta - dart_node_react: ^0.11.0-beta # 必需的对等依赖 + dart_node_react_native: ^0.13.0-beta + dart_node_react: ^0.13.0-beta # 必需的对等依赖 ``` 设置您的 Expo 项目: @@ -152,14 +152,28 @@ scrollView( 用于高效的列表渲染: ```dart -ReactElement userList({required List users}) { - return flatList( - data: users, - keyExtractor: (user, _) => user.id, - renderItem: (info) => userCard(user: info.item), - ItemSeparatorComponent: () => view( - style: {'height': 1, 'backgroundColor': '#eee'}, - ), +ReactElement userList({required List users}) { + JSString keyFor(JSObject user, JSNumber index) => + switch (user['id']) { + final JSString id => id, + _ => ''.toJS, + }; + + ReactElement renderUser(JSObject info) => + switch (info['item']) { + final JSObject user => userCard(user: user), + _ => view(), + }; + + return flatList( + data: users.toJS, + keyExtractor: keyFor.toJS, + renderItem: renderUser.toJS, + props: { + 'ItemSeparatorComponent': (() => view( + style: {'height': 1, 'backgroundColor': '#eee'}, + )).toJS, + }, ); } ``` @@ -169,15 +183,15 @@ ReactElement userList({required List users}) { 用于显示图片: ```dart -// 本地图片 -image( - source: AssetSource('assets/logo.png'), +// 本地图片(source 是一个普通的 Map) +rnImage( + source: {'uri': 'asset:/logo.png'}, style: {'width': 100, 'height': 100}, ) // 远程图片 -image( - source: UriSource('https://example.com/image.jpg'), +rnImage( + source: {'uri': 'https://example.com/image.jpg'}, style: {'width': 200, 'height': 150}, resizeMode: 'cover', ) @@ -250,25 +264,29 @@ view( 与 React Navigation 一起使用(通过 JS 互操作): ```dart -// 定义屏幕 -ReactElement homeScreen({required NavigationProps nav}) { +// 定义屏幕。屏幕组件以 JSObject 形式接收 props, +// 使用 extractScreenProps 即可得到带类型的 navigation 和 route。 +ReactElement homeScreen(JSObject props) { + final screen = extractScreenProps(props); + return view(children: [ text('Home Screen'), touchableOpacity( - onPress: () => nav.navigate('Details', {'id': 123}), - children: [text('Go to Details')])], + onPress: () => screen?.navigation.navigate('Details', {'id': 123}), + children: [text('Go to Details')], ), ]); } -ReactElement detailsScreen({required NavigationProps nav}) { - final id = nav.route.params['id']; +ReactElement detailsScreen(JSObject props) { + final screen = extractScreenProps(props); + final id = screen?.route.getParam('id'); return view(children: [ text('Details for $id'), touchableOpacity( - onPress: () => nav.goBack(), - children: [text('Go Back')])], + onPress: () => screen?.navigation.goBack(), + children: [text('Go Back')], ), ]); } diff --git a/packages/dart_node_sql_js/README_zh.md b/packages/dart_node_sql_js/README_zh.md new file mode 100644 index 0000000..c3ed7a4 --- /dev/null +++ b/packages/dart_node_sql_js/README_zh.md @@ -0,0 +1,101 @@ +[sql.js](https://github.com/sql-js/sql.js) 的类型化 Dart 绑定 —— 编译为 +WebAssembly 的 SQLite。为 Node.js 应用提供同步的纯内存 SQLite,并可显式持久化到磁盘。 + +与原生绑定不同,sql.js 无需编译,在任何支持 WebAssembly 的环境中运行方式都一致。 +数据库存在于内存中;你可以通过 `save` 或 `close` 将其持久化到文件。 + +## 安装 + +```yaml +dependencies: + dart_node_sql_js: ^0.13.0-beta + nadz: ^0.0.7-beta +``` + +同时安装 npm 包: + +```bash +npm install sql.js +``` + +## 快速开始 + +```dart +import 'package:dart_node_sql_js/dart_node_sql_js.dart'; +import 'package:nadz/nadz.dart'; + +Future main() async { + // 启动时初始化一次 WebAssembly 运行时。 + final runtime = switch (await initializeSqlJs()) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), + }; + + // 如果 ./my.db 存在则打开它,否则创建一个新数据库。 + final db = switch (openDatabase('./my.db', sqlJs: runtime)) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), + }; + + db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)'); + + final insert = switch (db.prepare('INSERT INTO users (name) VALUES (?)')) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), + }; + insert.run(['Alice']); + + final query = switch (db.prepare('SELECT * FROM users')) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), + }; + final rows = switch (query.all()) { + Success(:final value) => value, + Error(:final error) => throw Exception(error), + }; + print(rows); // [{id: 1, name: Alice}] + + // 将内存中的数据库持久化到 ./my.db。 + db.close(); +} +``` + +## 持久化 + +sql.js 完全在内存中运行。序列化它(`export()`)的唯一方式会释放所有存活的预编译语句, +因此该绑定**不会**在每条语句之后写入磁盘。而是: + +- `db.save()` 按需将当前状态刷新到后端文件。 +- `db.close()` 先保存,再释放数据库。 + +重新打开同一路径会加载已持久化的字节。 + +## API + +`initializeSqlJs()` 返回 `Future>`。将该运行时传给 +每一次 `openDatabase` 调用。 + +`openDatabase(path, sqlJs: runtime)` 返回 `Result`,包含: + +| 成员 | 说明 | +|--------|-------------| +| `prepare(sql)` | 准备一条可复用的语句。 | +| `exec(sql)` | 执行一条或多条语句,忽略结果。 | +| `pragma(value)` | 执行 `PRAGMA`。 | +| `save()` | 将内存中的数据库持久化到其文件。 | +| `close()` | 先持久化,再关闭。 | +| `isOpen()` | 数据库是否仍处于打开状态。 | + +预编译的 `Statement` 提供: + +| 成员 | 说明 | +|--------|-------------| +| `all([params])` | 以列名为键的映射列表返回所有行。 | +| `get([params])` | 第一行,或 null。 | +| `run([params])` | 执行,返回 `changes` 和 `lastInsertRowid`。 | + +每个操作都返回 `Result`(来自 [nadz](https://pub.dev/packages/nadz)),而不是抛出异常。 + +## 许可证 + +BSD 3-Clause。参见 [LICENSE](LICENSE)。 diff --git a/packages/dart_node_vsix/README.md b/packages/dart_node_vsix/README.md index 522feed..a354540 100644 --- a/packages/dart_node_vsix/README.md +++ b/packages/dart_node_vsix/README.md @@ -6,7 +6,7 @@ Type-safe VSCode extension API bindings for Dart. Build Visual Studio Code exten ```yaml dependencies: - dart_node_vsix: ^0.11.0-beta + dart_node_vsix: ^0.13.0-beta ``` ## Quick Start @@ -271,8 +271,8 @@ void main() { ## Example -See [too_many_cooks_vscode_extension](https://github.com/melbournedeveloper/too_many_cooks) for a complete real-world example. +Too Many Cooks, an MCP server originally built in this ecosystem, shipped a VSCode extension. It has since moved to its own home at [tmc-mcp.dev](https://tmc-mcp.dev) and is no longer part of this repository. ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_vsix). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_vsix). diff --git a/packages/dart_node_ws/README.md b/packages/dart_node_ws/README.md index 4bd3533..0ac64ca 100644 --- a/packages/dart_node_ws/README.md +++ b/packages/dart_node_ws/README.md @@ -6,7 +6,7 @@ Type-safe WebSocket bindings for Node.js, enabling real-time bidirectional commu ```yaml dependencies: - dart_node_ws: ^0.11.0-beta + dart_node_ws: ^0.13.0-beta ``` Also install the ws package via npm: @@ -271,4 +271,4 @@ server.onConnection((client, url) { ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_ws). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_ws). diff --git a/packages/dart_node_ws/README_zh.md b/packages/dart_node_ws/README_zh.md index e1898f9..08d9166 100644 --- a/packages/dart_node_ws/README_zh.md +++ b/packages/dart_node_ws/README_zh.md @@ -6,7 +6,7 @@ ```yaml dependencies: - dart_node_ws: ^0.11.0-beta + dart_node_ws: ^0.13.0-beta ``` 通过 npm 安装 ws 包: @@ -271,4 +271,4 @@ server.onConnection((client, url) { ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/dart_node_ws) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_ws) 上获取。 diff --git a/packages/reflux/README.md b/packages/reflux/README.md index a442c8b..45b7aa3 100644 --- a/packages/reflux/README.md +++ b/packages/reflux/README.md @@ -6,7 +6,7 @@ Reflux is a state management library for **React with Dart** and **Flutter**. It ```yaml dependencies: - reflux: ^0.11.0-beta + reflux: ^0.13.0-beta ``` ## Core Concepts @@ -142,4 +142,4 @@ See the [full API documentation](/api/reflux/) for all available functions and t ## Source Code -The source code is available on [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/reflux). +The source code is available on [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/reflux). diff --git a/packages/reflux/README_zh.md b/packages/reflux/README_zh.md index b4ec479..3179b96 100644 --- a/packages/reflux/README_zh.md +++ b/packages/reflux/README_zh.md @@ -5,7 +5,7 @@ Reflux 是一个用于 **React with Dart** 和 **Flutter** 的状态管理库。 ```yaml dependencies: - reflux: ^0.11.0-beta + reflux: ^0.13.0-beta ``` ## 核心概念 @@ -141,4 +141,4 @@ timeTravel.redo(); // 前进一步 ## 源代码 -源代码可在 [GitHub](https://github.com/melbournedeveloper/dart_node/tree/main/packages/reflux) 上获取。 +源代码可在 [GitHub](https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/reflux) 上获取。 diff --git a/website/.gitignore b/website/.gitignore index 73fe75f..985b940 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -9,6 +9,7 @@ src/docs/mcp/index.md src/docs/logging/index.md src/docs/reflux/index.md src/docs/jsx/index.md +src/docs/sql-js/index.md src/zh/docs/core/index.md src/zh/docs/express/index.md src/zh/docs/react/index.md @@ -19,3 +20,4 @@ src/zh/docs/mcp/index.md src/zh/docs/logging/index.md src/zh/docs/reflux/index.md src/zh/docs/jsx/index.md +src/zh/docs/sql-js/index.md diff --git a/website/eleventy.config.js b/website/eleventy.config.js index edf96dc..49754f2 100644 --- a/website/eleventy.config.js +++ b/website/eleventy.config.js @@ -27,8 +27,8 @@ const techdocOptions = { author: "dart_node team", themeColor: "#0E7C6B", stylesheet: "/assets/css/styles.css", - twitterSite: "@dart_node", - twitterCreator: "@dart_node", + twitterSite: "@cfdevelop", + twitterCreator: "@cfdevelop", ogImage: "/assets/images/og-image.png", ogImageWidth: "1200", ogImageHeight: "630", @@ -36,8 +36,8 @@ const techdocOptions = { name: "dart_node", logo: "/assets/images/og-image.png", sameAs: [ - "https://github.com/melbournedeveloper/dart_node", - "https://twitter.com/dart_node", + "https://github.com/MelbourneDeveloper/dart_node", + "https://x.com/cfdevelop", "https://pub.dev/publishers/christianfindlay.com/packages" ] } diff --git a/website/package-lock.json b/website/package-lock.json index d4ef921..5beaa96 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -8,11 +8,11 @@ "name": "dart-node-website", "version": "1.0.0", "devDependencies": { - "@11ty/eleventy": "^3.1.2", + "@11ty/eleventy": "^3.1.6", "@babel/core": "^7.28.6", "@playwright/test": "^1.57.0", "babel-plugin-istanbul": "^7.0.1", - "eleventy-plugin-techdoc": "^0.1.0", + "eleventy-plugin-techdoc": "^0.2.0", "istanbul-lib-instrument": "^6.0.3", "jsdom": "^24.1.3", "nyc": "^17.1.0", @@ -20,9 +20,9 @@ } }, "node_modules/@11ty/dependency-tree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@11ty/dependency-tree/-/dependency-tree-4.0.0.tgz", - "integrity": "sha512-PTOnwM8Xt+GdJmwRKg4pZ8EKAgGoK7pedZBfNSOChXu8MYk2FdEsxdJYecX4t62owpGw3xK60q9TQv/5JI59jw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@11ty/dependency-tree/-/dependency-tree-4.0.2.tgz", + "integrity": "sha512-RTF6VTZHatYf7fSZBUN3RKwiUeJh5dhWV61gDPrHhQF2/gzruAkYz8yXuvGLx3w3ZBKreGrR+MfYpSVkdbdbLA==", "dev": true, "license": "MIT", "dependencies": { @@ -30,9 +30,9 @@ } }, "node_modules/@11ty/dependency-tree-esm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@11ty/dependency-tree-esm/-/dependency-tree-esm-2.0.2.tgz", - "integrity": "sha512-kSTmXneksQLBhwsfqjxiSi9ecRKENXmRtT5RG95rFoWSI8kkwLcGlYpoXsPkCD9uQwSU1rmDzXBDnqUJlWaIyw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@11ty/dependency-tree-esm/-/dependency-tree-esm-2.0.4.tgz", + "integrity": "sha512-MYKC0Ac77ILr1HnRJalzKDlb9Z8To3kXQCltx299pUXXUFtJ1RIONtULlknknqW8cLe19DLVgmxVCtjEFm7h0A==", "dev": true, "license": "MIT", "dependencies": { @@ -43,45 +43,45 @@ } }, "node_modules/@11ty/eleventy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-3.1.2.tgz", - "integrity": "sha512-IcsDlbXnBf8cHzbM1YBv3JcTyLB35EK88QexmVyFdVJVgUU6bh9g687rpxryJirHzo06PuwnYaEEdVZQfIgRGg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-3.1.6.tgz", + "integrity": "sha512-ZlSiR1PLdS2lv7TelBgWAhcvMiLNZkPBlLEb+lh7kGYZ+Mk0bo9qcYgVsewvw9W7Em0RH3wd01h5fAstNDh0zA==", "dev": true, "license": "MIT", "dependencies": { - "@11ty/dependency-tree": "^4.0.0", - "@11ty/dependency-tree-esm": "^2.0.0", + "@11ty/dependency-tree": "^4.0.2", + "@11ty/dependency-tree-esm": "^2.0.4", "@11ty/eleventy-dev-server": "^2.0.8", - "@11ty/eleventy-plugin-bundle": "^3.0.6", + "@11ty/eleventy-plugin-bundle": "^3.0.7", "@11ty/eleventy-utils": "^2.0.7", "@11ty/lodash-custom": "^4.17.21", - "@11ty/posthtml-urls": "^1.0.1", - "@11ty/recursive-copy": "^4.0.2", + "@11ty/posthtml-urls": "^1.0.3", + "@11ty/recursive-copy": "^4.0.4", "@sindresorhus/slugify": "^2.2.1", "bcp-47-normalize": "^2.3.0", "chokidar": "^3.6.0", - "debug": "^4.4.1", + "debug": "^4.4.3", "dependency-graph": "^1.0.0", "entities": "^6.0.1", "filesize": "^10.1.6", "gray-matter": "^4.0.3", "iso-639-1": "^3.1.5", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "kleur": "^4.1.5", - "liquidjs": "^10.21.1", - "luxon": "^3.6.1", - "markdown-it": "^14.1.0", + "liquidjs": "^10.27.0", + "luxon": "^3.7.2", + "markdown-it": "^14.2.0", "minimist": "^1.2.8", - "moo": "^0.5.2", + "moo": "0.5.2", "node-retrieve-globals": "^6.0.1", "nunjucks": "^3.2.4", - "picomatch": "^4.0.2", + "picomatch": "^4.0.4", "please-upgrade-node": "^3.2.0", - "posthtml": "^0.16.6", + "posthtml": "^0.16.7", "posthtml-match-helper": "^2.0.3", - "semver": "^7.7.2", - "slugify": "^1.6.6", - "tinyglobby": "^0.2.14" + "semver": "^7.8.1", + "slugify": "^1.6.9", + "tinyglobby": "^0.2.16" }, "bin": { "eleventy": "cmd.cjs" @@ -126,29 +126,19 @@ } }, "node_modules/@11ty/eleventy-navigation": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@11ty/eleventy-navigation/-/eleventy-navigation-0.3.5.tgz", - "integrity": "sha512-4aKW5aIQDFed8xs1G1pWcEiFPcDSwZtA4IH1eERtoJ+Xy+/fsoe0pzbDmw84bHZ9ACny5jblENhfZhcCxklqQw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-navigation/-/eleventy-navigation-1.0.5.tgz", + "integrity": "sha512-zb6xe29cM9viSdYtZywKIkJw2HIROyBINdBcFWC9uD0c/jYOTAex5nwy3HNEuh5t6/Ld/S9V4gEizfmeYuYpCQ==", "dev": true, "license": "MIT", "dependencies": { - "dependency-graph": "^0.11.0" + "dependency-graph": "^1.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/11ty" } }, - "node_modules/@11ty/eleventy-navigation/node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/@11ty/eleventy-plugin-bundle": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-3.0.7.tgz", @@ -169,16 +159,16 @@ } }, "node_modules/@11ty/eleventy-plugin-rss": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-rss/-/eleventy-plugin-rss-2.0.4.tgz", - "integrity": "sha512-LF60sGVlxGTryQe3hTifuzrwF8R7XbrNsM2xfcDcNMSliLN4kmB+7zvoLRySRx0AQDjqhPTAeeeT0ra6/9zHUQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-rss/-/eleventy-plugin-rss-3.0.0.tgz", + "integrity": "sha512-kKW4DcR57xAyRx0e8gNhKh56ahHVEaAj8/TuXQDnw+B46ig2bWADJAlyj/GdV37IG5ja9dZ4SgKZrs/CHz6YWQ==", "dev": true, "license": "MIT", "dependencies": { - "@11ty/eleventy-utils": "^2.0.0", - "@11ty/posthtml-urls": "^1.0.1", - "debug": "^4.4.0", - "posthtml": "^0.16.6" + "@11ty/eleventy-utils": "^2.0.7", + "@11ty/posthtml-urls": "^1.0.2", + "debug": "^4.4.3", + "posthtml": "^0.16.7" }, "funding": { "type": "opencollective", @@ -228,9 +218,9 @@ } }, "node_modules/@11ty/posthtml-urls": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@11ty/posthtml-urls/-/posthtml-urls-1.0.1.tgz", - "integrity": "sha512-6EFN/yYSxC/OzYXpq4gXDyDMlX/W+2MgCvvoxf11X1z76bqkqFJ8eep5RiBWfGT5j0323a1pwpelcJJdR46MCw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@11ty/posthtml-urls/-/posthtml-urls-1.0.3.tgz", + "integrity": "sha512-1YvhnkaNlFnnJic1rBMWmTC2adbuy+JQiBfl1Hecr1Wjjik1pQZmGyk/eC9zKX/FQv52s2Nht1Gi/UwhYqrBeg==", "dev": true, "license": "MIT", "dependencies": { @@ -244,15 +234,15 @@ } }, "node_modules/@11ty/recursive-copy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@11ty/recursive-copy/-/recursive-copy-4.0.2.tgz", - "integrity": "sha512-174nFXxL/6KcYbLYpra+q3nDbfKxLxRTNVY1atq2M1pYYiPfHse++3IFNl8mjPFsd7y2qQjxLORzIjHMjL3NDQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@11ty/recursive-copy/-/recursive-copy-4.0.4.tgz", + "integrity": "sha512-oI7m8pa7/IAU/3lqRU9vjBbs20iKFo7x+1K9kT3aVira6scc1X9MjBdgLCHzLJeJ7iB6wydioA+kr9/qPnvmlQ==", "dev": true, "license": "ISC", "dependencies": { "errno": "^1.0.0", "junk": "^3.1.0", - "maximatch": "^0.1.0", + "minimatch": "^3.1.5", "slash": "^3.0.0" }, "engines": { @@ -676,30 +666,29 @@ } }, "node_modules/@inquirer/ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", - "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" } }, "node_modules/@inquirer/checkbox": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", - "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.1.tgz", + "integrity": "sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -711,17 +700,17 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz", + "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -733,23 +722,22 @@ } }, "node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.3" + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -774,18 +762,18 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.23", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", - "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.2.2.tgz", + "integrity": "sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/external-editor": "^1.0.3", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/external-editor": "^3.0.3", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -797,18 +785,17 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", - "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.1.tgz", + "integrity": "sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -820,17 +807,17 @@ } }, "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==", "dev": true, "license": "MIT", "dependencies": { "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" + "iconv-lite": "^0.7.2" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -859,27 +846,27 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" } }, "node_modules/@inquirer/input": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", - "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.2.tgz", + "integrity": "sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -891,17 +878,17 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", - "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.1.1.tgz", + "integrity": "sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -913,18 +900,18 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", - "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.1.1.tgz", + "integrity": "sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -936,25 +923,25 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", - "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.5.2.tgz", + "integrity": "sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.3.2", - "@inquirer/confirm": "^5.1.21", - "@inquirer/editor": "^4.2.23", - "@inquirer/expand": "^4.0.23", - "@inquirer/input": "^4.3.1", - "@inquirer/number": "^3.0.23", - "@inquirer/password": "^4.0.23", - "@inquirer/rawlist": "^4.1.11", - "@inquirer/search": "^3.2.2", - "@inquirer/select": "^4.4.2" + "@inquirer/checkbox": "^5.2.1", + "@inquirer/confirm": "^6.1.1", + "@inquirer/editor": "^5.2.2", + "@inquirer/expand": "^5.1.1", + "@inquirer/input": "^5.1.2", + "@inquirer/number": "^4.1.1", + "@inquirer/password": "^5.1.1", + "@inquirer/rawlist": "^5.3.1", + "@inquirer/search": "^4.2.1", + "@inquirer/select": "^5.2.1" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -966,18 +953,17 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", - "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.1.tgz", + "integrity": "sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -989,19 +975,18 @@ } }, "node_modules/@inquirer/search": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", - "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.2.1.tgz", + "integrity": "sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1013,20 +998,19 @@ } }, "node_modules/@inquirer/select": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", - "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.1.tgz", + "integrity": "sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1038,13 +1022,13 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1377,49 +1361,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2003,16 +1944,16 @@ "license": "ISC" }, "node_modules/eleventy-plugin-techdoc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/eleventy-plugin-techdoc/-/eleventy-plugin-techdoc-0.1.0.tgz", - "integrity": "sha512-XAMXG5j+jpOpmojAet5ZwSyB7hV5JhFkAW6viZn9nxu0BjLUwg7hMQ7MKmpyKdWZiBjOQpcHlDmSgK75pD2qTw==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eleventy-plugin-techdoc/-/eleventy-plugin-techdoc-0.2.0.tgz", + "integrity": "sha512-4bDYIPMk/maqXyX0+LTYHc8HdgmqAa2WtKISq2llMz/RUz65MsP60hfx+XMtbnRPP3Dkonf+65rKpbLD+PU20A==", "dev": true, "license": "MIT", "dependencies": { - "@11ty/eleventy-navigation": "^0.3.5", - "@11ty/eleventy-plugin-rss": "^2.0.2", + "@11ty/eleventy-navigation": "^1.0.5", + "@11ty/eleventy-plugin-rss": "^3.0.0", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0", - "@inquirer/prompts": "^7.0.0", + "@inquirer/prompts": "^8.5.2", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0" }, @@ -2212,6 +2153,33 @@ "node": ">=0.10.0" } }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3251,19 +3219,29 @@ } }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" } }, "node_modules/liquidjs": { - "version": "10.24.0", - "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.24.0.tgz", - "integrity": "sha512-TAUNAdgwaAXjjcUFuYVJm9kOVH7zc0mTKxsG9t9Lu4qdWjB2BEblyVIYpjWcmJLMGgiYqnGNJjpNMHx0gp/46A==", + "version": "10.27.0", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.27.0.tgz", + "integrity": "sha512-tw/OA59K7aIBlMKIrKlumr37fiZUheShVHXY8cVctWisgY1p9mc5hreOvlreoS0wTiwlWk14Ya7305c2a/Cg5w==", "dev": true, "license": "MIT", "dependencies": { @@ -3352,15 +3330,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "linkify-it": "^5.0.1", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -3403,22 +3391,6 @@ "node": ">= 0.4" } }, - "node_modules/maximatch": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/maximatch/-/maximatch-0.1.0.tgz", - "integrity": "sha512-9ORVtDUFk4u/NFfo0vG/ND/z7UQCVZBL539YW0+U1I7H1BkZwizcPx5foFv7LCPcBnm2U6RjFnQOsIvN4/Vm2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-differ": "^1.0.0", - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "minimatch": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -3467,9 +3439,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -3521,13 +3493,13 @@ "license": "MIT" }, "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-preload": { @@ -3816,9 +3788,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -4163,9 +4135,9 @@ } }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", "dev": true, "license": "ISC", "bin": { @@ -4260,9 +4232,9 @@ } }, "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz", + "integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==", "dev": true, "license": "MIT", "engines": { @@ -4425,14 +4397,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -4828,19 +4800,6 @@ "engines": { "node": ">=6" } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/website/package.json b/website/package.json index b324e49..a918677 100644 --- a/website/package.json +++ b/website/package.json @@ -18,11 +18,11 @@ "update:theme": "npm update eleventy-plugin-techdoc" }, "devDependencies": { - "@11ty/eleventy": "^3.1.2", + "@11ty/eleventy": "^3.1.6", "@babel/core": "^7.28.6", "@playwright/test": "^1.57.0", "babel-plugin-istanbul": "^7.0.1", - "eleventy-plugin-techdoc": "^0.1.0", + "eleventy-plugin-techdoc": "^0.2.0", "istanbul-lib-instrument": "^6.0.3", "jsdom": "^24.1.3", "nyc": "^17.1.0", diff --git a/website/scripts/copy-readmes.js b/website/scripts/copy-readmes.js index b79e3c8..b2ca1d2 100644 --- a/website/scripts/copy-readmes.js +++ b/website/scripts/copy-readmes.js @@ -27,17 +27,31 @@ const packageToDocsMap = { 'dart_node_mcp': { slug: 'mcp', title: 'dart_node_mcp', order: 7, pubdev: 'dart_node_mcp' }, 'dart_logging': { slug: 'logging', title: 'dart_logging', order: 8, pubdev: 'dart_logging' }, 'reflux': { slug: 'reflux', title: 'reflux', order: 9, pubdev: 'reflux' }, - 'dart_jsx': { slug: 'jsx', title: 'dart_jsx', order: 10, pubdev: 'dart_jsx' }, + 'dart_node_sql_js': { slug: 'sql-js', title: 'dart_node_sql_js', order: 10, pubdev: 'dart_node_sql_js' }, + // dart_jsx is not published to pub.dev (publish_to: none) — link to source instead. + 'dart_jsx': { slug: 'jsx', title: 'dart_jsx', order: 11, pubdev: null }, }; -// CTA HTML to inject after installation sections -function getPackageLinksHtml(pubdevPackage, lang = 'en') { - const viewText = lang === 'zh' ? '在 pub.dev 查看' : 'View on pub.dev'; +// CTA HTML to inject after installation sections. +// Published packages link to pub.dev; unpublished ones (pubdev === null) +// link to their source folder on GitHub so the button never 404s. +const repoUrl = 'https://github.com/MelbourneDeveloper/dart_node'; + +function getPackageLinksHtml(config, packageDir, lang = 'en') { const starText = lang === 'zh' ? '给个 Star' : 'Star on GitHub'; + const primary = config.pubdev + ? { + href: `https://pub.dev/packages/${config.pubdev}`, + text: lang === 'zh' ? '在 pub.dev 查看' : 'View on pub.dev', + } + : { + href: `${repoUrl}/tree/main/packages/${packageDir}`, + text: lang === 'zh' ? '在 GitHub 查看源码' : 'View source on GitHub', + }; return ` `; } @@ -97,13 +111,13 @@ function processReadme(content, packageName, config, lang = 'en') { let result = lines.slice(startIndex).join('\n').trim(); // Inject package links after the first code block following "## Installation" or "## 安装" - if (config && config.pubdev) { + if (config) { const installationPattern = lang === 'zh' ? /(## 安装[\s\S]*?```[\s\S]*?```)/ : /(## Installation[\s\S]*?```[\s\S]*?```)/; const installationMatch = result.match(installationPattern); if (installationMatch) { - const packageLinks = getPackageLinksHtml(config.pubdev, lang); + const packageLinks = getPackageLinksHtml(config, packageName, lang); result = result.replace(installationMatch[0], installationMatch[0] + packageLinks); } } diff --git a/website/scripts/generate-api-docs.js b/website/scripts/generate-api-docs.js index b32e06d..9a985db 100644 --- a/website/scripts/generate-api-docs.js +++ b/website/scripts/generate-api-docs.js @@ -40,6 +40,7 @@ const PACKAGES = [ 'dart_node_react_native', 'dart_node_ws', 'dart_node_better_sqlite3', + 'dart_node_sql_js', 'dart_node_mcp', 'dart_logging', 'reflux', @@ -280,8 +281,11 @@ const processContent = (element, packageName, dom, langPrefix = '') => { // Links like ../dart_node_ws/Foo.html -> /api/dart_node_ws/Foo/ // (removing the duplicate package/package structure) - // Apply language prefix for non-English versions - newHref.startsWith('../') && (newHref = newHref.replace(/^\.\.\//, `${langPrefix}/api/`)); + // dartdoc emits one or more leading "../" depending on the source page + // depth (e.g. ../../dart_node_ws/Foo.html). Collapse the entire leading + // "../" run to /api/ so deep pages don't produce /api/../ (404) links. + // Apply language prefix for non-English versions. + newHref.startsWith('../') && (newHref = newHref.replace(/^(?:\.\.\/)+/, `${langPrefix}/api/`)); // Links like Foo.html -> Foo/ (relative, stays same level) // Links like Foo/bar.html -> Foo/bar/ @@ -478,6 +482,11 @@ description: Complete API documentation for all dart_node packages

SQLite bindings using better-sqlite3 for Node.js.

+ +

dart_node_sql_js

+

sql.js bindings — SQLite compiled to WebAssembly, in-memory with disk persistence.

+
+

dart_node_mcp

Model Context Protocol (MCP) bindings for AI tool integration.

diff --git a/website/src/_data/navigation.json b/website/src/_data/navigation.json index 97c4a55..5970c15 100644 --- a/website/src/_data/navigation.json +++ b/website/src/_data/navigation.json @@ -14,7 +14,7 @@ }, { "text": "GitHub", - "url": "https://github.com/melbournedeveloper/dart_node", + "url": "https://github.com/MelbourneDeveloper/dart_node", "external": true } ], @@ -67,6 +67,10 @@ "text": "dart_node_better_sqlite3", "url": "/docs/sqlite/" }, + { + "text": "dart_node_sql_js", + "url": "/docs/sql-js/" + }, { "text": "dart_node_mcp", "url": "/docs/mcp/" @@ -100,7 +104,8 @@ }, { "text": "Examples", - "url": "/docs/examples/" + "url": "https://github.com/MelbourneDeveloper/dart_node/tree/main/examples", + "external": true } ] }, @@ -113,11 +118,11 @@ }, { "text": "pub.dev", - "url": "https://pub.dev/publishers/dartnode.dev/packages" + "url": "https://pub.dev/publishers/christianfindlay.com/packages" }, { - "text": "Twitter", - "url": "https://twitter.com/dart_node" + "text": "X (Twitter)", + "url": "https://x.com/cfdevelop" } ] }, diff --git a/website/src/_data/navigation_zh.json b/website/src/_data/navigation_zh.json index 931cd35..1e550fa 100644 --- a/website/src/_data/navigation_zh.json +++ b/website/src/_data/navigation_zh.json @@ -14,7 +14,7 @@ }, { "text": "GitHub", - "url": "https://github.com/melbournedeveloper/dart_node", + "url": "https://github.com/MelbourneDeveloper/dart_node", "external": true } ], @@ -67,6 +67,10 @@ "text": "dart_node_better_sqlite3", "url": "/zh/docs/sqlite/" }, + { + "text": "dart_node_sql_js", + "url": "/zh/docs/sql-js/" + }, { "text": "dart_node_mcp", "url": "/zh/docs/mcp/" @@ -100,7 +104,8 @@ }, { "text": "示例", - "url": "/zh/docs/examples/" + "url": "https://github.com/MelbourneDeveloper/dart_node/tree/main/examples", + "external": true } ] }, @@ -113,11 +118,11 @@ }, { "text": "pub.dev", - "url": "https://pub.dev/publishers/dartnode.dev/packages" + "url": "https://pub.dev/publishers/christianfindlay.com/packages" }, { - "text": "Twitter", - "url": "https://twitter.com/dart_node" + "text": "X (Twitter)", + "url": "https://x.com/cfdevelop" } ] }, diff --git a/website/src/_data/site.json b/website/src/_data/site.json index d4096ed..0abfd29 100644 --- a/website/src/_data/site.json +++ b/website/src/_data/site.json @@ -6,10 +6,10 @@ "author": "dart_node team", "language": "en", "themeColor": "#0E7C6B", - "twitter": "@dart_node", - "twitterSite": "@dart_node", - "twitterCreator": "@dart_node", - "github": "https://github.com/melbournedeveloper/dart_node", + "twitter": "@cfdevelop", + "twitterSite": "@cfdevelop", + "twitterCreator": "@cfdevelop", + "github": "https://github.com/MelbourneDeveloper/dart_node", "stylesheet": "/assets/css/styles.css", "ogImage": "/assets/images/og-image.png", "ogImageWidth": "1200", @@ -19,8 +19,8 @@ "name": "dart_node", "logo": "/assets/images/og-image.png", "sameAs": [ - "https://github.com/melbournedeveloper/dart_node", - "https://twitter.com/dart_node", + "https://github.com/MelbourneDeveloper/dart_node", + "https://x.com/cfdevelop", "https://pub.dev/publishers/christianfindlay.com/packages" ] } diff --git a/website/src/_includes/layouts/base.njk b/website/src/_includes/layouts/base.njk index 80e92e2..ed9b3bc 100644 --- a/website/src/_includes/layouts/base.njk +++ b/website/src/_includes/layouts/base.njk @@ -87,7 +87,7 @@ } - + @@ -319,7 +311,7 @@

{{ section.title }}

@@ -333,14 +325,14 @@ {% if lang == 'zh' %}给个 Star{% else %}Star on GitHub{% endif %} - + {% if lang == 'zh' %}在 pub.dev 点赞{% else %}Like on pub.dev{% endif %} diff --git a/website/src/assets/images/apple-touch-icon.png b/website/src/assets/images/apple-touch-icon.png new file mode 100644 index 0000000..4d71e23 Binary files /dev/null and b/website/src/assets/images/apple-touch-icon.png differ diff --git a/website/src/assets/images/og-image.png b/website/src/assets/images/og-image.png new file mode 100644 index 0000000..6dfdd1e Binary files /dev/null and b/website/src/assets/images/og-image.png differ diff --git a/website/src/blog/build-react-website-with-dart.md b/website/src/blog/build-react-website-with-dart.md index d2e3ae5..030850a 100644 --- a/website/src/blog/build-react-website-with-dart.md +++ b/website/src/blog/build-react-website-with-dart.md @@ -42,8 +42,8 @@ environment: sdk: ^3.0.0 dependencies: - dart_node_core: ^0.11.0-beta - dart_node_react: ^0.11.0-beta + dart_node_core: ^0.13.0-beta + dart_node_react: ^0.13.0-beta ``` Run `dart pub get`. Done. No webpack config. No babel. No 47 dev dependencies fighting each other. @@ -93,11 +93,11 @@ ReactElement Counter() => createElement( h2('Count: ${count.value}'), button( text: 'Increment', - onClick: (_) => count.setWithUpdater((c) => c + 1), + onClick: () => count.setWithUpdater((c) => c + 1), ), button( text: 'Reset', - onClick: (_) => count.set(0), + onClick: () => count.set(0), ), ], ); @@ -115,7 +115,29 @@ No more `useState(undefined)` gymnastics. Just `useState(0)` ## Building Forms (The Part Everyone Dreads) -Forms don't have to be painful. Here's a login form that actually works: +Forms don't have to be painful. The `input` function gives you an `onChange` +callback that receives a typed `SyntheticEvent`. To read the current value, write +a tiny helper that pulls `target.value` off the event: + +```dart +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'package:dart_node_react/dart_node_react.dart'; + +/// Extract the input value from a change event. +JSString getInputValue(SyntheticEvent event) { + final target = event.target; + return switch (target) { + final JSObject t => switch (t['value']) { + final JSString v => v, + _ => throw StateError('Input value is not a string'), + }, + _ => throw StateError('Event target is not an object'), + }; +} +``` + +Now the login form is just a handful of typed elements: ```dart ReactElement LoginForm() => createElement( @@ -195,7 +217,7 @@ ReactElement UserList() => createElement( else ul( children: usersState.value - .map((user) => li(child: span(user))) + .map((user) => li(user)) .toList(), ), ], @@ -224,8 +246,8 @@ ReactElement PageLayout() => createElement( className: 'main-content', children: [ section( - className: 'hero', - children: [ + {'className': 'hero'}, + [ h2('Welcome'), pEl('Build type-safe React apps with Dart.'), ], @@ -318,16 +340,17 @@ ReactElement TaskManager() => createElement( button(text: 'Add', onClick: addTask), ], ), - ul( + div( className: 'task-list', children: tasksState.value.indexed .map( - (item) => li( + (item) => div( + className: 'task-item', children: [ span(item.$2), button( text: 'Delete', - onClick: (_) => removeTask(item.$1), + onClick: () => removeTask(item.$1), ), ], ), @@ -344,7 +367,7 @@ State management, event handling, list rendering. All type-safe. All Dart. ## What's Next? -You've got the basics. Now go build something. Explore [more hooks](/api/dart_node_react/) like `useMemo` and `useCallback`. Check out the [full-stack example](https://github.com/AstroCodez/dart_node/tree/main/examples/frontend) with authentication, API integration, and WebSocket support. +You've got the basics. Now go build something. Explore [more hooks](/api/dart_node_react/) like `useMemo` and `useCallback`. Check out the [full-stack example](https://github.com/MelbourneDeveloper/dart_node/tree/main/examples/frontend) with authentication, API integration, and WebSocket support. No more fighting with type coercion. No more `any` escape hatches. Just clean, type-safe React apps in a language that respects your time. diff --git a/website/src/blog/introducing-dart-node.md b/website/src/blog/introducing-dart-node.md index 3fa1ee0..a03736d 100644 --- a/website/src/blog/introducing-dart-node.md +++ b/website/src/blog/introducing-dart-node.md @@ -1,6 +1,6 @@ --- layout: layouts/blog.njk -title: "Introducing dart_node: Full-Stack Dart for the JavaScript Ecosystem" +title: "Introducing dart_node: Full-Stack Dart for JavaScript" description: "We're excited to announce dart_node, a framework for building React, React Native, and Express applications entirely in Dart." date: 2024-01-15 author: "dart_node team" @@ -103,8 +103,8 @@ ReactElement counter() { final count = useState(0); return button( - onClick: (_) => count.setWithUpdater((c) => c + 1), - children: [text('Count: ${count.value}')], + text: 'Count: ${count.value}', + onClick: () => count.setWithUpdater((c) => c + 1), ); } ``` @@ -117,7 +117,7 @@ React Native bindings for mobile development. Use with Expo for a complete mobil ReactElement app() { return safeAreaView(children: [ view(style: {'padding': 20}, children: [ - rnText(children: [text('Hello from Dart!')]), + text('Hello from Dart!'), ]), ]); } diff --git a/website/src/docs/dart-to-js.md b/website/src/docs/dart-to-js.md index 4edf729..800078f 100644 --- a/website/src/docs/dart-to-js.md +++ b/website/src/docs/dart-to-js.md @@ -34,11 +34,11 @@ dart compile js lib/main.dart -o build/main.js -O2 | Level | Description | Use Case | |-------|-------------|----------| -| `-O0` | No optimization | Debugging | -| `-O1` | Basic optimization | Development | -| `-O2` | Full optimization (default) | Production | -| `-O3` | Aggressive optimization | Maximum performance | -| `-O4` | Most aggressive | When size/speed is critical | +| `-O0` | No optimizations | Debugging only | +| `-O1` | Default: whole-program analysis and inlining | Development / default | +| `-O2` | Safe production-oriented optimizations (e.g. minification) | Production | +| `-O3` | Potentially unsafe optimizations | Maximum performance | +| `-O4` | More aggressive unsafe optimizations | When size/speed is critical | ## Node.js Compatibility @@ -55,10 +55,10 @@ void main() { } ``` -Or use our build tool (recommended): +Wrap this in a small build script and run it after `dart compile js`: ```bash -dart run tools/build/build.dart my_app +dart compile js web/app.dart -o build/app.js -O2 ``` ## Output Structure @@ -180,7 +180,7 @@ Future main(List args) async { 4. **Prefer `const`** - Constant values are evaluated at compile time -5. **Profile your output** - Check the `.js.info` file for size breakdown: +5. **Profile your output** - Use the `--dump-info` output for a size breakdown: ```bash dart compile js lib/main.dart -o build/main.js --dump-info diff --git a/website/src/docs/getting-started.md b/website/src/docs/getting-started.md index ca36eb0..ed50e1b 100644 --- a/website/src/docs/getting-started.md +++ b/website/src/docs/getting-started.md @@ -103,7 +103,7 @@ faq: Welcome to dart_node! This guide will help you build your first application using Dart for the JavaScript ecosystem. @@ -137,8 +137,8 @@ environment: sdk: ^3.10.0 dependencies: - dart_node_core: ^0.11.0-beta - dart_node_express: ^0.11.0-beta + dart_node_core: ^0.13.0-beta + dart_node_express: ^0.13.0-beta ``` Then run: @@ -236,7 +236,7 @@ Now that you have a basic server running, explore: ## Example Projects -Check out the [examples directory](https://github.com/melbournedeveloper/dart_node/tree/main/examples) for complete working applications: +Check out the [examples directory](https://github.com/MelbourneDeveloper/dart_node/tree/main/examples) for complete working applications: - **backend/** - Express server with REST API - **frontend/** - React web application @@ -247,5 +247,5 @@ Check out the [examples directory](https://github.com/melbournedeveloper/dart_no If dart_node is useful to you, please consider: - [Star the repository on GitHub](https://github.com/MelbourneDeveloper/dart_node) - It helps others discover the project -- [Like the packages on pub.dev](https://pub.dev/publishers/dartnode.dev/packages) - Boost visibility in the Dart ecosystem -- [Share on social media](https://twitter.com/intent/tweet?text=Check%20out%20dart_node%20-%20Full-Stack%20Dart%20for%20React,%20React%20Native,%20and%20Express!%20https://dartnode.dev) - Spread the word +- [Like the packages on pub.dev](https://pub.dev/publishers/christianfindlay.com/packages) - Boost visibility in the Dart ecosystem +- [Share on social media](https://twitter.com/intent/tweet?text=Check%20out%20dart_node%20-%20Full-Stack%20Dart%20for%20React,%20React%20Native,%20and%20Express!%20https://dartnode.org) - Spread the word diff --git a/website/src/index.njk b/website/src/index.njk index da9d7c7..34a21b7 100644 --- a/website/src/index.njk +++ b/website/src/index.njk @@ -61,13 +61,13 @@ keywords: "dart_node, Dart JavaScript, Dart React, Dart Express, Dart Node.js, T {"@type": "ListItem", "position": 3, "name": "dart_node_react", "description": "React bindings with hooks and JSX-like syntax", "url": "https://dartnode.org/docs/react/"}, {"@type": "ListItem", "position": 4, "name": "dart_node_react_native", "description": "React Native + Expo bindings for mobile development", "url": "https://dartnode.org/docs/react-native/"}, {"@type": "ListItem", "position": 5, "name": "dart_node_ws", "description": "WebSocket bindings for real-time communication", "url": "https://dartnode.org/docs/websockets/"}, - {"@type": "ListItem", "position": 6, "name": "dart_node_better_sqlite3", "description": "Synchronous SQLite3 bindings with WAL mode", "url": "https://dartnode.org/docs/sqlite/"}, - {"@type": "ListItem", "position": 7, "name": "dart_node_mcp", "description": "Model Context Protocol server for AI tools", "url": "https://dartnode.org/docs/mcp/"}, - {"@type": "ListItem", "position": 8, "name": "reflux", "description": "Redux-style state container with pattern matching", "url": "https://dartnode.org/docs/reflux/"}, - {"@type": "ListItem", "position": 9, "name": "dart_logging", "description": "Pino-style structured logging", "url": "https://dartnode.org/docs/logging/"}, - {"@type": "ListItem", "position": 10, "name": "dart_jsx", "description": "JSX transpiler for Dart", "url": "https://dartnode.org/docs/jsx/"}, - {"@type": "ListItem", "position": 11, "name": "dart_node_vsix", "description": "VSCode extension API bindings for building extensions in Dart", "url": "https://dartnode.org/docs/vsix/"}, - {"@type": "ListItem", "position": 12, "name": "too-many-cooks", "description": "Multi-agent coordination MCP server", "url": "https://dartnode.org/docs/too-many-cooks/"} + {"@type": "ListItem", "position": 6, "name": "dart_node_better_sqlite3", "description": "Synchronous native SQLite3 bindings with WAL mode", "url": "https://dartnode.org/docs/sqlite/"}, + {"@type": "ListItem", "position": 7, "name": "dart_node_sql_js", "description": "Typed bindings for sql.js — SQLite compiled to WebAssembly", "url": "https://dartnode.org/docs/sql-js/"}, + {"@type": "ListItem", "position": 8, "name": "dart_node_mcp", "description": "Model Context Protocol server for AI tools", "url": "https://dartnode.org/docs/mcp/"}, + {"@type": "ListItem", "position": 9, "name": "reflux", "description": "Redux-style state container with pattern matching", "url": "https://dartnode.org/docs/reflux/"}, + {"@type": "ListItem", "position": 10, "name": "dart_logging", "description": "Pino-style structured logging", "url": "https://dartnode.org/docs/logging/"}, + {"@type": "ListItem", "position": 11, "name": "dart_jsx", "description": "JSX transpiler for Dart", "url": "https://dartnode.org/docs/jsx/"}, + {"@type": "ListItem", "position": 12, "name": "dart_node_vsix", "description": "VSCode extension API bindings for building extensions in Dart", "url": "https://github.com/MelbourneDeveloper/dart_node/tree/main/packages/dart_node_vsix"} ] } @@ -89,7 +89,7 @@ keywords: "dart_node, Dart JavaScript, Dart React, Dart Express, Dart Node.js, T
@@ -320,13 +320,23 @@ ReactElement counter() {
S

dart_node_better_sqlite3

-

Typed bindings for better-sqlite3 with synchronous SQLite3 and WAL mode.

+

Typed bindings for better-sqlite3 with synchronous native SQLite3 and WAL mode.

+
+
Q
+

dart_node_sql_js

+

Typed bindings for sql.js — SQLite compiled to WebAssembly. In-memory, no native build, with explicit disk persistence.

+ +
+
X

reflux

@@ -353,7 +363,7 @@ ReactElement counter() {

JSX transpiler for Dart — write JSX syntax that compiles to dart_node_react calls.

@@ -362,18 +372,16 @@ ReactElement counter() {

dart_node_vsix

VSCode extension API bindings for building Visual Studio Code extensions in Dart.

T
-

too-many-cooks

-

Multi-agent coordination MCP server for AI agents editing codebases simultaneously.

+

Too Many Cooks

+

Multi-agent coordination MCP server, originally built with dart_node_mcp. It has moved to its own home and is no longer part of this repo.

diff --git a/website/src/zh/api/index.md b/website/src/zh/api/index.md index faf5619..5054461 100644 --- a/website/src/zh/api/index.md +++ b/website/src/zh/api/index.md @@ -41,6 +41,11 @@ description: 所有 dart_node 包的完整 API 文档

使用 better-sqlite3 的 SQLite 绑定。

+ +

dart_node_sql_js

+

sql.js 绑定 —— 编译为 WebAssembly 的 SQLite,纯内存运行并可持久化到磁盘。

+
+

dart_node_mcp

模型上下文协议(MCP)绑定,用于 AI 工具集成。

diff --git a/website/src/zh/blog/introducing-dart-node.md b/website/src/zh/blog/introducing-dart-node.md index 4513659..bb84dd8 100644 --- a/website/src/zh/blog/introducing-dart-node.md +++ b/website/src/zh/blog/introducing-dart-node.md @@ -105,8 +105,8 @@ ReactElement counter() { final count = useState(0); return button( - onClick: (_) => count.setWithUpdater((c) => c + 1), - children: [text('Count: ${count.value}')], + text: 'Count: ${count.value}', + onClick: () => count.setWithUpdater((c) => c + 1), ); } ``` @@ -119,7 +119,7 @@ ReactElement counter() { ReactElement app() { return safeAreaView(children: [ view(style: {'padding': 20}, children: [ - rnText(children: [text('Hello from Dart!')]), + text('Hello from Dart!'), ]), ]); } diff --git a/website/src/zh/docs/dart-to-js.md b/website/src/zh/docs/dart-to-js.md index 020dfa9..8e28f3a 100644 --- a/website/src/zh/docs/dart-to-js.md +++ b/website/src/zh/docs/dart-to-js.md @@ -36,11 +36,11 @@ dart compile js lib/main.dart -o build/main.js -O2 | 级别 | 说明 | 使用场景 | |-------|-------------|----------| -| `-O0` | 无优化 | 调试 | -| `-O1` | 基本优化 | 开发 | -| `-O2` | 完全优化(默认) | 生产 | -| `-O3` | 激进优化 | 最高性能 | -| `-O4` | 最激进 | 对大小/速度要求严格时 | +| `-O0` | 不做优化 | 仅调试 | +| `-O1` | 默认:全程序分析与内联 | 开发 / 默认 | +| `-O2` | 安全的、面向生产的优化(如压缩) | 生产 | +| `-O3` | 可能不安全的优化 | 最高性能 | +| `-O4` | 更激进的不安全优化 | 对大小/速度要求严格时 | ## Node.js 兼容性 @@ -57,10 +57,10 @@ void main() { } ``` -或使用我们的构建工具(推荐): +将其包装成一个小的构建脚本,并在 `dart compile js` 之后运行: ```bash -dart run tools/build/build.dart my_app +dart compile js web/app.dart -o build/app.js -O2 ``` ## 输出结构 @@ -182,7 +182,7 @@ Future main(List args) async { 4. **优先使用 `const`** - 常量值在编译时计算 -5. **分析输出** - 检查 `.js.info` 文件了解大小分布: +5. **分析输出** - 使用 `--dump-info` 的输出了解大小分布: ```bash dart compile js lib/main.dart -o build/main.js --dump-info diff --git a/website/src/zh/docs/getting-started.md b/website/src/zh/docs/getting-started.md index 4c331c1..d0cf204 100644 --- a/website/src/zh/docs/getting-started.md +++ b/website/src/zh/docs/getting-started.md @@ -41,8 +41,8 @@ environment: sdk: ^3.10.0 dependencies: - dart_node_core: ^0.11.0-beta - dart_node_express: ^0.11.0-beta + dart_node_core: ^0.13.0-beta + dart_node_express: ^0.13.0-beta ``` 然后运行: @@ -140,7 +140,7 @@ Dart 代码在运行时使用 JS 互操作来调用这些 npm 包。 ## 示例项目 -查看 [示例目录](https://github.com/melbournedeveloper/dart_node/tree/main/examples) 获取完整的工作应用程序: +查看 [示例目录](https://github.com/MelbourneDeveloper/dart_node/tree/main/examples) 获取完整的工作应用程序: - **backend/** - 带 REST API 的 Express 服务器 - **frontend/** - React Web 应用程序 diff --git a/website/src/zh/index.njk b/website/src/zh/index.njk index 417fe9e..bb7c902 100644 --- a/website/src/zh/index.njk +++ b/website/src/zh/index.njk @@ -212,7 +212,14 @@ ReactElement counter() {
S

dart_node_better_sqlite3

-

better-sqlite3 的类型化绑定,支持同步 SQLite3 和 WAL 模式。

+

better-sqlite3 的类型化绑定,支持同步原生 SQLite3 和 WAL 模式。

+
+ +
@@ -231,20 +238,21 @@ ReactElement counter() {
J

dart_jsx

Dart 的 JSX 转译器 — JSX 语法编译为 dart_node_react 调用。

+ 了解更多 →
V

dart_node_vsix

VSCode 扩展 API 绑定,使用 Dart 构建 Visual Studio Code 扩展。

- 了解更多 → + 在 GitHub 查看源码 →
T
-

too-many-cooks

-

多 AI 代理协调 MCP 服务器,用于多个代理同时编辑代码库。

- 了解更多 → +

Too Many Cooks

+

多 AI 代理协调 MCP 服务器,最初使用 dart_node_mcp 构建。它已迁移到独立站点,不再是本仓库的一部分。

+ tmc-mcp.dev →