JQ::Lite(3)

πŸ“¦ version 2.45, in alpine 3.24

πŸ’– NAME πŸ’–

Okay bestie, welcome to JQ::Lite(3) β€” that's the manual name β€” from the User Contributed Perl Documentation. This page is all about JQ::Lite β€” jq-compatible JSON query engine in pure Perl (no external binaries). No cap, that's the whole vibe. πŸ’…


🌸 VERSION 🌸

Version 2.45. That's the number, periodt.


✨ SYNOPSIS ✨

Here's the little main-character snippet:

use JQ::Lite;

my $jq = JQ::Lite->new;
my @results = $jq->run_query($json_text, '.users[].name');

for my $r (@results) {
    print encode_json($r), "\n";
}

πŸ¦‹ DESCRIPTION πŸ¦‹

JQ::Lite is a lightweight, jq-compatible alternative JSON processor written in pure Perl. It's literally designed for environments where the jq binary (https://stedolan.github.io/jq/) cannot be installed β€” think minimal Linux distributions, restricted servers, legacy systems, containers, CI pipelines, or air-gapped environments. 🫢

JQ::Lite provides a familiar jq-like query syntax for extracting, traversing, and filtering JSON data, while requiring no external binaries, no XS modules, and no compilation step. Iconic.

The module can be used both as a Perl library and via the bundled command-line tool jq-lite, offering long-term CLI stability suitable for OS-level packaging. It's giving "built for real deployments." πŸ‘‘


πŸŽ€ USE CASES πŸŽ€

  • jq is not available or cannot be installed
  • Minimal or embedded Linux environments (e.g. Alpine Linux)
  • Containers and CI systems without package managers
  • Legacy systems with old or restricted toolchains
  • Offline or security-sensitive (air-gapped) deployments

πŸ”₯ FEATURES πŸ”₯

Core capabilities

  • Pure Perl implementation β€” no XS, no native libraries, no external binaries.
  • jq-compatible dot-notation traversal β€” for example .users[].name.
  • Optional key access using ? β€” safely skip missing fields.
  • Array indexing and flattening helpers β€” such as .users[0] and .users[].
  • Boolean filtering with select(...) β€” supports comparison operators and logical and / or expressions.
  • Pipe-style query chaining using the | operator β€” similar to jq.
  • Iterator and transformation helpers β€” including map(...), map_values(...), walk(...), limit(n), drop(n), tail(n), chunks(n), range(...), and enumerate().
  • Interactive REPL mode β€” experiment with jq-like filters line by line.
  • Command-line interface jq-lite β€” reads JSON from STDIN or files.
  • Decoder selection via --use β€” JSON::PP, JSON::XS, and compatible modules.
  • Debug logging with --debug β€” and discoverable helpers via --help-functions.

Built-in functions

JQ::Lite mirrors a substantial portion of the jq (https://stedolan.github.io/jq/) function library. Functions are grouped below for easier scanning; every name can be used directly in filters. iykyk. πŸ’–

  • Structure introspection: length, type, keys, keys_unsorted, values, paths, leaf_paths, to_entries, from_entries, with_entries, map_values, walk, arrays, objects, scalars, index, indices, rindex.
  • Selection and set operations: select, has, contains, any, all, unique, unique_by, group_by, group_count, pick, merge_objects, setpath, getpath, del, delpaths, compact, drop, rest, tail.
  • Transformation helpers: map, map_values, walk, enumerate, transpose, flatten_all, flatten_depth, chunks, range, implode, explode, join, split, slice, substr, trim, ltrimstr, rtrimstr, startswith, endswith, upper, lower, ascii_upcase, ascii_downcase, titlecase, tostring, tojson, fromjson, to_number, compact.
  • Aggregation and math: count, sum, sum_by, add, product, min, max, min_by, max_by, avg, avg_by, median, median_by, mode, percentile, variance, stddev, nth, first, last, reverse, sort, sort_desc, sort_by.
  • Flow control and predicates: select, empty, not, test, reduce, foreach, if / then / else constructs.

πŸ’… CONSTRUCTOR πŸ’…

new

my $jq = JQ::Lite->new;

Creates a new instance. Pass raw => 1 to enable raw output and vars => \%hash to predeclare jq-style variables (for example, JQ::Lite->new(vars => { name => 'Alice' })). Obsessed with how easy that is.


🧚 METHODS 🧚

run_query

my @results = $jq->run_query($json_text, $query);

Runs a jq-like query against the given JSON string. Returns a list of matched results. Each result is a Perl scalar (string, number, arrayref, hashref, etc.) depending on the query. It's giving "results, bestie."


🌈 SUPPORTED SYNTAX 🌈

  • .key.subkey to traverse nested hashes.
  • .array[0] for positional access and .array[] to flatten arrays.
  • .key? for optional key access that yields no output when the key is missing.

Filtering and projection

  • Boolean filtering: "select(.key " 1 and .key2 == "foo")>.
  • group_by(.field), group_count(.field), sum_by(.field), avg_by(.field), and median_by(.field) for grouped reductions.
  • reduce expr as $var (init; update) for accumulators with lexical bindings.
  • foreach expr as $var (init; update [; extract]) to stream intermediate results while folding values.
  • unique_by(.key) and sort_by(.key) for deduplicating and ordering complex structures.
  • .key | count or .[] | select(...) | count to count items after applying filters.
  • .array | map(.field) | join(", ") to transform and format array values.

Conditionals

  • if CONDITION then FILTER [elif CONDITION then FILTER ...] [else FILTER] end for jq-style branching.

Evaluates conditions as filters against the current input. The first truthy branch emits its result; optional elif clauses cascade additional tests, and the optional else filter only runs when no prior branch matches. Without an else clause a fully falsey chain produces no output. No output is still a vibe, technically.

Example:

if .score >= 90 then "A"
elif .score >= 80 then "B"
else "C"
end

Sorting and ranking

  • sort_desc() to order scalars in descending order.
  • sort_by(.key) for ordering arrays of objects by computed keys.
  • min_by(.field) / max_by(.field) to select items with the smallest or largest projected values.
  • percentile(p), mode, and nth(n) for statistical lookups within numeric arrays.

String and collection utilities

  • join(", "), split(separator), explode(), and implode() for converting between text and arrays.
  • keys_unsorted() and values() for working with object metadata.
  • paths() and leaf_paths() to enumerate structure paths.
  • getpath(path) and setpath(path; value) to read and write by path arrays without mutating the original input.
  • pick(...) and merge_objects() to reshape objects.
  • to_entries(), from_entries(), and with_entries(filter) for entry-wise transformations.
  • map_values(filter) to apply filters across every value in an object or array of objects.

Detailed helper reference

  • walk(filter) β€” Recursively traverses arrays and objects, applying the supplied filter to each value after its children have been transformed, matching jq's walk/1 behaviour. Arrays and hashes are rebuilt so nested values can be updated in a single pass, while scalars pass directly to the filter.

Example:

.profile | walk(upper)

Returns:

{ "name": "ALICE", "note": "TEAM LEAD" }
  • recurse([filter]) β€” Performs a depth-first traversal mirroring jq's recurse. Each invocation emits the current value and then evaluates either the optional child filter or, when omitted, the object's values and array elements. This makes it easy to walk arbitrary tree structures:

Example:

.users[0] | recurse(.children[]?) | .name

Returns:

"Alice"
"Bob"
"Carol"
  • empty() β€” Discards all output. Compatible with jq. Useful when only side effects or filtering is needed without output.

Example:

.users[] | select(.age > 25) | empty
  • .[] β€” alias for flattening top-level arrays.
  • transpose() β€” Pivots an array of arrays from row-oriented to column-oriented form. When rows have different lengths, the result truncates to the shortest row so that every column contains the same number of elements.

Example:

[[1, 2, 3], [4, 5, 6]] | transpose

Returns:

[[1, 4], [2, 5], [3, 6]]
  • flatten_all() β€” Recursively flattens nested arrays into a single-level array while preserving non-array values.

Example:

[[1, 2], [3, [4]]] | flatten_all

Returns:

[1, 2, 3, 4]
  • flatten_depth(n) β€” Flattens nested arrays up to n levels deep while leaving deeper nesting intact.

Example:

[[1, [2]], [3, [4]]] | flatten_depth(1)

Returns:

[1, [2], 3, [4]]
  • arrays β€” Emits its input only when the value is an array reference, mirroring jq's arrays filter. Scalars and objects yield no output, making it convenient to select array inputs prior to additional processing.

Example:

.items[] | arrays

Returns only the array entries from .items.

  • objects β€” Emits its input only when the value is a hash reference, mirroring jq's objects filter. Scalars and arrays yield no output, letting you isolate objects inside heterogeneous streams.

Example:

.items[] | objects

Returns only the object entries from .items.

  • scalars β€” Emits its input only when the value is a scalar (including strings, numbers, booleans, or null/undef), mirroring jq's scalars helper. Arrays and objects yield no output, making it easy to focus on terminal values within mixed streams.

Example:

.items[] | scalars

Returns only the scalar entries from .items.

  • type() β€” Returns the type of the value as a string: string, number, boolean, array, object, or null.

Example:

.name | type     # => "string"
.tags | type     # => "array"
.profile | type  # => "object"
  • lhs // rhs β€” Implements jq's alternative operator. The left-hand side is returned when it produces a defined value (including false or empty arrays); otherwise the right-hand expression is evaluated as a fallback.

Example:

.users[] | (.nickname // .name)

Returns the nickname when present, otherwise the name field.

  • default(value)
  • default(value) β€” the safety net bestie 🫢 it replaces undefined or null pipeline values with a literal fallback so your pipeline never ghosts you.

Example:

.nickname | default("unknown")

That guarantees the string "unknown" gets emitted whenever .nickname is missing or null. Obsessed, honestly πŸ’…

  • nth(n) β€” grabs the nth element out of an array. Zero-based, so 0 is the first one, like a civilized human being.

Example:

.users | nth(0)   # first user
.users | nth(2)   # third user
  • del(key) β€” yeets a specified key out of a hash object and hands you back a new hash without that key. Original stays unbothered.

Example:

.profile | del("password")

If the key isn't even there, you get the original hash back, unchanged ✨

If you aim it at a non-hash object? It just returns the object unchanged. No drama. πŸ¦‹

  • delpaths(paths) β€” the group-chat version of del: removes multiple keys or indices, all identified by the paths expression you feed it. That expression is evaluated against the current input (think: by using paths() or other jq-lite helpers) and it should yield an array of path arrays, mirroring jq's delpaths/1 behaviour fr fr.

Example:

.profile | delpaths([["password"], ["tokens", 0]])

Paths can be generated dynamically using helpers such as paths() before being passed to delpaths. When a referenced path is missing it is ignored β€” no error, no tantrum. And providing an empty path ([]) removes the entire input value, yielding null. So like… be careful with that one, bestie πŸ’€

  • compact() β€” clears out undef and null values from an array. The glow-up is real.

Example:

.data | compact()

Before: [1, null, 2, null, 3]

After: [1, 2, 3]

  • upper() β€” converts strings to uppercase. When you point it at an array, each scalar element gets uppercased recursively, leaving nested hashes or booleans untouched (they're not invited to this party πŸ™ˆ).

Example:

.title | upper      # => "HELLO WORLD"
.tags  | upper      # => ["PERL", "JSON"]
  • titlecase() β€” converts strings to title case, i.e. first letter of each word uppercase. On arrays, each scalar element gets transformed recursively, leaving nested hashes or booleans untouched.

Example:

.title | titlecase   # => "Hello World"
.tags  | titlecase   # => ["Perl", "Json"]
  • lower() β€” converts strings to lowercase. On arrays, each scalar element gets lowercased recursively, leaving nested hashes or booleans untouched.

Example:

.title | lower      # => "hello world"
.tags  | lower      # => ["perl", "json"]
  • has(key) β€” the vibe check: does the current value actually expose the key or index you asked about? πŸ‘‘
    • For hashes, returns true when the key is present.
    • For arrays, returns true when the zero-based index exists.

Example:

.meta  | has("version")   # => true
.items | has(2)            # => true when at least 3 elements exist
  • contains(value) β€” checks whether the current value includes the fragment you handed it πŸ”
    • For strings, returns true when the substring exists.
    • For arrays, returns true if any element equals the supplied value.
    • For hashes, returns true when the key is present.

Example:

.title | contains("perl")     # => true
.tags  | contains("json")     # => true
.meta  | contains("lang")     # => true
  • contains_subset(value) β€” the opt-in jq-style subset containment. Behaves like contains/1, but when arrays are involved the right-hand array must be a multiset subset of the left-hand one. Order does not matter and duplicate elements are honoured (yes, they count). Nested arrays inside hashes get evaluated with the same subset rules. It's giving "math but make it fashion" 🌸

Examples:

[1,2,3] | contains_subset([2,3])            # => true
[1,2,3] | contains_subset([3,2])            # => true
[1,2,3] | contains_subset([4])              # => false
{"b":{"y":[1,2,3]}} | contains_subset({"b":{"y":[2]}})  # => true
  • test(pattern[, flags]) β€” asks the question: does the current string input match this regular expression? The pattern is interpreted as a Perl-compatible regex, and the optional flag string supports the common jq modifiers i, m, s, and x.
    • Scalars are coerced to strings before matching (booleans become "true" or "false").
    • Arrays are processed element-wise, returning arrays of JSON booleans.
    • Non-string container values that cannot be coerced yield false.

Examples:

.title | test("^Hello")           # => true
.title | test("world")            # => false (case-sensitive)
.title | test("world"; "i")      # => true (case-insensitive)
.tags  | test("^p")               # => [true, false, false]
  • all([filter]) β€” checks whether every element (optionally projected through filter) is truthy, mirroring jq's all/1 helper. It's the strict friend who needs everyone on board 😀
    • For arrays without a filter, returns true when every element is truthy (empty arrays yield true).
    • For arrays with a filter, applies the filter to each element and requires every produced value to be truthy.
    • When the current input is a scalar, falls back to checking the value's truthiness (or the filter's results when supplied).

Examples:

.flags | all            # => true when every element is truthy (empty => true)
.users | all(.active)   # => true when every user is active
  • any([filter]) β€” returns true when at least one value in the input is truthy. When a filter is provided, it's evaluated against each array element (or the current value when you're not operating on an array) and the truthiness of the filter's results is what counts. One icon is enough to carry the group 🫢
    • For arrays without a filter, returns true if any element is truthy.
    • For arrays with a filter, returns true when the filter yields a truthy value for any element.
    • For scalars, hashes, and other values, evaluates the value (or filter results) directly.

Example:

.flags | any            # => true when any element is truthy
.users | any(.active)   # => true when any user is active
  • not β€” logical negation using jq's truthiness rules. Returns true when the current input is falsy (e.g. false, null, empty string, empty array, or empty object) and false otherwise. Arrays and objects are considered truthy when they contain at least one element or key, respectively. Flip the vibe ✨

Examples:

true  | not   # => false
[]    | not   # => true
.ok   | not   # => negates the truthiness of .ok
  • unique_by(".key") β€” removes duplicate objects (or values) from an array by projecting each entry onto the supplied key path, keeping only the first occurrence of each signature. Use . to deduplicate by the entire value. First come, first served πŸ’…

Example:

.users | unique_by(.name)      # => keeps first record for each name
.tags  | unique_by(.)          # => removes duplicate scalars
  • startswith("prefix") β€” returns true if the current string (or each string inside an array) begins with the supplied prefix. Non-string values yield false, no exceptions, no pity 😭

Example:

.title | startswith("Hello")   # => true
.tags  | startswith("j")       # => [false, true, false]
  • endswith("suffix") β€” returns true if the current string (or each string inside an array) ends with the supplied suffix. Non-string values yield false.

Example:

.title | endswith("World")     # => true
.tags  | endswith("n")         # => [false, true, false]
  • substr(start[, length]) β€” extracts a substring from the current string using zero-based indexing. When applied to arrays, each scalar element receives the same slicing arguments recursively. Snipped and snatched βœ‚οΈ

Examples:

.title | substr(0, 5)       # => "Hello"
.tags  | substr(-3)         # => ["erl", "SON"]
  • slice(start[, length]) β€” returns a portion of the current array using zero-based indexing. Negative start values count from the end of the array (iconic behaviour). When length is omitted, the slice continues through the final element. Non-array inputs pass through unchanged so pipelines can mix scalar and array values safely 🌈

Examples:

.users | slice(0, 2)        # => first two users
.users | slice(-2)          # => last two users
  • rest β€” returns the current array without its first element. Empty arrays yield empty arrays (no errors, no theatrics). Non-array inputs pass through untouched so mixed pipelines remain compatible πŸ¦‹

Examples:

[1,2,3] | rest              # => [2,3]
[]       | rest             # => []
  • tail(n) β€” returns the final n elements of the current array. When n is zero the result is an empty array, and when n exceeds the array length the entire array is returned unchanged. Non-array inputs pass through untouched so the helper composes cleanly inside pipelines that also yield scalars or objects. All about that last-bits energy ✨

Examples:

.users | tail(2)            # => last two users
.users | tail(10)           # => full array when shorter than 10
  • chunks(n) β€” splits the current array into sub-arrays of length n. A size of zero is rounded up to one so you always receive at least single-element chunks (it refuses to give you nothing, king behaviour πŸ‘‘). When the final partition would be shorter than n, the trailing chunk contains only the remaining elements. Non-array inputs are returned untouched so mixed pipelines continue working as expected.

Examples:

[1,2,3,4,5] | chunks(2)     # => [[1,2],[3,4],[5]]
[1,2,3]     | chunks(0)     # => [[1],[2],[3]]
  • range(start; end[, step]) β€” emits a numeric sequence that begins at start (default 0) and advances by step (default 1) until it reaches but does not include end. When the step is negative the helper counts downward and stops once the value is less than or equal to the exclusive bound. Non-numeric arguments result in the input being passed through unchanged so pipelines remain resilient. It's giving countdown πŸ•›

Examples:

null | range(5)           # => 0,1,2,3,4
null | range(2; 6; 2)     # => 2,4
null | range(10; 2; -4)   # => 10,6
  • enumerate() β€” converts arrays into an array of objects pairing each element with its zero-based index. Each object contains two keys: index for the position and value for the original element. Non-array inputs are returned unchanged so the helper composes inside pipelines that may yield scalars or hashes. Numbered, labelled, organised β€” main character energy πŸŽ€

Examples:

  • index(value) β€” gives you the zero-based index of the first time that value shows up ✨. When you're working with an array it does deep comparisons, so nested stuff (hashes, arrays, booleans) matches the way you'd expect β€” no cap. When the current thing is a string instead, it returns the position of the substring, or null if it's not in there anywhere πŸ’…
.users | index("Alice")     # => 0
.tags  | index("json")      # => 1
  • rindex(value) β€” same vibe as index but from the back πŸ¦‹. It gives you the zero-based index of the final occurrence. Arrays get scanned from the end using deep comparisons, and strings give you the position of the last matching substring (or null when there's no match at all) πŸŽ€
.users | rindex("Alice")    # => 3
.tags  | rindex("perl")     # => 2
"banana" | rindex("an")    # => 3
  • indices(value) β€” the full receipts πŸ“‹: every zero-based index where the value appears. For arrays it runs deep comparisons against each element and collects the matching indexes into an array. For strings it hunts for literal substring matches (including overlapping ones) and drops each starting position. And when the fragment is empty, it returns positions for every character boundary, mirroring jq's behaviour exactly πŸ‘‘
.users | indices("Alice")     # => [0, 3]
"banana" | indices("an")      # => [1, 3]
"perl"   | indices("")        # => [0, 1, 2, 3, 4]
  • abs() β€” absolute values for numbers, periodt πŸ’ͺ. Scalars get converted directly, while arrays are processed element-by-element with non-numeric entries preserved (they stay exactly as they were, bestie)
.temperature | abs      # => 12
.deltas      | abs      # => [3, 4, 5, "n/a"]
  • ceil() β€” rounds numbers up to the nearest integer πŸ”₯. Scalars and array elements that look like numbers get rounded upward, while everything else passes through untouched 🌸
.price   | ceil     # => 20
.changes | ceil     # => [2, -1, "n/a"]
  • floor() β€” rounds numbers down to the nearest integer 🌈. Scalars and array elements that look like numbers go downward, and non-numeric values are left alone, no notes πŸ’…
.price   | floor    # => 19
.changes | floor    # => [1, -2, "n/a"]
  • round() β€” nearest integer, standard rounding rules: half up for positive values, half down for negatives 🎯. Scalars and array elements that look like numbers get adjusted, other values pass through unchanged 🫢
.price   | round    # => 19
.changes | round    # => [1, -2, "n/a"]
  • clamp(min, max) β€” keeps your numeric values inside the inclusive range you hand it 🧚. Scalars and array elements that look like numbers get coerced into numeric context and clamped between the supplied minimum and maximum. If a bound is omitted or isn't numeric, that side is treated as unbounded 😌. Non-numeric values pass through unchanged so your pipelines stay lossless β€” we love a respectful queen
.score  | clamp(0, 100)       # => 87
.deltas | clamp(-5, 5)        # => [-5, 2, 5, "n/a"]
  • tostring() β€” turns the current value into a JSON string representation ✨. Scalars are stringified directly, booleans become "true"/"false", and undefined values render as "null". Arrays and objects get encoded to their JSON text form, so the output matches jq's behavior on structured data ⭐
.score   | tostring   # => "42"
.profile | tostring   # => "{\"name\":\"Alice\"}"
  • tojson() β€” encodes the current value as JSON text no matter what type it started as, mirroring jq's "tojson" πŸ‘‘. Scalars, booleans, nulls, arrays and objects all produce a JSON string, which means raw string inputs can be re-escaped safely for embedding or for decoding later πŸ’–
.score   | tojson   # => "42"
.name    | tojson   # => "\"Alice\""
.profile | tojson   # => "{\"name\":\"Alice\"}"
  • fromjson() β€” parses JSON text back into native Perl data structures πŸ¦‹. Plain strings get decoded directly, while arrays are processed element-by-element to mirror jq's handy broadcasting behaviour. Invalid JSON inputs are passed through unchanged so nothing gets lost along the way πŸ™ˆ
.raw   | fromjson   # => {"name":"Bob"}
.lines | fromjson   # => [1, true, null]
  • to_number() β€” coerces values that look like numbers into actual numeric scalars πŸ”’. Strings are converted using Perl's numeric semantics, booleans become 1 or 0, and arrays are processed element-by-element. Non-numeric strings, objects and other references come back unchanged so your pipeline stays lossless πŸ’œ
.score    | to_number   # => 42
.strings  | to_number   # => [10, "n/a", 3.5]
  • trim() β€” swipes the leading and trailing whitespace off strings 🧹. Arrays are processed recursively, while hashes and other references are left untouched πŸ’…
.title | trim          # => "Hello World"
.tags  | trim          # => ["perl", "json"]
  • ltrimstr("prefix") β€” removes "prefix" from the start of strings when it's actually there πŸŽ€. Arrays are processed recursively so nested string values get the same treatment. Inputs that don't begin with the supplied prefix come back unchanged 🌸
.title | ltrimstr("Hello ")  # => "World"
.tags  | ltrimstr("#")       # => ["perl", "json"]
  • rtrimstr("suffix") β€” removes "suffix" from the end of strings when present 🎯. Arrays are processed recursively so nested string values are handled consistently. Inputs that don't end with the supplied suffix come back unchanged 🌟
.title | rtrimstr(" World")  # => "Hello"
.tags  | rtrimstr("ing")     # => ["perl", "json"]

πŸ”₯ COMMAND LINE USAGE πŸ”₯

"jq-lite" is a CLI wrapper for this module, bestie β€” here's the whole menu πŸ‘‘

cat data.json | jq-lite '.users[].name'
jq-lite '.users[] | select(.age > 25)' data.json
jq-lite -r '.users[].name' data.json
jq-lite '.[] | select(.active == true) | .name' data.json
jq-lite '.users[] | select(.age > 25) | count' data.json
jq-lite '.users | map(.name) | join(", ")'
jq-lite '.users[] | select(.age > 25) | empty'
jq-lite '.profile | values'

πŸ’¬ Interactive Mode πŸ’¬

Omit the query and you drop straight into interactive mode:

jq-lite data.json

Then you can just type queries line-by-line against the same JSON input 🫢 it's giving live coding session, honestly.

πŸ•΅οΈ Decoder Selection and Debug πŸ•΅οΈ

jq-lite --use JSON::PP --debug '.users[0].name' data.json

πŸ“– Show Supported Functions πŸ“–

jq-lite --help-functions

Displays all built-in functions and their descriptions β€” a whole glossary, no gatekeeping πŸ’–


🧸 REQUIREMENTS 🧸

Uses only core modules:

  • JSON::PP

Optional: JSON::XS, Cpanel::JSON::XS, JSON::MaybeXS


πŸ‘€ SEE ALSO πŸ‘€

JSON::PP, jq β€” https://stedolan.github.io/jq/


🌐 HOMEPAGE 🌐

The project homepage provides documentation, examples, and release notes: https://kawamurashingo.github.io/JQ-Lite/index-en.html.


✍️ AUTHOR ✍️

Shingo Kawamura β€” pannakoota1@gmail.com


πŸ“œ LICENSE πŸ“œ

Same as Perl itself.


And that's the whole page, bestie πŸŽ€ this was written for perl v5.42.2 and dated 2026-05-08 β€” so if your Perl is older than that whole era, maybe go update before you come for me πŸ’…βœ¨ slay, and happy querying πŸ¦‹πŸ’–

πŸ“¦ where this page comes from