context_spook 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +103 -12
  3. data/Rakefile +0 -1
  4. data/bin/context_spook +6 -3
  5. data/context_spook.gemspec +7 -8
  6. data/lib/context_spook/generator.rb +75 -45
  7. data/lib/context_spook/output_context.rb +2 -2
  8. data/lib/context_spook/toon/encoder.rb +628 -0
  9. data/lib/context_spook/toon/example.md +36 -66
  10. data/lib/context_spook/toon.rb +13 -15
  11. data/lib/context_spook/utils.rb +14 -9
  12. data/lib/context_spook/version.rb +1 -1
  13. data/spec/context_spook/generator_spec.rb +63 -2
  14. data/spec/context_spook/toon_encoder_spec.rb +90 -0
  15. data/spec/context_spook/toon_spec.rb +15 -16
  16. data/spec/examples/README.md +79 -0
  17. data/spec/examples/conversions/keyed-tabular-objects.json +19 -0
  18. data/spec/examples/conversions/keyed-tabular-objects.toon +4 -0
  19. data/spec/examples/conversions/nested-field-groups.json +28 -0
  20. data/spec/examples/conversions/nested-field-groups.toon +4 -0
  21. data/spec/examples/conversions/tabular-arrays.json +22 -0
  22. data/spec/examples/conversions/tabular-arrays.toon +4 -0
  23. data/spec/examples/invalid/delimiter-mismatch.toon +3 -0
  24. data/spec/examples/invalid/length-mismatch.toon +3 -0
  25. data/spec/examples/invalid/multiple-root-primitives.toon +2 -0
  26. data/spec/examples/valid/comments.toon +5 -0
  27. data/spec/examples/valid/delimiter-scoping.toon +5 -0
  28. data/spec/examples/valid/mixed-arrays.toon +5 -0
  29. data/spec/examples/valid/nested-objects.toon +9 -0
  30. data/spec/examples/valid/objects.toon +6 -0
  31. data/spec/examples/valid/pipe-delimiter.toon +3 -0
  32. data/spec/examples/valid/primitive-arrays.toon +3 -0
  33. data/spec/examples/valid/tab-delimiter.toon +3 -0
  34. data/spec/fixtures/encode/arrays-nested.json +120 -0
  35. data/spec/fixtures/encode/arrays-objects.json +160 -0
  36. data/spec/fixtures/encode/arrays-primitive.json +95 -0
  37. data/spec/fixtures/encode/arrays-tabular.json +182 -0
  38. data/spec/fixtures/encode/delimiters.json +214 -0
  39. data/spec/fixtures/encode/objects-keyed.json +160 -0
  40. data/spec/fixtures/encode/objects.json +288 -0
  41. data/spec/fixtures/encode/primitives.json +262 -0
  42. data/spec/fixtures/encode/whitespace.json +54 -0
  43. metadata +35 -18
@@ -1,69 +1,39 @@
1
- # TOON (Token-Oriented Object Notation)
1
+ # TOON format — quick reference
2
+ #
3
+ # key: value scalar (bare if safe)
4
+ # key[2]: array with length
5
+ # - item list item
6
+ # key[4]{col1,col2}: tabular array (CSV rows)
7
+ # val1,val2 one row per line
8
+ #
9
+ # Quoting: double-quote a string only if it contains
10
+ # , { } [ ] # " \ or starts/ends with whitespace, or looks
11
+ # like a number / true / false / null.
2
12
 
3
- It is a compact, human-readable serialization format that reduces JSON token
4
- count by 30-40% while maintaining lossless data fidelity. It uses CSV-style
5
- tabular arrays for uniform data structures and YAML-like indentation for nested
6
- objects, making it ideal for LLM input where token efficiency and parsing
7
- reliability matter.
8
-
9
- Key benefit: Same data, fewer tokens, better LLM understanding.
10
-
11
- **Original JSON:**
12
- ```json
13
- {
14
- "database": {
15
- "name": "user_management",
16
- "tables": [
17
- {
18
- "name": "users",
19
- "columns": [
20
- {
21
- "name": "id",
22
- "type": "integer",
23
- "primary_key": true
24
- },
25
- {
26
- "name": "email",
27
- "type": "string",
28
- "unique": true
29
- }
30
- ],
31
- "row_count": 1000
32
- },
33
- {
34
- "name": "orders",
35
- "columns": [
36
- {
37
- "name": "id",
38
- "type": "integer",
39
- "primary_key": true
40
- },
41
- {
42
- "name": "user_id",
43
- "type": "integer"
44
- }
45
- ],
46
- "row_count": 5000
47
- }
48
- ]
49
- }
50
- }
51
- ```
52
-
53
- **TOON equivalent:**
54
- ```
13
+ files:
14
+ "app/models/user.rb":
15
+ content[4]{linenumber,line}:
16
+ 1,require 'json'
17
+ 2,""
18
+ 3,"# a comment line"
19
+ 4,def parse(data)
20
+ size: 42
21
+ lines: 4
55
22
  database:
56
23
  name: user_management
57
- tables[2]{name,row_count}:
58
- users,1000
59
- orders,5000
60
- tables[2]{name,columns}:
61
- users:
62
- columns[2]{name,type,primary_key}:
63
- id,integer,true
64
- email,string,true
65
- orders:
66
- columns[2]{name,type}:
67
- id,integer,true
68
- user_id,integer,false
69
- ```
24
+ tables[2]:
25
+ - name: users
26
+ columns[2]:
27
+ - name: id
28
+ type: integer
29
+ primary_key: true
30
+ - name: email
31
+ type: string
32
+ row_count: 1000
33
+ - name: orders
34
+ columns[2]:
35
+ - name: id
36
+ type: integer
37
+ - name: user_id
38
+ type: integer
39
+ row_count: 5000
@@ -1,13 +1,17 @@
1
- require 'toon'
1
+ require 'json'
2
+ require 'context_spook/toon/encoder'
2
3
 
3
4
  # The ContextSpook::TOON module provides TOON (Token-Oriented Object Notation)
4
- # serialization functionality for context objects.
5
+ # v4 serialization functionality for context objects.
5
6
  module ContextSpook::TOON
6
- # Converts the context object to TOON format.
7
+ # Converts the context object to TOON v4 format.
8
+ #
9
+ # Uses a fast JSON round-trip (C-optimized) to normalize the data,
10
+ # then walks the canonical Hash/Array tree with our native encoder.
7
11
  #
8
12
  # @return [String] the TOON-encoded representation of the context
9
13
  def to_toon
10
- ::Toon.encode(as_json)
14
+ Encoder.encode(as_json)
11
15
  end
12
16
  memoize method: :to_toon
13
17
 
@@ -15,19 +19,13 @@ module ContextSpook::TOON
15
19
  #
16
20
  # @return [Integer] the size in bytes of the TOON representation
17
21
  def toon_size
18
- to_toon.size
22
+ to_toon.bytesize
19
23
  end
20
24
 
21
- # The toon_example method reads and returns the content of an example
22
- # Markdown file that demonstrates the TOON (Token-Oriented Object Notation)
23
- # format.
24
- #
25
- # This method is used to provide a sample representation of how context data
26
- # can be formatted in TOON, which is an alternative serialization format
27
- # supported by the ContextSpook library.
25
+ # Streams the TOON v4 output to the given IO.
28
26
  #
29
- # @return [ String ] the content of the TOON example Markdown file
30
- def toon_example
31
- File.read(Pathname.new(__dir__) + 'toon/example.md')
27
+ # @param output [IO] the IO to write to (e.g. STDOUT, File, StringIO)
28
+ def to_toon_stream(output)
29
+ Encoder.encode(as_json, output: output)
32
30
  end
33
31
  end
@@ -6,15 +6,14 @@ module ContextSpook::Utils
6
6
  # The format_size method converts a byte size value into a human-readable
7
7
  # string with binary units.
8
8
  #
9
- # This method takes a raw byte count and formats it using the Tins::Unit
10
- # library to display the size with appropriate binary prefixes (KiB, MiB,
11
- # etc.) and two decimal places.
9
+ # library to display the size with appropriate IEC prefixes (KiB, MiB,
10
+ # etc.) and one decimal place.
12
11
  #
13
12
  # @param context_size [ Integer ] the size in bytes to be formatted
14
13
  #
15
14
  # @return [ String ] the formatted size string with binary units
16
15
  def format_size(context_size)
17
- Tins::Unit.format(context_size, format: '%.2f %U', unit: ?b, prefix: 1024)
16
+ Tins::Unit.format(context_size, format: '%.1f %U', unit: ?B, prefix: :iec_uc)
18
17
  end
19
18
 
20
19
  # The format_tokens method converts a token count into a human-readable
@@ -28,14 +27,20 @@ module ContextSpook::Utils
28
27
  end
29
28
 
30
29
  # The estimate_tokens method provides a crude estimation of token counts
31
- # based on the byte size of the input content.
30
+ # from either a raw byte count or a string.
32
31
  #
33
- # This follows a heuristic where one token is roughly equivalent to 3.5 bytes.
32
+ # This follows a heuristic where one token is roughly equivalent to 3.5
33
+ # bytes. A numeric argument is treated as a byte count directly; any other
34
+ # value is converted to a string and measured by its byte size.
34
35
  #
35
- # @param text [ String ] the content to be estimated
36
+ # @param text_or_bytesize [ String, Integer ] the content to estimate
37
+ # tokens for, either as a string or a precomputed byte count
36
38
  #
37
39
  # @return [ Integer ] the estimated number of tokens
38
- def estimate_tokens(text)
39
- (text.size.to_f / 3.5).ceil
40
+ def estimate_tokens(text_or_bytesize)
41
+ unless text_or_bytesize.is_a?(Numeric)
42
+ text_or_bytesize = text_or_bytesize.to_s.bytesize
43
+ end
44
+ (text_or_bytesize.to_f / 3.5).ceil
40
45
  end
41
46
  end
@@ -1,6 +1,6 @@
1
1
  module ContextSpook
2
2
  # ContextSpook version
3
- VERSION = '1.6.0'
3
+ VERSION = '1.7.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -12,7 +12,7 @@ describe ContextSpook::Generator do
12
12
  expect_any_instance_of(described_class).to receive(:verbose_puts).
13
13
  with(/Built.*?31.0 T tokens.*?of JSON context in total/)
14
14
  my_context = ContextSpook.generate_context do
15
- self.token_estimator = -> text { (text.size.to_f / Math::PI).ceil }
15
+ self.token_estimator = -> text { (text.bytesize.to_f / Math::PI).ceil }
16
16
  context do
17
17
  variable foo: 'bar'
18
18
  metadata version: '1.0'
@@ -92,12 +92,73 @@ describe ContextSpook::Generator do
92
92
  expect_any_instance_of(described_class).to receive(:output_context_size).
93
93
  and_call_original
94
94
  expect_any_instance_of(described_class).to receive(:verbose_puts).
95
- with(/Built.*?Kb.*?KT.*?of JSON context in total/)
95
+ with(/Built.*?KiB.*?KT.*?of JSON context in total/)
96
96
  allow_any_instance_of(described_class::Context).to receive(:verbose_puts)
97
97
  my_context
98
98
  end
99
99
  end
100
100
 
101
+ describe 'manifest mode' do
102
+ let :manifest_context do
103
+ ContextSpook.generate_context(manifest: true, verbose: false) do
104
+ context do
105
+ file 'lib/context_spook.rb'
106
+ end
107
+ end
108
+ end
109
+
110
+ let :full_context do
111
+ ContextSpook.generate_context(manifest: false, verbose: false) do
112
+ context do
113
+ file 'lib/context_spook.rb'
114
+ end
115
+ end
116
+ end
117
+
118
+ it 'defaults to manifest: false' do
119
+ gen = described_class.send(:new)
120
+ expect(gen.manifest).to eq false
121
+ end
122
+
123
+ it 'stores manifest: true on Generator' do
124
+ gen = described_class.send(:new, manifest: true)
125
+ expect(gen.manifest).to eq true
126
+ end
127
+
128
+ it 'stamps manifest: true in metadata' do
129
+ expect(manifest_context.metadata[:manifest]).to eq true
130
+ end
131
+
132
+ it 'omits manifest key from metadata when false' do
133
+ expect(full_context.metadata).not_to have_key(:manifest)
134
+ end
135
+
136
+ it 'includes content and lines in non-manifest mode' do
137
+ file = full_context.files['lib/context_spook.rb']
138
+ expect(file[:content]).to be_a(Array)
139
+ expect(file[:content].first).to include(:linenumber, :line)
140
+ expect(file[:lines]).to be > 0
141
+ end
142
+
143
+ it 'omits content and lines in manifest mode' do
144
+ file = manifest_context.files['lib/context_spook.rb']
145
+ expect(file).not_to have_key(:content)
146
+ expect(file).not_to have_key(:lines)
147
+ end
148
+
149
+ it 'retains metadata fields in manifest mode' do
150
+ file = manifest_context.files['lib/context_spook.rb']
151
+ expect(file[:size]).to be > 0
152
+ expect(file[:tokens]).to be > 0
153
+ expect(file[:content_types]).to be_present
154
+ end
155
+
156
+ it 'produces smaller output in manifest mode' do
157
+ expect(manifest_context.to_json.bytesize).
158
+ to be < full_context.to_json.bytesize
159
+ end
160
+ end
161
+
101
162
  describe 'Context' do
102
163
  it 'can have variables' do
103
164
  expect(my_context.variables[:branch]).to be_present
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'json'
5
+
6
+ # Conformance spec: drives ContextSpook::TOON::Encoder against the official
7
+ # TOON v4 encode fixtures from the toon-format-spec repository.
8
+ #
9
+ # Each fixture file is a JSON document:
10
+ # { "tests": [ { "name", "input", "expected", ["options"], ["minSpecVersion"] } ] }
11
+ #
12
+ # We load every encode fixture, iterate the test cases, encode via our
13
+ # encoder, and compare the output string.
14
+ #
15
+ # Fixtures with minSpecVersion above our target are skipped.
16
+
17
+ describe ContextSpook::TOON::Encoder do
18
+ FIXTURES_DIR = File.expand_path('../fixtures/encode', __dir__)
19
+ MAX_SPEC_VERSION = '4.4'
20
+
21
+ describe '.new' do
22
+ it 'can output to IO' do
23
+ io = StringIO.new
24
+ encoder = described_class.new(output: io)
25
+ encoder.encode(%w[ hello world ], 0)
26
+ expect(io.string).to eq '[2]: hello,world'
27
+ end
28
+
29
+ it 'can output to StringArray' do
30
+ encoder = described_class.new
31
+ encoder.encode(%w[ hello world ], 0)
32
+ expect(encoder.to_s).to eq '[2]: hello,world'
33
+ end
34
+ end
35
+
36
+ def self.fixture_files
37
+ Dir[File.join(FIXTURES_DIR, '*.json')].sort
38
+ end
39
+
40
+ def self.version_ok?(fixture)
41
+ return true unless (ver = fixture['minSpecVersion'])
42
+ ver.version <= MAX_SPEC_VERSION.version
43
+ end
44
+
45
+ fixture_files.each do |path|
46
+ data = JSON.parse(File.read(path))
47
+ category = File.basename(path, '.json')
48
+
49
+ describe "fixture: #{category}" do
50
+ data['tests'].each do |test|
51
+ name = test['name']
52
+ input = JSON.parse(JSON.generate(test['input']))
53
+
54
+ # Skip tests for spec versions we haven't implemented yet
55
+ if self.version_ok?(test)
56
+ it name do
57
+ options = test['options'] || {}
58
+ delimiter = options['delimiter'] || ','
59
+ indent = (options['indentSize'] || 2)
60
+
61
+ actual = described_class.encode(
62
+ input, indent: indent, delimiter:
63
+ )
64
+ expected = test['expected']
65
+
66
+ if actual == expected
67
+ # pass
68
+ else
69
+ # Pretty diff for debugging
70
+ expected_lines = expected.lines
71
+ actual_lines = actual.lines
72
+ diff_lines = []
73
+ max_len = [expected_lines.size, actual_lines.size].max
74
+ (0...max_len).each do |i|
75
+ e = expected_lines[i] || '<missing>'
76
+ a = actual_lines[i] || '<missing>'
77
+ diff_lines << " #{i + 1}: expected #{e.inspect} got #{a.inspect}" \
78
+ unless e == a
79
+ end
80
+ raise "TOON mismatch:\n#{diff_lines.join("\n")}"
81
+ end
82
+ end
83
+ else
84
+ skip "#{name} (requires spec v#{test['minSpecVersion']}, max: #{MAX_SPEC_VERSION})" do
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -20,22 +20,21 @@ describe ContextSpook::TOON do
20
20
 
21
21
  it 'produces valid TOON output' do
22
22
  toon_output = my_context.to_toon
23
- expect(toon_output).to match(
24
- %r{
25
- \Afiles:\n
26
- ^[ ]{2}"lib/context_spook\.rb":\n
27
- ^[ ]{4}content:[ ]"[^"]+"\n
28
- ^[ ]{4}size:[ ]120\n
29
- ^[ ]{4}lines:[ ]5\n
30
- ^[ ]{4}content_types\[2\]:[ ]application/x-ruby,text/x-ruby\n
31
- ^commands:\n
32
- ^metadata:\n
33
- ^[ ]{2}version:[ ]"1\.0"\n
34
- ^[ ]{2}format:[ ]JSON\n
35
- ^variables:\n
36
- ^[ ]{2}foo:[ ]bar\z
37
- }x
23
+ expect(toon_output).to start_with(
24
+ "files:\n" \
25
+ " \"lib/context_spook.rb\":\n" \
26
+ " content[5]{linenumber,line}:\n" \
27
+ " 1,module ContextSpook\n"
38
28
  )
29
+ expect(toon_output).to include(" size: 120\n")
30
+ expect(toon_output).to include(" lines: 5\n")
31
+ expect(toon_output)
32
+ .to include(" content_types[2]: application/x-ruby,text/x-ruby\n")
33
+ expect(toon_output).to include("commands:\n")
34
+ expect(toon_output).to include(" version: \"1.0\"\n")
35
+ expect(toon_output).to include(" format: JSON\n")
36
+ expect(toon_output).to include("variables:\n")
37
+ expect(toon_output).to end_with(" foo: bar")
39
38
  end
40
39
  end
41
40
 
@@ -49,7 +48,7 @@ describe ContextSpook::TOON do
49
48
  it 'matches the size of the TOON output' do
50
49
  size = my_context.toon_size
51
50
  toon_output = my_context.to_toon
52
- expect(size).to eq(toon_output.size)
51
+ expect(size).to eq(toon_output.bytesize)
53
52
  end
54
53
  end
55
54
 
@@ -0,0 +1,79 @@
1
+ # TOON Examples
2
+
3
+ Example TOON files, one per format feature – each filename names the feature it demonstrates. All files validate against the [reference implementation](https://github.com/toon-format/toon): valid examples decode in strict mode, invalid examples error, and every conversion pair is generated by the reference encoder and round-trip verified.
4
+
5
+ ## Valid Examples
6
+
7
+ ### Objects
8
+
9
+ - [`valid/objects.toon`](valid/objects.toon) – Flat object with primitive values
10
+ - Key-value pairs across all data types: string, number, boolean, null
11
+ - Spec: §8
12
+
13
+ - [`valid/nested-objects.toon`](valid/nested-objects.toon) – Multi-level nested objects
14
+ - Indentation-based nesting (2 spaces per level) instead of braces
15
+ - Spec: §8, §12
16
+
17
+ ### Arrays
18
+
19
+ - [`valid/primitive-arrays.toon`](valid/primitive-arrays.toon) – Inline primitive arrays
20
+ - Compact inline form `[N]: item1,item2,item3` and the empty array `[]`
21
+ - Spec: §9.1
22
+
23
+ - [`valid/mixed-arrays.toon`](valid/mixed-arrays.toon) – Mixed-type array (list form)
24
+ - One `-` item per element, used wherever neither inline nor tabular form applies
25
+ - Spec: §9.4
26
+
27
+ ### Comments
28
+
29
+ - [`valid/comments.toon`](valid/comments.toon) – Full-line `#` comments
30
+ - Stripped by decoders in a lexical pre-pass, before row counting – decode-only; encoders never emit them
31
+ - Spec: §5.1
32
+
33
+ ### Delimiters
34
+
35
+ - [`valid/pipe-delimiter.toon`](valid/pipe-delimiter.toon) – Pipe delimiter (`|`)
36
+ - Delimiter marker appears in both header `[N|]` and field list `{field|...}`
37
+ - Spec: §11
38
+
39
+ - [`valid/tab-delimiter.toon`](valid/tab-delimiter.toon) – Tab delimiter (`\t`)
40
+ - Tab character appears in both header and between fields
41
+ - Spec: §11
42
+
43
+ - [`valid/delimiter-scoping.toon`](valid/delimiter-scoping.toon) – Document vs active delimiter
44
+ - Tabular row cells split only on the active delimiter; object field values follow document delimiter quoting
45
+ - Spec: §11
46
+
47
+ ## Conversions
48
+
49
+ Side-by-side JSON ↔ TOON pairs, one per tabular form:
50
+
51
+ - [`conversions/tabular-arrays.json`](conversions/tabular-arrays.json) + [`conversions/tabular-arrays.toon`](conversions/tabular-arrays.toon)
52
+ - Uniform arrays of objects collapse into a table: fields declared once in the header, one row per element
53
+ - Spec: §9.3
54
+
55
+ - [`conversions/nested-field-groups.json`](conversions/nested-field-groups.json) + [`conversions/nested-field-groups.toon`](conversions/nested-field-groups.toon)
56
+ - A uniform nested-object column folds into the header (`{id,customer{name,country},total}`) while rows stay flat
57
+ - Spec: §9.3
58
+
59
+ - [`conversions/keyed-tabular-objects.json`](conversions/keyed-tabular-objects.json) + [`conversions/keyed-tabular-objects.toon`](conversions/keyed-tabular-objects.toon)
60
+ - Objects whose values are uniform objects become tables whose entry rows carry their own keys; the colon after the length (`[3:]`) marks the keyed header
61
+ - Spec: §9.5
62
+
63
+ ## Invalid Examples
64
+
65
+ Files that intentionally violate TOON syntax rules and MUST error in strict mode:
66
+
67
+ - [`invalid/length-mismatch.toon`](invalid/length-mismatch.toon) – Array length mismatch
68
+ - Declares `[3]` but provides only 2 rows
69
+ - Spec: §14.1
70
+
71
+ - [`invalid/multiple-root-primitives.toon`](invalid/multiple-root-primitives.toon) – Multiple depth-0 primitives
72
+ - Two non-key-value lines at root depth
73
+ - Spec: §5, §14.2
74
+
75
+ - [`invalid/delimiter-mismatch.toon`](invalid/delimiter-mismatch.toon) – Header delimiter mismatch
76
+ - Declares pipe delimiter in brackets (`[N|]`) but uses comma-separated fields (`{a,b}`)
77
+ - Spec: §6
78
+
79
+ For reference test fixtures, see the [`tests/`](../tests/) directory.
@@ -0,0 +1,19 @@
1
+ {
2
+ "environments": {
3
+ "production": {
4
+ "region": "eu-central-1",
5
+ "replicas": 6,
6
+ "debug": false
7
+ },
8
+ "staging": {
9
+ "region": "eu-central-1",
10
+ "replicas": 2,
11
+ "debug": true
12
+ },
13
+ "development": {
14
+ "region": "local",
15
+ "replicas": 1,
16
+ "debug": true
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,4 @@
1
+ environments[3:]{region,replicas,debug}:
2
+ production: eu-central-1,6,false
3
+ staging: eu-central-1,2,true
4
+ development: local,1,true
@@ -0,0 +1,28 @@
1
+ {
2
+ "orders": [
3
+ {
4
+ "id": 1,
5
+ "customer": {
6
+ "name": "Ada",
7
+ "country": "DK"
8
+ },
9
+ "total": 99
10
+ },
11
+ {
12
+ "id": 2,
13
+ "customer": {
14
+ "name": "Bob",
15
+ "country": "UK"
16
+ },
17
+ "total": 149
18
+ },
19
+ {
20
+ "id": 3,
21
+ "customer": {
22
+ "name": "Cleo",
23
+ "country": "FR"
24
+ },
25
+ "total": 249
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,4 @@
1
+ orders[3]{id,customer{name,country},total}:
2
+ 1,Ada,DK,99
3
+ 2,Bob,UK,149
4
+ 3,Cleo,FR,249
@@ -0,0 +1,22 @@
1
+ {
2
+ "users": [
3
+ {
4
+ "id": 1,
5
+ "name": "Ada",
6
+ "role": "admin",
7
+ "active": true
8
+ },
9
+ {
10
+ "id": 2,
11
+ "name": "Bob",
12
+ "role": "developer",
13
+ "active": true
14
+ },
15
+ {
16
+ "id": 3,
17
+ "name": "Cleo",
18
+ "role": "designer",
19
+ "active": false
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,4 @@
1
+ users[3]{id,name,role,active}:
2
+ 1,Ada,admin,true
3
+ 2,Bob,developer,true
4
+ 3,Cleo,designer,false
@@ -0,0 +1,3 @@
1
+ items[2|]{a,b}:
2
+ 1|2
3
+ 3|4
@@ -0,0 +1,3 @@
1
+ items[3]{id,name}:
2
+ 1,Ada
3
+ 2,Bob
@@ -0,0 +1,2 @@
1
+ id 123
2
+ name Ada
@@ -0,0 +1,5 @@
1
+ # Weekly export, generated 2025-01-15
2
+ users[2]{id,name,role}:
3
+ # comment lines are stripped before row counting
4
+ 1,Ada,admin
5
+ 2,Bob,user
@@ -0,0 +1,5 @@
1
+ rows[1|]{id|value}:
2
+ 1|a,b
3
+
4
+ items[1|]:
5
+ - note: "a,b"
@@ -0,0 +1,5 @@
1
+ items[3]:
2
+ - 1
3
+ - a: hello
4
+ b: world
5
+ - text value
@@ -0,0 +1,9 @@
1
+ user:
2
+ id: 123
3
+ name: Ada Lovelace
4
+ contact:
5
+ email: ada@example.com
6
+ phone: +1-555-0100
7
+ settings:
8
+ theme: dark
9
+ notifications: true
@@ -0,0 +1,6 @@
1
+ id: 123
2
+ name: Ada Lovelace
3
+ active: true
4
+ email: ada@example.com
5
+ score: 98.5
6
+ nickname: null
@@ -0,0 +1,3 @@
1
+ items[2|]{sku|name|qty|price}:
2
+ A1|Widget|2|9.99
3
+ B2|Gadget|1|14.5
@@ -0,0 +1,3 @@
1
+ tags[3]: admin,ops,dev
2
+ numbers[5]: 1,2,3,4,5
3
+ empty: []
@@ -0,0 +1,3 @@
1
+ items[2 ]{sku name qty price}:
2
+ A1 Widget 2 9.99
3
+ B2 Gadget 1 14.5