Purpose: Provide just enough project context so an AI assistant can make correct, idiomatic edits fast. Keep responses concrete, reflect existing patterns, and avoid generic advice.
- Library type: Pure Dart package (query string codec) ported from Node.js
qswith near‑parity semantics. - Public surface (
lib/qs_dart.dart) re‑exports:- Top‑level
decode/encode(wrappers aroundQS.decode/QS.encode). - Option models:
DecodeOptions,EncodeOptions, enums (Duplicates,ListFormat,Format,Sentinel,DecodeKind). Undefinedsentinel (marks intentionally omitted values during encoding/merging).Uriextensions (src/uri.dart) for ergonomic integration.
- Top‑level
- Implementation split:
src/qs.dartorchestrates encode/decode; heavy lifting delegated to privatepartfilesextensions/decode.dart&extensions/encode.dartand utility helpers inutils.dart.utils.dartcentralizes low‑level merging (Utils.merge), compaction (Utils.compact), scalar detection, percent encoding, HTML entity handling.- Option classes encode guard‑rails: depth, parameterLimit, listLimit, charsetSentinel, etc.
- Design principle: mirror Node
qsbehavior while remaining Dart‑idiomatic (orderedMap<String, dynamic>, explicit options objects, strong enums instead of magic strings).
- Decode limits: default
depth=5,parameterLimit=1000,listLimit=20; exceeding may coerce indices into object keys or (with strict flags) throw. - List vs Map merging mimics Node: duplicate keys accumulate to lists unless
duplicatesoption changes strategy. Undefinedentries are placeholders stripped byUtils.compactpost‑decode / during encode pruning; never serializeUndefineditself.Utils.encodeusesallowMalformed: truewhen decodingByteBufferas UTF‑8 to match NodeBuffer.toString('utf8').- Charset sentinel: when
charsetSentinel=true,utf8=✓token (encoded differently per charset) overrides providedcharsetand is omitted from output. allowDots&decodeDotInKeys: invalid combination (decodeDotInKeys: truewithallowDots: false) must throw (constructor asserts). Preserve that invariant.- Merge semantics: list holes are treated as missing values when merging list‑of‑maps by index (no
[Undefined, map]pair at an index);parseLists=falsenormalizes list results into string‑key maps. - Negative
listLimitdisables numeric indexing; withthrowOnLimitExceededcertain pushes must throwRangeError(match existing patterns in decode logic—consult decode part file before altering behavior). - Encoding pipeline can inject custom encoder/decoder hooks; preserve argument order and named params (
charset,format,kind).
- Public APIs: lowerCamelCase; files: snake_case; enums: PascalCase members.
- Avoid printing (
avoid_printlint); no side‑effects outside encode/decode. - Keep option constructors const & validating invariants via
assert. - Prefer extension methods & small helpers over large monolithic functions.
- Maintain iteration order: when converting iterables to maps use string indices (
Utils.createIndexMap); merging may temporarily useSplayTreeMapfor deterministic ordering.
- Unit tests in
test/unit/mirror README examples & edge cases (list formats, depth, duplicates, charsets, null handling, custom hooks). - E2E tests in
test/e2e/exercise higher‑level URI extensions. - Comparison tests (
test/comparison/) ensure parity with the JS implementation via fixture JSON & a Node script; runtest/comparison/compare_outputs.shafter semantic changes to core encode/decode logic. - When adding behavior, first add/modify a unit test replicating the JS
qsbehavior (consult upstream if uncertain), then adjust implementation.
- Install deps:
make install(wrapsdart pub get). - Quality gate before commits/PR:
make sure(analyze + format check + tests). Always keep CI green. - Run tests:
make test(VM platform). Add coverage:make show_test_coverage→ opens HTML atcoverage/html/index.html. - Formatting:
make format(ordart format .); never commit unformatted code. - Dependency upgrades:
make upgrade; check outdated:make check_outdated.
- Write or update a focused test under
test/unit/(mirroring file/module naming). - If changing cross‑language semantics, update comparison fixtures & run the comparison script.
- Implement minimal changes: prefer editing encode/decode parts or localized helpers in
utils.dartrather than inflating public API. - Update README only if user‑visible behavior changes (options, defaults, new enum values). Keep examples minimal and consistent with test expectations.
- Run
make sure; fix lint warnings as errors.
- DO NOT expose new symbols via
qs_dart.dartunless intentionally part of the public contract. - Avoid broad refactors of
utils.dart; many subtle invariants (Undefined pruning, ordering, surrogate pair handling) are covered by tests—modify surgically. - Keep percent‑encoding logic streaming & segment‑aware (
_segmentLimit=1024) to preserve performance. - When adding an option: ensure const constructor, add to
copyWith(if present in file—verify before editing), equality viaEquatableMixin, and README table/example. - Ensure new enums get exported in
qs_dart.dartonly if needed by library consumers (options signatures, return types).
- Encoding avoids splitting surrogate pairs when chunking strings; preserve that loop structure.
- Merge uses structural checks to decide list append vs map merge; naive rebuilding can degrade performance & ordering.
- Avoid recursive deep traversal for compaction—current iterative approach prevents stack overflows on adversarial inputs.
- Input type validation: non‑String/non‑Map to
QS.decodemust throwArgumentError.value(message currently: 'The input must be a String or a Map<String, dynamic>'). Keep wording for stable tests. - Depth/list/parameter overflows toggle between soft limiting or throwing depending on
strictDepth/throwOnLimitExceededflags; never silently ignore an explicit strict flag.
- README examples double as test patterns—when you change semantics, sync both.
- Keep code comments concise; prefer section banners only where logic is subtle (merging, encoding loops).
- Commit subject prefix emoji (e.g.,
:sparkles: Add X,:bug:etc.) aligned with existing history. - PRs: link issues, state behavior changes, mention any parity deviations from Node
qs.
- Additional list formats or duplicate strategies: introduce enum value + guarded branch + tests.
- Extra utility for selective key removal during encode (implemented via
filterlambda) — prefer documenting patterns rather than expanding API unless necessary.
If anything above is unclear or you need deeper decode/encode part internals, request those specific files before editing them.
Feedback welcome: highlight unclear invariants or missing edge cases so we can refine this guide.