Trending

#parser

Latest posts tagged with #parser on Bluesky

Latest Top
Trending

Posts tagged #parser

#parser: "I need a real life version of Spain,"… <pause>…

"BUTSPAINISREAL!!!"

🤭

1 0 0 0

#parser 🤦

1 0 0 0
Preview
Parse HTML into Markdown in Unison New version of `html-parse` library

An update to my html-parse library - a new handler to the existing #Unison ability to parses Html text into Markdown. Check the blog post for details. #parser #markdown

1 0 0 0
Original post on mjtsai.com

Apple’s .car File Format Ordinal0 (via Hacker News): In this post, I’ll walk through the process of reverse engineering the .car file format, explain its internal structures, and show how to pa...

#Technology #Asset #Catalog #(.car) #Mac #macOS #Tahoe #26 #Parser #WebAssembly

Origin | Interest […]

0 0 0 0

#parser: "las ganas en los hombres jóvenes" ;o

me mostraré la puerta xDDD

0 0 0 0
Preview
zig-systems-software/toon-parser at main · eddygarcas/zig-systems-software Systems programming utilities. Contribute to eddygarcas/zig-systems-software development by creating an account on GitHub.

Built on top of my previous JSON parser in Zig, I just implemented a TOON parser. Same core ideas, pushed a bit further to explore parsing patterns and structure in Zig. Super fun exercise in systems-level parsing.
Code’s here if you’re curious 👇
qunlot.short.gy/1mlYIL
#zig #toon #parser #ai

1 0 0 0
Preview
GitHub - eddygarcas/zig-systems-software: Systems programming utilities Systems programming utilities. Contribute to eddygarcas/zig-systems-software development by creating an account on GitHub.

I just finished a small JSON parser in Zig based on an example from Practical Zig Programming. The original was incomplete and not aligned with the current Zig version, so I rewrote and modernized it. Happy to share if anyone’s interested 👇
github.com/eddygarcas/z...
#ziglang #json #parser #zig

3 0 0 0
Preview
html-parse - HTML parser in Unison Parse HTML text into a structured representation

First building block towards creating an RSS reader on AT protocol - Unison HTML parser #unison #html #parser

7 2 0 1
Screen z aplikacji monitorującej zdrowie i kondycję.
Napisy w kolejnych wierszach:
"stan wytrenowania"
"Regeneracja"
"Od sty 16"
Poniżej nałożony napis w stylu memów:
"#PARSER: REZYGNACJA"

Screen z aplikacji monitorującej zdrowie i kondycję. Napisy w kolejnych wierszach: "stan wytrenowania" "Regeneracja" "Od sty 16" Poniżej nałożony napis w stylu memów: "#PARSER: REZYGNACJA"

poranny #parser od naszego sponsora

3 1 0 0
Post image

#parser: "fuck nudles" xD

alas: croquetas polacas - se prepara con el crepe <3

1 0 1 0

#parser
KREDKI ŚWIERSZCZOWE

2 0 0 0
Designing type-safe sync/async mode support in TypeScript I recently added sync/async mode support to Optique, a type-safe CLI parser for TypeScript. It turned out to be one of the trickier features I've implemented—the `object()` combinator alone needed to compute a combined mode from all its child parsers, and TypeScript's inference kept hitting edge cases. ## What is Optique? Optique is a type-safe, combinatorial CLI parser for TypeScript, inspired by Haskell's optparse-applicative. Instead of decorators or builder patterns, you compose small parsers into larger ones using combinators, and TypeScript infers the result types. Here's a quick taste: import { object } from "@optique/core/constructs"; import { argument, option } from "@optique/core/primitives"; import { string, integer } from "@optique/core/valueparser"; import { run } from "@optique/run"; const cli = object({ name: argument(string()), count: option("-n", "--count", integer()), }); // TypeScript infers: { name: string; count: number | undefined } const result = run(cli); // sync by default The type inference works through arbitrarily deep compositions—in most cases, you don't need explicit type annotations. ## How it started Lucas Garron (@lgarron) opened an issue requesting async support for shell completions. He wanted to provide `Tab`-completion suggestions by running shell commands like `git for-each-ref` to list branches and tags. // Lucas's example: fetching Git branches and tags in parallel const [branches, tags] = await Promise.all([ $`git for-each-ref --format='%(refname:short)' refs/heads/`.text(), $`git for-each-ref --format='%(refname:short)' refs/tags/`.text(), ]); At first, I didn't like the idea. Optique's entire API was synchronous, which made it simpler to reason about and avoided the “async infection” problem where one async function forces everything upstream to become async. I argued that shell completion should be near-instantaneous, and if you need async data, you should cache it at startup. But Lucas pushed back. The filesystem _is_ a database, and many useful completions inherently require async work—Git refs change constantly, and pre-caching everything at startup doesn't scale for large repos. Fair point. ## What I needed to solve So, how do you support both sync and async execution modes in a composable parser library while maintaining type safety? The key requirements were: * `parse()` returns `T` or `Promise<T>` * `complete()` returns `T` or `Promise<T>` * `suggest()` returns `Iterable<T>` or `AsyncIterable<T>` * When combining parsers, if _any_ parser is async, the combined result must be async * Existing sync code should continue to work unchanged The fourth requirement is the tricky one. Consider this: const syncParser = flag("--verbose"); const asyncParser = option("--branch", asyncValueParser); // What's the type of this? const combined = object({ verbose: syncParser, branch: asyncParser }); The combined parser should be async because one of its fields is async. This means we need type-level logic to compute the combined mode. ## Five design options I explored five different approaches, each with its own trade-offs. ### Option A: conditional types with mode parameter Add a mode type parameter to `Parser` and use conditional types: type Mode = "sync" | "async"; type ModeValue<M extends Mode, T> = M extends "async" ? Promise<T> : T; interface Parser<M extends Mode, TValue, TState> { parse(context: ParserContext<TState>): ModeValue<M, ParserResult<TState>>; // ... } The challenge is computing combined modes: type CombineModes<T extends Record<string, Parser<any, any, any>>> = T[keyof T] extends Parser<infer M, any, any> ? M extends "async" ? "async" : "sync" : never; ### Option B: mode parameter with default value A variant of Option A, but place the mode parameter first with a default of `"sync"`: interface Parser<M extends Mode = "sync", TValue, TState> { readonly $mode: M; // ... } The default value maintains backward compatibility—existing user code keeps working without changes. ### Option C: separate interfaces Define completely separate `Parser` and `AsyncParser` interfaces with explicit conversion: interface Parser<TValue, TState> { /* sync methods */ } interface AsyncParser<TValue, TState> { /* async methods */ } function toAsync<T, S>(parser: Parser<T, S>): AsyncParser<T, S>; Simpler to understand, but requires code duplication and explicit conversions. ### Option D: union return types for suggest() only The minimal approach. Only allow `suggest()` to be async: interface Parser<TValue, TState> { parse(context: ParserContext<TState>): ParserResult<TState>; // always sync suggest(context: ParserContext<TState>, prefix: string): Iterable<Suggestion> | AsyncIterable<Suggestion>; // can be either } This addresses the original use case but doesn't help if async `parse()` is ever needed. ### Option E: fp-ts style HKT simulation Use the technique from fp-ts to simulate Higher-Kinded Types: interface URItoKind<A> { Identity: A; Promise: Promise<A>; } type Kind<F extends keyof URItoKind<any>, A> = URItoKind<A>[F]; interface Parser<F extends keyof URItoKind<any>, TValue, TState> { parse(context: ParserContext<TState>): Kind<F, ParserResult<TState>>; } The most flexible approach, but with a steep learning curve. ## Testing the idea Rather than commit to an approach based on theoretical analysis, I created a prototype to test how well TypeScript handles the type inference in practice. I published my findings in the GitHub issue: > Both approaches correctly handle the “any async → all async” rule at the type level. (…) Complex conditional types like `ModeValue<CombineParserModes<T>, ParserResult<TState>>` sometimes require explicit type casting in the implementation. This only affects library internals. The user-facing API remains clean. The prototype validated that Option B (explicit mode parameter with default) would work. I chose it for these reasons: * _Backward compatible_ : The default `"sync"` keeps existing code working * _Explicit_ : The mode is visible in both types and runtime (via a `$mode` property) * _Debuggable_ : Easy to inspect the current mode at runtime * _Better IDE support_ : Type information is more predictable ## How `CombineModes` works The `CombineModes` type computes whether a combined parser should be sync or async: type CombineModes<T extends readonly Mode[]> = "async" extends T[number] ? "async" : "sync"; This type checks if `"async"` is present anywhere in the tuple of modes. If so, the result is `"async"`; otherwise, it's `"sync"`. For combinators like `object()`, I needed to extract modes from parser objects and combine them: // Extract the mode from a single parser type ParserMode<T> = T extends Parser<infer M, unknown, unknown> ? M : never; // Combine modes from all values in a record of parsers type CombineObjectModes<T extends Record<string, Parser<Mode, unknown, unknown>>> = CombineModes<{ [K in keyof T]: ParserMode<T[K]> }[keyof T][]>; ## Runtime implementation The type system handles compile-time safety, but the implementation also needs runtime logic. Each parser has a `$mode` property that indicates its execution mode: const syncParser = option("-n", "--name", string()); console.log(syncParser.$mode); // "sync" const asyncParser = option("-b", "--branch", asyncValueParser); console.log(asyncParser.$mode); // "async" Combinators compute their mode at construction time: function object<T extends Record<string, Parser<Mode, unknown, unknown>>>( parsers: T ): Parser<CombineObjectModes<T>, ObjectValue<T>, ObjectState<T>> { const parserKeys = Reflect.ownKeys(parsers); const combinedMode: Mode = parserKeys.some( (k) => parsers[k as keyof T].$mode === "async" ) ? "async" : "sync"; // ... implementation } ## Refining the API Lucas suggested an important refinement during our discussion. Instead of having `run()` automatically choose between sync and async based on the parser mode, he proposed separate functions: > Perhaps `run(…)` could be automatic, and `runSync(…)` and `runAsync(…)` could enforce that the inferred type matches what is expected. So we ended up with: * `run()`: automatic based on parser mode * `runSync()`: enforces sync mode at compile time * `runAsync()`: enforces async mode at compile time // Automatic: returns T for sync parsers, Promise<T> for async const result1 = run(syncParser); // string const result2 = run(asyncParser); // Promise<string> // Explicit: compile-time enforcement const result3 = runSync(syncParser); // string const result4 = runAsync(asyncParser); // Promise<string> // Compile error: can't use runSync with async parser const result5 = runSync(asyncParser); // Type error! I applied the same pattern to `parse()`/`parseSync()`/`parseAsync()` and `suggest()`/`suggestSync()`/`suggestAsync()` in the facade functions. ## Creating async value parsers With the new API, creating an async value parser for Git branches looks like this: import type { Suggestion } from "@optique/core/parser"; import type { ValueParser, ValueParserResult } from "@optique/core/valueparser"; function gitRef(): ValueParser<"async", string> { return { $mode: "async", metavar: "REF", parse(input: string): Promise<ValueParserResult<string>> { return Promise.resolve({ success: true, value: input }); }, format(value: string): string { return value; }, async *suggest(prefix: string): AsyncIterable<Suggestion> { const { $ } = await import("bun"); const [branches, tags] = await Promise.all([ $`git for-each-ref --format='%(refname:short)' refs/heads/`.text(), $`git for-each-ref --format='%(refname:short)' refs/tags/`.text(), ]); for (const ref of [...branches.split("\n"), ...tags.split("\n")]) { const trimmed = ref.trim(); if (trimmed && trimmed.startsWith(prefix)) { yield { kind: "literal", text: trimmed }; } } }, }; } Notice that `parse()` returns `Promise.resolve()` even though it's synchronous. This is because the `ValueParser<"async", T>` type requires all methods to use async signatures. Lucas pointed out this is a minor ergonomic issue. If only `suggest()` needs to be async, you still have to wrap `parse()` in a Promise. I considered per-method mode granularity (e.g., `ValueParser<ParseMode, SuggestMode, T>`), but the implementation complexity would multiply substantially. For now, the workaround is simple enough: // Option 1: Use Promise.resolve() parse(input) { return Promise.resolve({ success: true, value: input }); } // Option 2: Mark as async and suppress the linter // biome-ignore lint/suspicious/useAwait: sync implementation in async ValueParser async parse(input) { return { success: true, value: input }; } ## What it cost Supporting dual modes added significant complexity to Optique's internals. Every combinator needed updates: * Type signatures grew more complex with mode parameters * Mode propagation logic had to be added to every combinator * Dual implementations were needed for sync and async code paths * Type casts were sometimes necessary in the implementation to satisfy TypeScript For example, the `object()` combinator went from around 100 lines to around 250 lines. The internal implementation uses conditional logic based on the combined mode: if (combinedMode === "async") { return { $mode: "async" as M, // ... async implementation with Promise chains async parse(context) { // ... await each field's parse result }, }; } else { return { $mode: "sync" as M, // ... sync implementation parse(context) { // ... directly call each field's parse }, }; } This duplication is the cost of supporting both modes without runtime overhead for sync-only use cases. ## Lessons learned ### Listen to users, but validate with prototypes My initial instinct was to resist async support. Lucas's persistence and concrete examples changed my mind, but I validated the approach with a prototype before committing. The prototype revealed practical issues (like TypeScript inference limits) that pure design analysis would have missed. ### Backward compatibility is worth the complexity Making `"sync"` the default mode meant existing code continued to work unchanged. This was a deliberate choice. Breaking changes should require user action, not break silently. ### Unified mode vs per-method granularity I chose unified mode (all methods share the same sync/async mode) over per-method granularity. This means users occasionally write `Promise.resolve()` for methods that don't actually need async, but the alternative was multiplicative complexity in the type system. ### Designing in public The entire design process happened in a public GitHub issue. Lucas, Giuseppe, and others contributed ideas that shaped the final API. The `runSync()`/`runAsync()` distinction came directly from Lucas's feedback. ## Conclusion This was one of the more challenging features I've implemented in Optique. TypeScript's type system is powerful enough to encode the “any async means all async” rule at compile time, but getting there required careful design work and prototyping. What made it work: conditional types like `ModeValue<M, T>` can bridge the gap between sync and async worlds. You pay for it with implementation complexity, but the user-facing API stays clean and type-safe. Optique 0.9.0 with async support is currently in pre-release testing. If you'd like to try it, check out PR #70 or install the pre-release: npm add @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212 deno add --jsr @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212 Feedback is welcome!
1 1 0 0
Stop writing if statements for your CLI flags If you've built CLI tools, you've written code like this: if (opts.reporter === "junit" && !opts.outputFile) { throw new Error("--output-file is required for junit reporter"); } if (opts.reporter === "html" && !opts.outputFile) { throw new Error("--output-file is required for html reporter"); } if (opts.reporter === "console" && opts.outputFile) { console.warn("--output-file is ignored for console reporter"); } A few months ago, I wrote _Stop writing CLI validation. Parse it right the first time._ about parsing individual option values correctly. But it didn't cover the _relationships_ between options. In the code above, `--output-file` only makes sense when `--reporter` is `junit` or `html`. When it's `console`, the option shouldn't exist at all. We're using TypeScript. We have a powerful type system. And yet, here we are, writing runtime checks that the compiler can't help with. Every time we add a new reporter type, we need to remember to update these checks. Every time we refactor, we hope we didn't miss one. ## The state of TypeScript CLI parsers The old guard—Commander, yargs, minimist—were built before TypeScript became mainstream. They give you bags of strings and leave type safety as an exercise for the reader. But we've made progress. Modern TypeScript-first libraries like cmd-ts and Clipanion (the library powering Yarn Berry) take types seriously: // cmd-ts const app = command({ args: { reporter: option({ type: string, long: 'reporter' }), outputFile: option({ type: string, long: 'output-file' }), }, handler: (args) => { // args.reporter: string // args.outputFile: string }, }); // Clipanion class TestCommand extends Command { reporter = Option.String('--reporter'); outputFile = Option.String('--output-file'); } These libraries infer types for individual options. `--port` is a `number`. `--verbose` is a `boolean`. That's real progress. But here's what they can't do: express that `--output-file` is required _when_ `--reporter` is `junit`, and forbidden _when_ `--reporter` is `console`. The relationship between options isn't captured in the type system. So you end up writing validation code anyway: handler: (args) => { // Both cmd-ts and Clipanion need this if (args.reporter === "junit" && !args.outputFile) { throw new Error("--output-file required for junit"); } // args.outputFile is still string | undefined // TypeScript doesn't know it's definitely string when reporter is "junit" } Rust's clap and Python's Click have `requires` and `conflicts_with` attributes, but those are runtime checks too. They don't change the result type. If the parser configuration knows about option relationships, why doesn't that knowledge show up in the result type? ## Modeling relationships with `conditional()` Optique treats option relationships as a first-class concept. Here's the test reporter scenario: import { conditional, object } from "@optique/core/constructs"; import { option } from "@optique/core/primitives"; import { choice, string } from "@optique/core/valueparser"; import { run } from "@optique/run"; const parser = conditional( option("--reporter", choice(["console", "junit", "html"])), { console: object({}), junit: object({ outputFile: option("--output-file", string()), }), html: object({ outputFile: option("--output-file", string()), openBrowser: option("--open-browser"), }), } ); const [reporter, config] = run(parser); The `conditional()` combinator takes a discriminator option (`--reporter`) and a map of branches. Each branch defines what other options are valid for that discriminator value. TypeScript infers the result type automatically: type Result = | ["console", {}] | ["junit", { outputFile: string }] | ["html", { outputFile: string; openBrowser: boolean }]; When `reporter` is `"junit"`, `outputFile` is `string`—not `string | undefined`. The relationship is encoded in the type. Now your business logic gets real type safety: const [reporter, config] = run(parser); switch (reporter) { case "console": runWithConsoleOutput(); break; case "junit": // TypeScript knows config.outputFile is string writeJUnitReport(config.outputFile); break; case "html": // TypeScript knows config.outputFile and config.openBrowser exist writeHtmlReport(config.outputFile); if (config.openBrowser) openInBrowser(config.outputFile); break; } No validation code. No runtime checks. If you add a new reporter type and forget to handle it in the switch, the compiler tells you. ## A more complex example: database connections Test reporters are a nice example, but let's try something with more variation. Database connection strings: myapp --db=sqlite --file=./data.db myapp --db=postgres --host=localhost --port=5432 --user=admin myapp --db=mysql --host=localhost --port=3306 --user=root --ssl Each database type needs completely different options: * SQLite just needs a file path * PostgreSQL needs host, port, user, and optionally password * MySQL needs host, port, user, and has an SSL flag Here's how you model this: import { conditional, object } from "@optique/core/constructs"; import { withDefault, optional } from "@optique/core/modifiers"; import { option } from "@optique/core/primitives"; import { choice, string, integer } from "@optique/core/valueparser"; const dbParser = conditional( option("--db", choice(["sqlite", "postgres", "mysql"])), { sqlite: object({ file: option("--file", string()), }), postgres: object({ host: option("--host", string()), port: withDefault(option("--port", integer()), 5432), user: option("--user", string()), password: optional(option("--password", string())), }), mysql: object({ host: option("--host", string()), port: withDefault(option("--port", integer()), 3306), user: option("--user", string()), ssl: option("--ssl"), }), } ); The inferred type: type DbConfig = | ["sqlite", { file: string }] | ["postgres", { host: string; port: number; user: string; password?: string }] | ["mysql", { host: string; port: number; user: string; ssl: boolean }]; Notice the details: PostgreSQL defaults to port 5432, MySQL to 3306. PostgreSQL has an optional password, MySQL has an SSL flag. Each database type has exactly the options it needs—no more, no less. With this structure, writing `dbConfig.ssl` when the mode is `sqlite` isn't a runtime error—it's a compile-time impossibility. Try expressing this with `requires_if` attributes. You can't. The relationships are too rich. ## The pattern is everywhere Once you see it, you find this pattern in many CLI tools: **Authentication modes:** const authParser = conditional( option("--auth", choice(["none", "basic", "token", "oauth"])), { none: object({}), basic: object({ username: option("--username", string()), password: option("--password", string()), }), token: object({ token: option("--token", string()), }), oauth: object({ clientId: option("--client-id", string()), clientSecret: option("--client-secret", string()), tokenUrl: option("--token-url", url()), }), } ); **Deployment targets** , **output formats** , **connection protocols** —anywhere you have a mode selector that determines what other options are valid. ## Why `conditional()` exists Optique already has an `or()` combinator for mutually exclusive alternatives. Why do we need `conditional()`? The `or()` combinator distinguishes branches based on **structure** —which options are present. It works well for subcommands like `git commit` vs `git push`, where the arguments differ completely. But in the reporter example, the structure is identical: every branch has a `--reporter` flag. The difference lies in the flag's _value_ , not its presence. // This won't work as intended const parser = or( object({ reporter: option("--reporter", choice(["console"])) }), object({ reporter: option("--reporter", choice(["junit", "html"])), outputFile: option("--output-file", string()) }), ); When you pass `--reporter junit`, `or()` tries to pick a branch based on what options are present. Both branches have `--reporter`, so it can't distinguish them structurally. `conditional()` solves this by reading the discriminator's value first, then selecting the appropriate branch. It bridges the gap between structural parsing and value-based decisions. ## _The structure is the constraint_ Instead of parsing options into a loose type and then validating relationships, define a parser whose structure _is_ the constraint. Traditional approach | Optique approach ---|--- Parse → Validate → Use | Parse (with constraints) → Use Types and validation logic maintained separately | Types reflect the constraints Mismatches found at runtime | Mismatches found at compile time The parser definition becomes the single source of truth. Add a new reporter type? The parser definition changes, the inferred type changes, and the compiler shows you everywhere that needs updating. ## Try it If this resonates with a CLI you're building: * Documentation * Tutorial * `conditional()` reference * GitHub Next time you're about to write an `if` statement checking option relationships, ask: could the parser express this constraint instead? _The structure of your parser is the constraint._ You might not need that validation code at all.
1 1 0 1

wice psów #parser

2 0 1 0

czy pragniesz zespolenia
czy doła czasem masz
czy kumasz Kęstowicza
czy byłaś wewnątrz psa
#parser

2 0 2 0
Optique 0.7.0: Smarter error messages and validation library integrations We're thrilled to announce Optique 0.7.0, a release focused on developer experience improvements and expanding Optique's ecosystem with validation library integrations. Optique is a type-safe, combinatorial CLI argument parser for TypeScript. Unlike traditional CLI libraries that rely on configuration objects, Optique lets you compose parsers from small, reusable functions—bringing the same functional composition patterns that make Zod powerful to CLI development. If you're new to Optique, check out _Why Optique?_ to learn how this approach unlocks possibilities that configuration-based libraries simply can't match. This release introduces automatic “Did you mean?” suggestions for typos, seamless integration with Zod and Valibot validation libraries, duplicate option name detection for catching configuration bugs early, and context-aware error messages that help users understand exactly what went wrong. ## “Did you mean?”: Automatic typo suggestions We've all been there: you type `--verbos` instead of `--verbose`, and the CLI responds with an unhelpful “unknown option” error. Optique 0.7.0 changes this by automatically suggesting similar options when users make typos: const parser = object({ verbose: option("-v", "--verbose"), version: option("--version"), }); // User types: --verbos (typo) const result = parse(parser, ["--verbos"]); // Error: Unexpected option or argument: --verbos. // // Did you mean one of these? // --verbose // --version The suggestion system uses Levenshtein distance to find similar names, suggesting up to 3 alternatives when the edit distance is within a reasonable threshold. Suggestions work automatically for both option names and subcommand names across all parser types—`option()`, `flag()`, `command()`, `object()`, `or()`, and `longestMatch()`. See the automatic suggestions documentation for more details. ### Customizing suggestions You can customize how suggestions are formatted or disable them entirely through the `errors` option: // Custom suggestion format for option/flag parsers const portOption = option("--port", integer(), { errors: { noMatch: (invalidOption, suggestions) => suggestions.length > 0 ? message`Unknown option ${invalidOption}. Try: ${values(suggestions)}` : message`Unknown option ${invalidOption}.` } }); // Custom suggestion format for combinators const config = object({ host: option("--host", string()), port: option("--port", integer()) }, { errors: { suggestions: (suggestions) => suggestions.length > 0 ? message`Available options: ${values(suggestions)}` : [] } }); ## Zod and Valibot integrations Two new packages join the Optique family, bringing powerful validation capabilities from the TypeScript ecosystem to your CLI parsers. ### @optique/zod The new `@optique/zod` package lets you use Zod schemas directly as value parsers: import { option, object } from "@optique/core"; import { zod } from "@optique/zod"; import { z } from "zod"; const parser = object({ email: option("--email", zod(z.string().email())), port: option("--port", zod(z.coerce.number().int().min(1).max(65535))), format: option("--format", zod(z.enum(["json", "yaml", "xml"]))), }); The package supports both Zod v3.25.0+ and v4.0.0+, with automatic error formatting that integrates seamlessly with Optique's message system. See the Zod integration guide for complete usage examples. ### @optique/valibot For those who prefer a lighter bundle, `@optique/valibot` integrates with Valibot—a validation library with a significantly smaller footprint (~10KB vs Zod's ~52KB): import { option, object } from "@optique/core"; import { valibot } from "@optique/valibot"; import * as v from "valibot"; const parser = object({ email: option("--email", valibot(v.pipe(v.string(), v.email()))), port: option("--port", valibot(v.pipe( v.string(), v.transform(Number), v.integer(), v.minValue(1), v.maxValue(65535) ))), }); Both packages support custom error messages through their respective error handler options (`zodError` and `valibotError`), giving you full control over how validation failures are presented to users. See the Valibot integration guide for complete usage examples. ## Duplicate option name detection A common source of bugs in CLI applications is accidentally using the same option name in multiple places. Previously, this would silently cause ambiguous parsing where the first matching parser consumed the option. Optique 0.7.0 now validates option names at parse time and fails with a clear error message when duplicates are detected: const parser = object({ input: option("-i", "--input", string()), interactive: option("-i", "--interactive"), // Oops! -i is already used }); // Error: Duplicate option name -i found in fields: input, interactive. // Each option name must be unique within a parser combinator. This validation applies to `object()`, `tuple()`, `merge()`, and `group()` combinators. The `or()` combinator continues to allow duplicate option names since its branches are mutually exclusive. See the duplicate detection documentation for more details. If you have a legitimate use case for duplicate option names, you can opt out with `allowDuplicates: true`: const parser = object({ input: option("-i", "--input", string()), interactive: option("-i", "--interactive"), }, { allowDuplicates: true }); ## Context-aware error messages Error messages from combinators are now smarter about what they report. Instead of generic "No matching option or command found" messages, Optique now analyzes what the parser expects and provides specific feedback: // When only arguments are expected const parser1 = or(argument(string()), argument(integer())); // Error: Missing required argument. // When only commands are expected const parser2 = or(command("add", addParser), command("remove", removeParser)); // Error: No matching command found. // When both options and arguments are expected const parser3 = object({ port: option("--port", integer()), file: argument(string()), }); // Error: No matching option or argument found. ### Dynamic error messages with `NoMatchContext` For applications that need internationalization or context-specific messaging, the `errors.noMatch` option now accepts a function that receives a `NoMatchContext` object: const parser = or( command("add", addParser), command("remove", removeParser), { errors: { noMatch: ({ hasOptions, hasCommands, hasArguments }) => { if (hasCommands && !hasOptions && !hasArguments) { return message`일치하는 명령을 찾을 수 없습니다.`; // Korean } return message`잘못된 입력입니다.`; } } } ); ## Shell completion naming conventions The `run()` function now supports configuring whether shell completions use singular or plural naming conventions: run(parser, { completion: { name: "plural", // Uses "completions" and "--completions" } }); // Or for singular only run(parser, { completion: { name: "singular", // Uses "completion" and "--completion" } }); The default `"both"` accepts either form, maintaining backward compatibility while letting you enforce a consistent style in your CLI. ## Additional improvements * **Line break handling** : `formatMessage()` now distinguishes between soft breaks (single `\n`, converted to spaces) and hard breaks (double `\n\n`, creating paragraph separations), improving multi-line error message formatting. * **New utility functions** : Added `extractOptionNames()` and `extractArgumentMetavars()` to the `@optique/core/usage` module for programmatic access to parser metadata. ## Installation deno add --jsr @optique/core @optique/run npm add @optique/core @optique/run pnpm add @optique/core @optique/run yarn add @optique/core @optique/run bun add @optique/core @optique/run For validation library integrations: # Zod integration deno add jsr:@optique/zod # Deno npm add @optique/zod # npm/pnpm/yarn/bun # Valibot integration deno add jsr:@optique/valibot # Deno npm add @optique/valibot # npm/pnpm/yarn/bun ## Looking forward This release represents our commitment to making CLI development in TypeScript as smooth as possible. The “Did you mean?” suggestions and validation library integrations were among the most requested features, and we're excited to see how they improve your CLI applications. For detailed documentation and examples, visit the Optique documentation. We welcome your feedback and contributions on GitHub!
2 6 0 1
Post image Post image

major milestone achieved today! SEN's first native companion app, the #SENity #editor, now comes with a new #parser (tree-sitter powered) and supports SEN #native links (that never break! fall back to paths if SEN is not running) and file type icons! The outline panel is a first draft, more to come!

1 0 0 0

MohammadRaziei/pygixml: a python wrapper over pugixml #dev #python #xml #parser #pygixml

1 0 0 0

GitHub - rushter/selectolax: Python binding to Modest and Lexbor engines. Fast HTML5 parser with CSS selectors for Python. #dev #python #html #parser #selectolax

1 0 0 0
Syntax highlighted TypeScript sourcecode of the linked code example...

Syntax highlighted TypeScript sourcecode of the linked code example...

Tags are sets. Many apps support tagging of content, but most of them (incl. Mastodon) treat tags only as singular/isolated topic filters, akin to a flat folder-based approach. But tagging can be so, so much more powerful when treating tags as sets and […]

[Original post on mastodon.thi.ng]

1 0 0 0

ałmaty aj matulu hospody pomyłuj #parser

1 0 0 0
URL parser tester

bsky URL parsing #TEST
#parse #parsing #parser

timothygu.me/urltester/#i...

0 0 0 0
List of Internet top-level domains - Wikipedia

bsky URL parsing #TEST
#parse #parsing #parser

en.wikipedia.org/wiki/List_of...

0 0 0 0
Example Domain

bsky URL parsing #TEST
#parse #parsing #parser

example.com
https://example<.>com
example”.”com > xn--example-b46c.xn--com-6o0a
https://example%.%com
example#.#com
example{.}com
https://example|.|com
example\.\com > example/com
https://example^.^com
example~.~com
https://example[.]com
example`.`com

0 0 0 0
Post image

#parser: "Ass Banging" xD

I need coffee or sleep xD

2 1 0 0
Original post on mastodon.uno

🔍 / #software / #CSV / #parser/ #performance

I used #ChatGPT to help with technical definitions and finding book references. If anything seems wrong or you spot any AI hallucinations, let me know and I'll fix it. The actual GitHub project and parser code came straight from my discoveries and […]

1 2 0 0

śmietniku #parser

1 0 0 0
Original post on postgresql.fastware.com

Beyond the prompt: How PostgreSQL 18 turns your code into meaning We stand on the cusp of a new iteration of the world’s most advanced open-source relational database. With PostgreSQL 18 slated f...

#PostgreSQL #PostgreSQL #development #Open #source […]

[Original post on postgresql.fastware.com]

0 0 0 0
Optique 0.5.0: Enhanced error handling and message customization We're pleased to announce the release of Optique 0.5.0, which brings significant improvements to error handling, help text generation, and overall developer experience. This release maintains full backward compatibility, so you can upgrade without modifying existing code. ## Better code organization through module separation The large `@optique/core/parser` module has been refactored into three focused modules that better reflect their purposes. Primitive parsers like `option()` and `argument()` now live in `@optique/core/primitives`, modifier functions such as `optional()` and `withDefault()` have moved to `@optique/core/modifiers`, and combinator functions including `object()` and `or()` are now in `@optique/core/constructs`. // Before: everything from one module import { option, flag, argument, // primitives optional, withDefault, multiple, // modifiers object, or, merge // constructs } from "@optique/core/parser"; // After: organized imports (recommended) import { option, flag, argument } from "@optique/core/primitives"; import { optional, withDefault, multiple } from "@optique/core/modifiers"; import { object, or, merge } from "@optique/core/constructs"; While we recommend importing from these specialized modules for better clarity, all functions continue to be re-exported from the original `@optique/core/parser` module to ensure your existing code works unchanged. This reorganization makes the codebase more maintainable and helps developers understand the relationships between different parser types. ## Smarter error handling with automatic conversion One of the most requested features has been better error handling for default value callbacks in `withDefault()`. Previously, if your callback threw an error—say, when an environment variable wasn't set—that error would bubble up as a runtime exception. Starting with 0.5.0, these errors are automatically caught and converted to parser-level errors, providing consistent error formatting and proper exit codes. // Before (0.4.x): runtime exception that crashes the app const parser = object({ apiUrl: withDefault(option("--url", url()), () => { if (!process.env.API_URL) { throw new Error("API_URL not set"); // Uncaught exception! } return new URL(process.env.API_URL); }) }); // After (0.5.0): graceful parser error const parser = object({ apiUrl: withDefault(option("--url", url()), () => { if (!process.env.API_URL) { throw new Error("API_URL not set"); // Automatically caught and formatted } return new URL(process.env.API_URL); }) }); We've also introduced the `WithDefaultError` class, which accepts structured messages instead of plain strings. This means you can now throw errors with rich formatting that matches the rest of Optique's error output: import { WithDefaultError, message, envVar } from "@optique/core"; const parser = object({ // Plain error - automatically converted to text databaseUrl: withDefault(option("--db", url()), () => { if (!process.env.DATABASE_URL) { throw new Error("Database URL not configured"); } return new URL(process.env.DATABASE_URL); }), // Rich error with structured message apiToken: withDefault(option("--token", string()), () => { if (!process.env.API_TOKEN) { throw new WithDefaultError( message`Environment variable ${envVar("API_TOKEN")} is required for authentication` ); } return process.env.API_TOKEN; }) }); The new `envVar` message component ensures environment variables are visually distinct in error messages, appearing bold and underlined in colored output or wrapped in backticks in plain text. ## More helpful help text with custom default descriptions Default values in help text can sometimes be misleading, especially when they come from environment variables or are computed at runtime. Optique 0.5.0 allows you to customize how default values appear in help output through an optional third parameter to `withDefault()`. import { withDefault, message, envVar } from "@optique/core"; const parser = object({ // Before: shows actual URL value in help apiUrl: withDefault( option("--api-url", url()), new URL("https://api.example.com") ), // Help shows: --api-url URL [https://api.example.com] // After: shows descriptive text apiUrl: withDefault( option("--api-url", url()), new URL("https://api.example.com"), { message: message`Default API endpoint` } ), // Help shows: --api-url URL [Default API endpoint] }); This is particularly useful for environment variables and computed defaults: const parser = object({ // Environment variable authToken: withDefault( option("--token", string()), () => process.env.AUTH_TOKEN || "anonymous", { message: message`${envVar("AUTH_TOKEN")} or anonymous` } ), // Help shows: --token STRING [AUTH_TOKEN or anonymous] // Computed value workers: withDefault( option("--workers", integer()), () => os.cpus().length, { message: message`Number of CPU cores` } ), // Help shows: --workers INT [Number of CPU cores] // Sensitive information apiKey: withDefault( option("--api-key", string()), () => process.env.SECRET_KEY || "", { message: message`From secure storage` } ), // Help shows: --api-key STRING [From secure storage] }); Instead of displaying the actual default value, you can now show descriptive text that better explains where the value comes from. This is particularly useful for sensitive information like API tokens or for computed defaults like the number of CPU cores. The help system now properly handles ANSI color codes in default value displays, maintaining dim styling even when inner components have their own color formatting. This ensures default values remain visually distinct from the main help text. ## Comprehensive error message customization We've added a systematic way to customize error messages across all parser types and combinators. Every parser now accepts an `errors` option that lets you provide context-specific feedback instead of generic error messages. This applies to primitive parsers, value parsers, combinators, and even specialized parsers in companion packages. ### Primitive parser errors import { option, flag, argument, command } from "@optique/core/primitives"; import { message, optionName, metavar } from "@optique/core/message"; // Option parser with custom errors const serverPort = option("--port", integer(), { errors: { missing: message`Server port is required. Use ${optionName("--port")} to specify.`, invalidValue: (error) => message`Invalid port number: ${error}`, endOfInput: message`${optionName("--port")} requires a ${metavar("PORT")} number.` } }); // Command parser with custom errors const deployCommand = command("deploy", deployParser, { errors: { notMatched: (expected, actual) => message`Unknown command "${actual}". Did you mean "${expected}"?` } }); ### Value parser errors Error customization can be static messages for consistent errors or dynamic functions that incorporate the problematic input: import { integer, choice, string } from "@optique/core/valueparser"; // Integer with range validation const port = integer({ min: 1024, max: 65535, errors: { invalidInteger: message`Port must be a valid number.`, belowMinimum: (value, min) => message`Port ${String(value)} is reserved. Use ${String(min)} or higher.`, aboveMaximum: (value, max) => message`Port ${String(value)} exceeds maximum. Use ${String(max)} or lower.` } }); // Choice with helpful suggestions const logLevel = choice(["debug", "info", "warn", "error"], { errors: { invalidChoice: (input, choices) => message`"${input}" is not a valid log level. Choose from: ${values(choices)}.` } }); // String with pattern validation const email = string({ pattern: /^[^@]+@[^@]+\.[^@]+$/, errors: { patternMismatch: (input) => message`"${input}" is not a valid email address. Use format: user@example.com` } }); ### Combinator errors import { or, multiple, object } from "@optique/core/constructs"; // Or combinator with custom no-match error const format = or( flag("--json"), flag("--yaml"), flag("--xml"), { errors: { noMatch: message`Please specify an output format: --json, --yaml, or --xml.`, unexpectedInput: (token) => message`Unknown format option "${token}".` } } ); // Multiple parser with count validation const inputFiles = multiple(argument(string()), { min: 1, max: 5, errors: { tooFew: (count, min) => message`At least ${String(min)} file required, but got ${String(count)}.`, tooMany: (count, max) => message`Maximum ${String(max)} files allowed, but got ${String(count)}.` } }); ### Package-specific errors Both `@optique/run` and `@optique/temporal` packages have been updated with error customization support for their specialized parsers: // @optique/run path parser import { path } from "@optique/run/valueparser"; const configFile = option("--config", path({ mustExist: true, type: "file", extensions: [".json", ".yaml"], errors: { pathNotFound: (input) => message`Configuration file "${input}" not found. Please check the path.`, notAFile: (input) => message`"${input}" is a directory. Please specify a file.`, invalidExtension: (input, extensions, actual) => message`Invalid config format "${actual}". Use ${values(extensions)}.` } })); // @optique/temporal instant parser import { instant, duration } from "@optique/temporal"; const timestamp = option("--time", instant({ errors: { invalidFormat: (input) => message`"${input}" is not a valid timestamp. Use ISO 8601 format: 2024-01-01T12:00:00Z` } })); const timeout = option("--timeout", duration({ errors: { invalidFormat: (input) => message`"${input}" is not a valid duration. Use ISO 8601 format: PT30S (30 seconds), PT5M (5 minutes)` } })); Error customization integrates seamlessly with Optique's structured message format, ensuring consistent styling across all error output. The system helps you provide helpful, actionable feedback that guides users toward correct usage rather than leaving them confused by generic error messages. ## Looking forward This release focuses on improving the developer experience without breaking existing code. Every new feature is opt-in, and all changes maintain backward compatibility. We believe these improvements make Optique more pleasant to work with, especially when building user-friendly CLI applications that need clear error messages and helpful documentation. We're grateful to the community members who suggested these improvements and helped shape this release through discussions and issue reports. Your feedback continues to drive Optique's evolution toward being a more capable and ergonomic CLI parser for TypeScript. To upgrade to Optique 0.5.0, simply update your dependencies: npm update @optique/core @optique/run # or deno update For detailed migration guidance and API documentation, please refer to the official documentation. While no code changes are required, we encourage you to explore the new error customization options and help text improvements to enhance your CLI applications.
1 5 0 1