Raw JSON report machine-readable
{
"data": {
"repo": {
"topics": [],
"is_fork": false,
"size_kb": 27880,
"has_wiki": false,
"homepage": null,
"languages": {
"HTML": 937,
"Ruby": 586252,
"Shell": 2785
},
"pushed_at": "2026-07-15T02:30:48Z",
"created_at": "2008-06-03T01:11:36Z",
"owner_type": "User",
"updated_at": "2026-07-21T10:44:37Z",
"description": "The PDF::Reader library implements a PDF parser conforming as much as possible to the PDF specification from Adobe.",
"is_archived": false,
"is_disabled": false,
"license_spdx": "MIT",
"default_branch": "main",
"license_spdx_raw": "MIT",
"primary_language": "Ruby",
"significant_languages": [
"Ruby"
]
},
"owner": {
"blog": "http://yob.id.au",
"name": "James Healy",
"type": "User",
"login": "yob",
"company": null,
"location": "Melbourne, Australia",
"followers": 236,
"avatar_url": "https://avatars.githubusercontent.com/u/8132?v=4",
"created_at": "2008-04-22T07:49:16Z",
"is_verified": null,
"public_repos": 143,
"account_age_days": 6664
},
"license": {
"state": "standard",
"spdx_id": "MIT",
"raw_spdx": "MIT",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "v2.15.1",
"kind": "patch",
"published_at": "2025-12-28T02:03:22Z"
},
{
"tag": "v2.15.0",
"kind": "minor",
"published_at": "2025-08-13T12:14:34Z"
},
{
"tag": "v2.14.1",
"kind": "patch",
"published_at": "2025-02-04T00:11:38Z"
},
{
"tag": "v2.14.0",
"kind": "minor",
"published_at": "2025-01-29T02:59:05Z"
},
{
"tag": "v2.13.0",
"kind": "minor",
"published_at": "2024-11-02T00:33:54Z"
},
{
"tag": "v2.12.0",
"kind": "minor",
"published_at": "2023-12-26T01:27:58Z"
},
{
"tag": "v2.11.0",
"kind": "minor",
"published_at": "2022-10-26T11:13:32Z"
},
{
"tag": "v2.10.0",
"kind": "minor",
"published_at": "2022-05-11T22:49:11Z"
},
{
"tag": "v2.9.2",
"kind": "patch",
"published_at": "2022-02-20T00:07:15Z"
},
{
"tag": "v2.9.1",
"kind": "patch",
"published_at": "2022-02-04T10:52:37Z"
},
{
"tag": "v2.9.0",
"kind": "minor",
"published_at": "2022-01-24T13:01:24Z"
},
{
"tag": "v2.8.0",
"kind": "minor",
"published_at": "2021-12-28T12:17:48Z"
},
{
"tag": "v2.7.0",
"kind": "minor",
"published_at": "2021-12-13T13:07:44Z"
},
{
"tag": "v2.6.0",
"kind": "minor",
"published_at": "2021-11-12T12:08:55Z"
},
{
"tag": "v2.5.0",
"kind": "minor",
"published_at": "2021-06-06T00:02:18Z"
},
{
"tag": "v2.4.2",
"kind": "patch",
"published_at": "2021-01-28T11:42:39Z"
},
{
"tag": "v2.4.1",
"kind": "patch",
"published_at": "2020-09-24T04:08:29Z"
},
{
"tag": "v2.4.0",
"kind": "minor",
"published_at": "2019-11-21T12:58:09Z"
},
{
"tag": "v2.3.0",
"kind": "minor",
"published_at": "2019-11-07T03:00:40Z"
},
{
"tag": "v2.2.1",
"kind": "patch",
"published_at": "2019-07-27T13:26:30Z"
},
{
"tag": "v2.2.0",
"kind": "minor",
"published_at": "2018-12-18T11:01:05Z"
},
{
"tag": "v2.1.0",
"kind": "minor",
"published_at": "2018-02-15T00:02:51Z"
},
{
"tag": "v2.0.0",
"kind": "major",
"published_at": "2017-02-25T04:35:34Z"
},
{
"tag": "v1.4.1",
"kind": "patch",
"published_at": "2017-01-01T14:08:15Z"
},
{
"tag": "v1.4.0",
"kind": "minor",
"published_at": "2016-02-22T11:58:29Z"
},
{
"tag": "v1.3.3",
"kind": "patch",
"published_at": "2013-04-07T06:36:12Z"
},
{
"tag": "v1.3.2",
"kind": "patch",
"published_at": "2013-02-26T10:49:29Z"
},
{
"tag": "v1.3.1",
"kind": "patch",
"published_at": "2013-02-12T02:48:58Z"
},
{
"tag": "v1.3.0",
"kind": "minor",
"published_at": "2012-12-29T23:43:58Z"
},
{
"tag": "v1.2.0",
"kind": "minor",
"published_at": "2012-08-30T14:20:56Z"
},
{
"tag": "v1.1.1",
"kind": "patch",
"published_at": "2012-05-09T06:06:18Z"
},
{
"tag": "v1.1.0",
"kind": "minor",
"published_at": "2012-03-25T03:49:49Z"
},
{
"tag": "v1.0.0",
"kind": "major",
"published_at": "2012-01-16T11:01:44Z"
},
{
"tag": "v0.10.1",
"kind": "patch",
"published_at": "2011-10-20T11:57:51Z"
},
{
"tag": "v0.10.0",
"kind": "minor",
"published_at": "2011-07-06T09:55:22Z"
},
{
"tag": "v0.9.3",
"kind": "patch",
"published_at": "2011-07-02T03:55:17Z"
},
{
"tag": "v0.9.2",
"kind": "patch",
"published_at": "2011-04-24T02:16:01Z"
},
{
"tag": "v0.9.1",
"kind": "patch",
"published_at": "2010-12-21T01:43:36Z"
},
{
"tag": "v0.9.0",
"kind": "minor",
"published_at": "2010-11-20T02:49:14Z"
},
{
"tag": "v0.8.6",
"kind": "patch",
"published_at": "2010-08-26T17:30:25Z"
},
{
"tag": "v0.8.5",
"kind": "patch",
"published_at": "2010-04-11T01:33:08Z"
},
{
"tag": "v0.8.4",
"kind": "patch",
"published_at": "2010-03-29T09:15:04Z"
},
{
"tag": "v0.8.3",
"kind": "patch",
"published_at": "2010-02-14T04:43:47Z"
},
{
"tag": "v0.8.2",
"kind": "patch",
"published_at": "2010-01-01T08:45:44Z"
},
{
"tag": "v0.8.1",
"kind": "patch",
"published_at": "2009-11-27T06:18:34Z"
},
{
"tag": "v0.8.0",
"kind": "minor",
"published_at": "2009-11-19T14:40:43Z"
},
{
"tag": "v0.7.7",
"kind": "patch",
"published_at": "2009-09-11T09:01:21Z"
},
{
"tag": "v0.7.6",
"kind": "patch",
"published_at": "2009-08-28T01:56:46Z"
},
{
"tag": "v0.7.5",
"kind": "patch",
"published_at": "2008-08-27T00:12:08Z"
},
{
"tag": "v0.7.4",
"kind": "patch",
"published_at": "2008-08-07T03:42:09Z"
},
{
"tag": "v0.7.3",
"kind": "patch",
"published_at": "2008-06-11T04:07:35Z"
},
{
"tag": "v0.7.2",
"kind": "patch",
"published_at": "2008-05-20T07:25:24Z"
},
{
"tag": "v0.7.1",
"kind": "patch",
"published_at": "2008-05-11T12:02:40Z"
},
{
"tag": "v0.7.0",
"kind": "minor",
"published_at": "2008-05-06T05:09:34Z"
},
{
"tag": "v0.6.2",
"kind": "patch",
"published_at": "2008-03-22T06:10:13Z"
},
{
"tag": "v0.6.1",
"kind": "patch",
"published_at": "2008-03-21T09:25:41Z"
},
{
"tag": "v0.5.1",
"kind": "patch",
"published_at": "2008-01-01T12:07:47Z"
},
{
"tag": "0.1.1",
"kind": "patch",
"published_at": "2011-07-06T10:06:09Z"
},
{
"tag": "0.1.0",
"kind": "minor",
"published_at": "2011-07-04T14:26:48Z"
},
{
"tag": "0.0.6",
"kind": "patch",
"published_at": "2011-04-26T01:39:49Z"
},
{
"tag": "0.0.4",
"kind": "patch",
"published_at": "2011-04-24T02:24:24Z"
},
{
"tag": "0.0.3",
"kind": "patch",
"published_at": "2011-04-07T08:06:14Z"
},
{
"tag": "0.0.2",
"kind": "patch",
"published_at": "2011-04-01T05:46:04Z"
},
{
"tag": "0.0.1",
"kind": "patch",
"published_at": "2011-03-27T15:18:20Z"
}
],
"recent_commits": [
{
"oid": "578db3d49b9cfc458b186915896ae29f62ccd126",
"body": "In some rare situations, Encoding#to_utf8 was returning invalid utf8\nstrings. That's not the goal, pdf-reader should always return valid utf8\nwhen it claims to. The problem is that we can't always trust the\ndifference tables pdf producing apps provide. In this particular case,\nwe can't blindly take \n[…]\n was partially fixed in #604, but adding the integration spec\nexposed that a further fix was required.\n\nI don't love the style here, but the test is green so I'll commit it. I\ncan tidy the code later.",
"is_bot": false,
"headline": "Avoid returning invalid utf8 from Encoding#to_utf8 (#605)",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-07-15T02:30:46Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "48278303b004f3324f5cefd100b80f88b6277d62",
"body": "In some rare situations, Encoding#to_utf8 was returning invalid utf8 strings.\nThat's not the goal, pdf-reader should always return valid utf8 when it claims\nto. The problem is that we can't always trust the difference tables pdf\nproducing apps provide. In this particular case, we can't blindly take\n\n[…]\n was partially fixed in #604, but adding the integration spec exposed\nthat a further fix was required.\n\nI don't love the style here, but the test is green so I'll commit it. I can\ntidy the code later.",
"is_bot": false,
"headline": "Avoid returning invalid utf8 from Encoding#to_utf8",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-07-15T02:24:34Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e57131e391224cbc6cc13ede349304247df88b4c",
"body": null,
"is_bot": false,
"headline": "Merge branch 'pr/604'",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-07-15T01:11:03Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1b69da828ab62da89197bf5050a14d91d980ed98",
"body": null,
"is_bot": false,
"headline": "coerce this unpacked value into an integer to keep sorbet happy",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-07-15T01:10:26Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fea46b5272d445fb12fc767257924c7449589381",
"body": "…unknown-char box\n\nSigned-off-by: Sai Asish Y <say.apm35@gmail.com>",
"is_bot": false,
"headline": "Replace invalid Unicode code points from differences tables with the …",
"author_name": "Sai Asish Y",
"author_login": "SAY-5",
"committed_at": "2026-07-10T20:22:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "62cd78c2d8fee6c9554dadeceff04ee99e0b8361",
"body": "…lation! (#601)\n\n`process_glyph_displacement` was allocating a new `TransformationMatrix`\non every character painted. Adding `prepend_translation!`` allows\nin-place mutation\n\nAlso applied to `move_text_position` to save an allocation per\ntext-position move.\n\nA small reduction in object allocations w\n[…]\ntex.pdf\n pdflatex 35.344M memsize ( 160.000 retained)\n 433.088k objects ( 4.000 retained)\n 50.000 strings ( 2.000 retained)\n```",
"is_bot": false,
"headline": "Eliminate TransformationMatrix allocation per glyph via prepend_trans…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:36:06Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "2e6cf0433a5f1bab164ead4b17223f1c742f929b",
"body": "…lation!\n\n`process_glyph_displacement` was allocating a new `TransformationMatrix` on every character painted.\nAdding `prepend_translation!`` allows in-place mutation\n\nAlso applied to `move_text_position` to save an allocation per text-position move.",
"is_bot": false,
"headline": "Eliminate TransformationMatrix allocation per glyph via prepend_trans…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:33:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e87095b0641f79e1b87c8626aca4af195359576d",
"body": "…(#600)\n\n`\"#{id}:#{gen}\".hash` created a String on every Hash key lookup. Use\n`(id << 16) ^ gen` for a collision-resistant integer hash with zero\nallocations.\n\nA small reduction in object allocations when run on MRI 3.4.9:\n\n* `cairo-unicode.pdf`: 179k -> 174k allocations (~2% reduction)\n* `pdflatex.\n[…]\ntex.pdf\n pdflatex 41.972M memsize ( 160.000 retained)\n 515.939k objects ( 4.000 retained)\n 50.000 strings ( 2.000 retained)\n```",
"is_bot": false,
"headline": "Eliminate String allocation in Reference#hash via integer arithmetic …",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:27:43Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9d90633a11e0188a13978cb323c9e589b790ad28",
"body": "\"#{id}:#{gen}\".hash created a String on every Hash key lookup.\nUse (id << 16) ^ gen for a collision-resistant integer hash with zero\nallocations.",
"is_bot": false,
"headline": "Eliminate String allocation in Reference#hash via integer arithmetic",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:25:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a5457a11d5c8542e4abeecfa18fa8214056b0eae",
"body": "…eam op (#599)\n\n`parse_token` was calling `Token.new(token)` for every operator\nrecognised in the operators hash. Since PDF content streams repeat a\nsmall fixed set of operators (Tj, BT, ET, cm, Td, …) many times, a\nclass-level `INTERNED_TOKENS` hash now caches each unique Token after it\nis created \n[…]\ntex.pdf\n pdflatex 42.562M memsize ( 160.000 retained)\n 530.697k objects ( 4.000 retained)\n 50.000 strings ( 2.000 retained)\n```",
"is_bot": false,
"headline": "Intern operator Token objects to avoid one allocation per content-str…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:20:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "aeedf61395c89480cec57d4b7b0a4121606b214e",
"body": "…eam op\n\nparse_token was calling Token.new(token) for every operator recognised in the operators hash. Since\nPDF content streams repeat a small fixed set of operators (Tj, BT, ET, cm, Td, …) many times, a\nclass-level INTERNED_TOKENS hash now caches each unique Token after it is created the first time.",
"is_bot": false,
"headline": "Intern operator Token objects to avoid one allocation per content-str…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:16:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d1a6ab3cd120be95bd296043608fbef3aeb7d0a2",
"body": "A variety of tweaks to text extraction related code to allocate fewer\nobjects. The difference is significant, the below benchmarks were run on\nMRI 3.4.9:\n\n* `cairo-unicode.pdf`: 305k -> 182k allocations (~40% reduction)\n* `pdflatex.pdf`: 731K -> 540K allocations (~26% reduction)\n* `prince1.pdf`: 59k\n[…]\ntex.pdf\n pdflatex 42.946M memsize ( 0.000 retained)\n 540.417k objects ( 0.000 retained)\n 50.000 strings ( 0.000 retained)\n```",
"is_bot": false,
"headline": "Reduce object allocations in various text extraction related code (#598)",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:09:57Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "4501d5aa54de69a48275b2bf57f5b1962d00be85",
"body": "Multiple fonts often share the same encoding (e.g. StandardEncoding). Previously each Encoding\ninstance would read and parse the same file, allocating hundreds of objects per load. Cache the built\nHash per file path in FILE_MAPPINGS, and just dup the cached result for each instance.",
"is_bot": false,
"headline": "Cache parsed encoding file mappings to avoid repeated file loads",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:07:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ea4950b9dfa12e862e0cf194cea196e0eab67cd5",
"body": "`_m, single_byte, unicode = *l.match(...)` created 2 Arrays per line,\n`(MatchData#to_a + nil splat)` and `\"0x#{...}\"` interpolated 2 Strings per matching line.\nUse `=~` with $1/$2 to eliminate these allocs.",
"is_bot": false,
"headline": "Eliminate splat Array and hex interpolations in Encoding#load_mapping",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:05:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3e158fd5843160ffbcb6226e691a8b202f200601",
"body": "rect.contains?(run.origin) was calling TextRun#origin, which lazily allocates a Point per run.\nPrecompute the rect bounds once and compare run.x/run.y directly",
"is_bot": false,
"headline": "Avoid Point allocation per TextRun in BoundingRectangleRunsFilter",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:05:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "79c0b382b5ade874b52f297f375d12293f5e2e94",
"body": "…oding\n\nEach Encoding#initialize called default_mapping which created 256 two-element [key,value] arrays to\npass to Hash[tuples]. Replace with a frozen class-level constant built once via each_with_object,\nand just dup it in initialize.",
"is_bot": false,
"headline": "Precompute DEFAULT_MAPPING constant to avoid 256 Array allocs per Enc…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:05:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a3a1dff4f60c76a3a6efb67f88f6e0919237e836",
"body": "…ring_insert\n\nRange.new(index, end) allocated a Range object and String.new(needle) made an unnecessary copy for\nevery text run placed during to_s. Using the str[start, length]= slice form avoids both.",
"is_bot": false,
"headline": "Eliminate Range and String allocs per text run in PageLayout#local_st…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:05:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ae09787ef0ac6a08b9917623e92ff9d840be2648",
"body": "For CMap-mapped fonts (Identity-H, etc.) called with Integer glyph codes, to_utf8_via_cmap always\nends with cmap.decode(code).pack(\"U*\") — allocating a new String per call. Since glyph codes repeat\nheavily within a page, memoize the result in a per-font Hash to pay the pack cost only once per\nunique code.",
"is_bot": false,
"headline": "Cache UTF-8 strings per glyph code in Font#to_utf8 for CMap-mapped fonts",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T13:05:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f9bb002fbaa0e342508d13339d87df0c83f89cab",
"body": "…EPOINTS\n\nHash#fetch(key, default) evaluates the default eagerly, so @map.fetch(c, []) was allocating a\nthrowaway [] on every decode call — even on cache hits. Replacing the default with a frozen constant\neliminates one Array per glyph in any CMap-mapped font.",
"is_bot": false,
"headline": "Avoid Array allocation on every CMap#decode call via frozen EMPTY_COD…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:50:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fafe48cad6e026504e490c1f8de2bc1b20a8ad2d",
"body": "…font_size churn\n\nto_utf8_via_cmap was wrapping cmap.decode (already an Array) in another Array then flattening it — 2\nneedless allocations per glyph. Replaced with direct .pack and flat_map.\n\nAlso stopped invalidating @font_size per glyph: font_size depends only on trm.b/trm.d (scale/rotation),\nnot trm.e/f (translation) which is all that changes between glyphs.",
"is_bot": false,
"headline": "Eliminate 2 wasted Array allocs per glyph in to_utf8_via_cmap; stop @…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:49:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ad65f9014288c40ad9ca9397ba6891e1be9a79b6",
"body": "Every TextRun was eagerly allocating a Point for @origin, but the hot path\nonly accesses x and y directly. Now x/y are stored as plain numerics and\nthe Point is only created on demand (by bounding-rect filters and tests).",
"is_bot": false,
"headline": "Make TextRun#origin lazy to avoid allocating a Point per character",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:49:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "82cb9c3dab77e46dd31d6aba6adf9ffffc1a794e",
"body": "Specs are green on both, lets keep them that way",
"is_bot": false,
"headline": "Add jruby 10.1 and truffelruby 34 to the build matrix (#597)",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:31:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1ce7e26508828df0af59daefd1eea6a7f2f6557a",
"body": null,
"is_bot": false,
"headline": "Add jruby 10.1 and truffelruby 34 to the build matrix",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:29:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "93fc04a45d0b31ab279b62d29e43e6237fa15a0b",
"body": "A variety of tweaks to this class to allocate fewer objects. The\ndifference is modest, mostly depending on how much the PDF uses hex\nstrings and names with `#xx` format. The below benchmarks were run on\nMRI 3.4.9:\n\n* `cairo-unicode.pdf`: 326k -> 305k allocations (~6% reduction)\n* `pdflatex.pdf`: 744\n[…]\ntex.pdf\n pdflatex 46.900M memsize ( 0.000 retained)\n 731.675k objects ( 0.000 retained)\n 50.000 strings ( 0.000 retained)\n```",
"is_bot": false,
"headline": "Reduce object allocations in PDF::Reader::Parser (#596)",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:23:18Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ae0b737682e3ac62643399177b5a5a1d5b85e2fc",
"body": "prepare_hex_token already accumulates all hex chars as one token, so hex_string's str=\"\".dup + str<<token\nloop was an unnecessary copy. Use the token directly.\n\nAlso replace [str].pack('H*') with a cached @hex_pack_buffer=[\"\"] array to eliminate 9,624 Array\nallocations per cairo-unicode.pdf parse.",
"is_bot": false,
"headline": "Eliminate Array+String allocs in hex_string by reusing pack buffer",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:21:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a6561dda1a1d6e053db4578e875cc759a2a445f5",
"body": "The vast majority of PDF names contain no #xx sequences, so dup+gsub was\nallocating two strings unnecessarily. Now we only gsub when '#' is present.",
"is_bot": false,
"headline": "Skip dup+gsub in pdf_name when no hex escape sequences present",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:07:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0d928231d753a6f41c5d4020036f5b371fc7d71a",
"body": "Every numeric token (very common) was triggering a MatchData allocation via =~.\nSwitching to match?() avoids that object entirely.\n\nWe need a shim so the code continues to work on rubies 2.1 -> 2.3, and we don't avoid the allocation\nthere. That's OK though, compatibility is the goal on those old versions",
"is_bot": false,
"headline": "Use match? instead of =~ in parse_token to avoid MatchData allocation",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T12:07:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2dd38b9d3b1794fffe96e3631d89c8807d1fb4a6",
"body": "A variety of tweaks to this class to make it allocate fewer objects. The\ndifference is significant, the below benchmarks were run on MRI 3.4.9:\n\n* `cairo-unicode.pdf`: 486k -> 326k allocations (~32% reduction)\n* `pdflatex.pdf`: 936K -> 744K allocations (~20%)\n* `prince1.pdf`: 162k -> 74K allocations\n[…]\n000 retained) \n 744.293k objects ( 0.000 retained) \n 50.000 strings ( 0.000 retained)\n```",
"is_bot": false,
"headline": "Reduce object allocations in PDF::Reader::Buffer (#595)",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:58:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "427338611242d2061510a964fb18fc0bfda4f1c2",
"body": null,
"is_bot": false,
"headline": "Make changes in c2fb182 work on rubies 2.1 -> 2.3",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:56:07Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "640e7518bdcf27fd5314016eaace5dc2fedac5c7",
"body": "…llocs\n\n`when *TOKEN_WHITESPACE` expands the array via splat on every loop iteration,\nallocating a new Array each time. Inlining the 6 byte values directly into\nthe when clause eliminates ~105k Array allocations per cairo-unicode.pdf parse.",
"is_bot": false,
"headline": "Inline TOKEN_WHITESPACE bytes in case/when to eliminate splat Array a…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:40:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4e2ab1afd817fa490952611a07326cb07448260a",
"body": "…e_hex_token\n\nWhen a hex string closes normally with '>' (0x3E), byte.chr was allocating\na new String object. Branch explicitly on 0x3E to push the frozen '>' literal\ninstead; the rare non-'>' delimiter path keeps byte.chr.",
"is_bot": false,
"headline": "Avoid byte.chr String allocation on normal hex string close in prepar…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:40:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "249708030ec75e92460ea575e6c9a2dddfd670cc",
"body": "The '/' case in prepare_regular_token was building a new Array on every\nPDF name token to check whether the next byte terminates the name.\nNAME_TERMINATOR_BYTES is computed once at class load time instead.",
"is_bot": false,
"headline": "Hoist [nil, 0x20, 0x0A] + TOKEN_DELIMITER array to a frozen constant",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:39:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4f7f90ab2c9d9d88d7fceb140db0efa2cb322aec",
"body": "…ular_token\n\nRestructured the nil (EOF) case to push tok inline, and removed the trailing\n@tokens << tok guard. This lets us drop 10 unnecessary tok = \"\".dup resets\nthat fired whenever a non-empty token was followed by a delimiter.",
"is_bot": false,
"headline": "Eliminate dead tok = \"\".dup allocs per delimiter token in prepare_reg…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:39:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d785a7475d7fcc1f8a1a9a3d59fd462cfd377190",
"body": "…prepare_regular_token\n\nbyte.chr allocates a new String per call; frozen string literals in a\nfrozen_string_literal: true file are interned and reused across calls.",
"is_bot": false,
"headline": "Replace byte.chr with frozen string literals for delimiter tokens in …",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:39:42Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3d743520b33141d4eb40bd63aad2e71f2edbd88d",
"body": "…empty\n\nWhen hitting a delimiter with an empty tok accumulator, we were always allocating\na new empty string. Now we only allocate when tok had content and was pushed to\n@tokens (and thus can't be reused).",
"is_bot": false,
"headline": "Avoid \"\".dup allocation in prepare_regular_token when tok is already …",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:39:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c2fb18234174716b8e33ec1c35ba91dfed12b211",
"body": "…hData allocation\n\nCalled on nearly every token pair, .match() was allocating a MatchData object each time.\nSwitching to .match?() returns true/false without allocating.",
"is_bot": false,
"headline": "Use match? instead of match in merge_indirect_reference to avoid Matc…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:39:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f8e802248db2bab7f7a09805139dee2b8508e1a4",
"body": "add memory benchmarking script",
"is_bot": false,
"headline": "Merge pull request #594 from yob/allocations-benchmark",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:32:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ca4396f7fded76f82b2bfe4dc2173aebdd6b41e5",
"body": "I'm interested in benchmarking how many objects pdf-reader allocates while parsing various files,\nand I'll use this in my experiments",
"is_bot": false,
"headline": "add memory benchmarking script",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-05-03T11:29:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7473650301f81393d4dc41a814e7121e59e0b461",
"body": "Adjust the way CMaps with def operators are handled (592 witrh tweaks)",
"is_bot": false,
"headline": "Merge pull request #593 from yob/592-with-tweaks",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-29T12:25:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "52b531369f385d97915951692a26277c48f5e11b",
"body": "In #592 (see previous commit) a fix for handing CMap parsing was provided.\nIt works well and the new test coverage was excellent, but on style grounds\nI wanted to tweak the solution a bit.\n\nI'd prefer not to give Parser an API that introduces temporal coupling. That\nis, the behaviour of the class wi\n[…]\nr that's needed to parse postscript CMaps. At least, it's not needed\nfor the test file we have.\n\nFinally, I fixed a few long lines while I was in their vicinity, so the can\nmax_violations has reduced.",
"is_bot": false,
"headline": "Adjust the way CMaps with `def` operators are handled",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-29T12:22:09Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "aa5ca9697428e60151877f354b046f770ec86303",
"body": null,
"is_bot": false,
"headline": "Add support for PostScript def keywords in CMap parsing",
"author_name": "Andrew Roberts",
"author_login": "andrew-roberts-lp",
"committed_at": "2026-04-23T22:24:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a417864f7363a8b6261e0c5416655fab4b5a089f",
"body": null,
"is_bot": false,
"headline": "Merge branch 'pr/591'",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-08T11:14:26Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "197b8a2b162057173fcca0d0fbf590cf0003f366",
"body": null,
"is_bot": false,
"headline": "Allow one extra cane violation",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-08T11:11:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "26f0232312d407623242ea5dabd6a9db0bd59436",
"body": "The existing workaround for PDFs that start numbering at 1 instead of 0\nincorrectly triggers when object 1 is a legitimate free entry within an\nxref section that correctly starts at 0. When triggered, it resets the\nobject counter to 0, causing all subsequent object IDs to be off by one.\nThis makes p\n[…]\n\nsection header started at 1. On subsequent entries, objid == 1 means\nit was incremented from 0 and object 1 is a real free entry.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
"is_bot": false,
"headline": "Fix xref objid reset when object 1 is a legitimate free entry",
"author_name": "Micah Buckley-Farlee",
"author_login": "micahbf",
"committed_at": "2026-04-07T15:40:40Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "78285f58ba676a48f59453479b64fa70496377d8",
"body": "Support ActualText in marked content for text extraction",
"is_bot": false,
"headline": "Merge pull request #587 from 55728/support-actual-text-in-marked-content",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-06T01:27:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c36bb9ab83725fde9afa4e510bdcf6a7e18f5647",
"body": "When ActualText is present in a BDC/EMC marked content span, use it instead of ToUnicode-derived text for text extraction.\nThis handles PDFs where font feature substitutions (e.g. tnum) produce incorrect ToUnicode CMap entries.\n\nUses EncodingUtils.string_to_utf8 for ActualText encoding conversion per PDF 32000-1:2008 §14.9.4.\n\nFixes #523",
"is_bot": false,
"headline": "Support ActualText in marked content for text extraction",
"author_name": "Kenta Ishizaki",
"author_login": "55728",
"committed_at": "2026-04-05T06:17:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2a32edc341e754b02413c136d527c22c3df91604",
"body": "Improve UTF8 conversion of strings outside content streams",
"is_bot": false,
"headline": "Merge pull request #590 from yob/encoding-replacement-character",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T01:20:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18344a187c4d31a27154ff6c720e12189e5d1bec",
"body": null,
"is_bot": false,
"headline": "ensure all returned strings have a valid encoding",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T01:18:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0d5407a157dee832ba8da2bac8e1a19907277eea",
"body": "PDF::Reader::EncodingUtils is used to convert strings outside content streams into\nUTF8. The input strings are either UTF16 (most common in 2026), or PDFDocEncoding.\n\nFor the latter we have general purpose mapping of content stream strings in PDF::Reader::Encoding,\nhowever we weren't correctly handing a variety of special mappings in strings\noutside content streams.\n\nThis fixes that, meaning we'll correctly convert characters like the EURO in the pdf\nmetadata of older PDFs that ue PDFDocEncoding",
"is_bot": false,
"headline": "Improve conversion of PDFDocEncoding strings outside content streams",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T01:18:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1295634456f0fd8f391605462348ce142cfd8f40",
"body": "When converting a UTF16 string to UTF8, replace invalid bytes with `�` rather than\nraise an exception.\n\nThe vast majority of PDFs will have valid UTF16 strings, and I don't think it's worth\nbreaking the control flow of pdf-reader consumers by raising an exception",
"is_bot": false,
"headline": "Improve handling of UTF16 strings with invalid bytes",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T01:12:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b5d18921f62307fe22a48098c4fd081becb87fb9",
"body": "Small improvements to EncodingUtils",
"is_bot": false,
"headline": "Merge pull request #589 from yob/text-encoding-improvements",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T00:29:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e0e30baed4908c514a5e6e7b1631590ccef9a109",
"body": "* reduce allocations by saving the UTF16 BOM as a constant\n* reduce allocations by using a constant identifier for utf8 rather than a string\n* Use ruby's built-in tools to convert from UTF16 to UTF8. pdf-reader used to maintain\n compatibility with ruby 1.8 so we were unable to use `String#encode()`, but now that\n the minimum support ruby is 2.1, we can",
"is_bot": false,
"headline": "Small improvements to EncodingUtils",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T00:26:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ec3d4eca106eabe8d2979d9cde54d12c84302ef9",
"body": "extract text encoding utilities to a dedicated class with specs",
"is_bot": false,
"headline": "Merge pull request #588 from yob/extract-encoding-utils",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T00:08:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ce3beffa870f778849a24c70e8a3bd6d540ae13c",
"body": "I think we'll find these useful in other places, so I'm preemptively extracting them to somewhere\nthey can be called by other code",
"is_bot": false,
"headline": "extract text encoding utilities to a dedicated class with specs",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-04-05T00:05:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "83cbcb77a992cd3bcd538b40c28172b74cd3cd7b",
"body": "Update truffleruby specs from 33.0.0 -> 33.0.1",
"is_bot": false,
"headline": "Merge pull request #584 from yob/truffleruby-33-0-1",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-01-22T00:49:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d3b9e4dce85eb6fc57a04e7437d45eeb75c15365",
"body": "In #583 we added truffleruby to the CI matrix, and in the process uncovered an upstream bug:\n\nhttps://github.com/truffleruby/truffleruby/issues/4106\n\nThat bug is now fixed in 33.0.1, so bump CI to use that.",
"is_bot": false,
"headline": "Update truffleruby specs from 33.0.0 -> 33.0.1",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-01-22T00:45:08Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c5d57e0d4e06855954072d9f1ec154f75e4669f",
"body": "Add truffleruby to the CI matrix",
"is_bot": false,
"headline": "Merge pull request #583 from yob/truffleruby",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-01-19T04:25:46Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e381b1b7e2b8a8cb29a3f71876e2fd731a654760",
"body": "soft fail is fine, lets just see if it runs. I couldn't find a reasonable image\nto use on docker hub, so I've built a custom one using rbenv. Seems rbenv\nis a supported way to install truffleruby based on: https://truffleruby.dev/blog/truffleruby-33-is-released\n\nI had to reorder the way we parse invalid PDFs in specs. Calling `Page#fonts`\non one particular PDF triggered a regexp bug inside truffleruby",
"is_bot": false,
"headline": "Add truffleruby to the CI matrix",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-01-19T04:21:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e57c8f1b15cf689183d4a85828975e5f452c4ec0",
"body": "Expand test coverage for PDF::Reader::CMap",
"is_bot": false,
"headline": "Merge pull request #582 from yob/more-cmap-tests",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-01-18T08:22:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a18ed95cbdd886b70e6c43f4f7cd50323d9e4615",
"body": ".. and fix one bug along the way.\n\nA draft of the missing test coverage was generated by amp, and it identified\nthe bug. The proposed test structure didn't work for me though, so I significantly\nrefactored",
"is_bot": false,
"headline": "Expand test coverage for PDF::Reader::CMap",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2026-01-18T08:19:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b58039dd03921d3cac21386a0ffad17b925247e3",
"body": "Dynamically generate external RBS signatures at gem build time",
"is_bot": false,
"headline": "Merge pull request #580 from yob/external-rbs-in-gem",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T22:34:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "113938fc6b9871714ef8ecc18d06c8667a2a7c72",
"body": "We recently converted our type annotations to inline RBS comments, while\ncontinuing to use sorbet tooling for type checking and IDE integration, and continuing\nto distribute sorbet type signatures in the gem (as an extenral RBI file).\n\nThe RBS ecosystem doesn't natively support inline comments yet, \n[…]\nfer RBS tooling by distributing the gem with RBS style type annotations in\nan external RBS file.\n\nThe location of the sig files follows the docs in\nhttps://github.com/ruby/rbs/blob/v3.10.0/docs/gem.md",
"is_bot": false,
"headline": "Dynamically generate external RBS signatures at gem build time",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T22:26:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7aa78cf8229598340fbe0c4829e8580338886350",
"body": null,
"is_bot": false,
"headline": "a few comment fixes",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T13:24:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d95588080492be26af455a2896caf521ad31162b",
"body": null,
"is_bot": false,
"headline": "a few more type annotations",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T13:13:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bcaa421d4ef89337ccb6cc1d82001dbd0fbde037",
"body": "Bump sorbet to the latest version",
"is_bot": false,
"headline": "Merge pull request #579 from yob/bump-sorbet",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T12:56:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2e7a9f79fb91399a568ed3f7cff2ace5c7551e13",
"body": "... and make a couple of required fixes",
"is_bot": false,
"headline": "Bump sorbet to the latest version",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T12:54:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1ab4d38dcd0488509a0642fa27e4b87a01fbc259",
"body": "Use Point class rather than [x,y] Arrays in more places",
"is_bot": false,
"headline": "Merge pull request #578 from yob/more-points",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T12:33:03Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a250fffcda4eeb39250da903e1afc135a0be0fc6",
"body": "As I roll type hints out more broadly in this codebase, using `Array[Numeric]` to\nrepresent an (x,y) point is a hassle. When reading from an array like [0,1], the standard\nArray accessor methods can return a nil which makes sorbet unhappy and we need to\nstart adding guards to let sorbet know that th\n[…]\ntheir signature:\n\n* PageState#ctm_transform\n* PageState#ttm_transform\n\nInstead, I've soft deprecated them and added these alternatives:\n\n* PageState#ctm_transform_point\n* PageState#ttm_transform_point",
"is_bot": false,
"headline": "Use Point class rather than [x,y] Arrays in more places",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T12:30:49Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9e0b9349b378c37c11e2f72826a476becca635cc",
"body": "Update a few commands to default to ruby 3.4",
"is_bot": false,
"headline": "Merge pull request #577 from yob/ruby-bumps",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T07:19:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3ca767e378d6330a312644a38acd958229a729dc",
"body": "May as well use the next-to-latest ruby for:\n\n* running sorbet and cane in CI\n* as the default when running various commands locally in dev",
"is_bot": false,
"headline": "Update a few commands to default to ruby 3.4",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-29T07:04:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "57d8abe1195219346fe42b79d7b2f0245ea954a1",
"body": null,
"is_bot": false,
"headline": "Move unit specs into a folder structure that matches the code under test",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T02:20:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "544a01c097802bc94ff74567df18cbd8f56e5abf",
"body": "On the old version I started getting this error:\n\n $ bundle exec spoom srb sigs export\n ...\n Translating signatures from RBS to RBI...\n Injecting extend T::Sig to lib/pdf-reader.rb...\n Running Tapioca...\n Error: bundle exec tapioca gem pdf-reader --no-doc --no-loc --no-file-header \n[…]\nr/local/bundle/gems/tapioca-0.17.7/lib/tapioca/runtime/trackers/required_ancestor.rb:55:in '<module:Helpers>': undefined method 'new' for module T::Module (NoMethodError)\n prepend(Module.new do",
"is_bot": false,
"headline": "Update tapioca to the latest version",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T02:03:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "03ad935bc7ce5051f5caaed158ef658631112363",
"body": null,
"is_bot": false,
"headline": "prepare for release",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T01:55:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "14e7215f859ecebb3f7716bbeb5719338e9877f1",
"body": "Have another go at using gem.coop",
"is_bot": false,
"headline": "Merge pull request #576 from yob/gem.coop",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T01:47:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fa87d757f6754695e83a2b910f5b6ddea2e8dc07",
"body": "First attempted in f75b0ce, but reverted in 2540e5f because it wasn't working\non the (admittedtly ancient) jruby 9.1.\n\nTry again but only on the Gemfile used for specs on recent-ish rubies.",
"is_bot": false,
"headline": "Have another go at using gem.coop",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T01:44:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a9bfe1b8718cacf591dd2e1dafacbb4d34f345e1",
"body": "Add ruby 4.0 to the CI matrix",
"is_bot": false,
"headline": "Merge pull request #575 from yob/ruby-4-ci",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T01:39:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ae593218d85eb68916ebd6b6447c194e35123c6c",
"body": "v4.0 was released a few days ago: https://www.ruby-lang.org/en/news/2025/12/25/ruby-4-0-0-released/",
"is_bot": false,
"headline": "Add ruby 4.0 to the CI matrix",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-12-28T01:36:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3bc2510a10902949e8c9ff85786e17de2dec47fc",
"body": "Skip invalid UTF-16 surrogate pairs in CMaps",
"is_bot": false,
"headline": "Merge pull request #574 from yob/invalid-cmap-surrogate",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-11-30T04:05:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f0f1a618675d7c3c4a96161b46b6a6a8c6990462",
"body": "CMaps used as a ToUnicode mapping should only return valid Unicode codepoints,\nand the range 0xD800 -> 0xD8FF are not valid to return on their own. They\nindicate the start of a surrogate pair and should be followed by more hex chars.\n\nWhen they're not, lets just skip them from the mapping.\n\nAn existing spec had to be changed because it was asserting that an invalid\nsurrogate pair was being mapped.",
"is_bot": false,
"headline": "Skip invalid UTF-16 surrogate pairs in CMaps",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-11-30T04:03:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "42988a580bbbd817a043e0ad6d2e3049aba083f0",
"body": "Tounicode fix",
"is_bot": false,
"headline": "Merge pull request #573 from yob/tounicode-fix",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-11-30T02:51:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "25f5642b6fd25d128887599e5e9ccca9df1d8766",
"body": "I've added a test case for the previous commit - a PDF where ToUnicode is\na PDF Name rather than a Stream containing a CMap.\n\nWith that in place, I decided to fixc it in a slightly different way - attempting\nto dereference the value, and only then silently skipping loading ToUnicode\nif it's not the expected type.\n\nThis means that we'll also catch the case where ToUnicode is an indirect\nobject that points to a non-stream",
"is_bot": false,
"headline": "Solve non-stream ToUnicode in a slightly different way",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-11-30T02:49:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d833e2263e6f613261d4d0872712f661d98d4030",
"body": null,
"is_bot": false,
"headline": "Fix ToUnicode handling for symbolic fonts",
"author_name": "Sergey Krylov",
"author_login": "servn",
"committed_at": "2025-11-30T02:49:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2540e5f40f3ed29eb7d8768738f166f6f217f480",
"body": "This reverts commit f75b0ce392e5c7385b9f6b07f017796bc99a6dd5.\n\nIt was working great in CI, except on jruby 9.1 which throws an error when\nbundling:\n\n Could not fetch specs from https://gem.coop/ due to underlying error <Received\n fatal alert: handshake_failure (https://gem.coop/specs.4.8.gz)>\n\nFor now I just want the build to pass, so I'll revert and investigate later",
"is_bot": false,
"headline": "Revert \"Give gem.coop a go\"",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-11-30T02:48:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f75b0ce392e5c7385b9f6b07f017796bc99a6dd5",
"body": null,
"is_bot": false,
"headline": "Give gem.coop a go",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-11-07T05:16:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ecaa285abe818646e4a96b6d247d84d9606e2664",
"body": "Now that I need to generate the RBI file at release time, we need to install\ngems from the Gemfile. That means git, compiler, etc",
"is_bot": false,
"headline": "change release step to use a full ruby 3.4 image",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T12:14:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "99a55fc93e4853d53f9beb44ac74450aeaff3928",
"body": "It crashes if I have type definitions above the class like this, but works\nif I move it to be inside the class.\n\nSorta.\n\nIt no longer crashes, but the exported signatures for methods using these\ntypes in the sig come out as `untyped`. I can live with that for now, these\ntypes are definitely working with sorbet itself and ruby-lsp is detecting\nthem, it's just an RBI export issue",
"is_bot": false,
"headline": "Move back to an official release of spoom",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T12:10:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "61a80cdbff3602aa11ba4677f86184cc66280573",
"body": null,
"is_bot": false,
"headline": "We can go back to an upstream release of spoom now",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T12:06:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b457579278c02a1924cae8ebbfa4ec09a7b194ed",
"body": null,
"is_bot": false,
"headline": "Oops, I think I need to install spoom before using it here",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T12:04:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a899fee845de8cbd9e6c326a06a343142bb90aec",
"body": null,
"is_bot": false,
"headline": "prepare for relesae",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T12:01:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "435942eef4df235ae9893fb5a9f1415fd8bd4782",
"body": null,
"is_bot": false,
"headline": "remove unused config file for parlour",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T11:57:09Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b3155c08f887ea1b38052d014c1445a447c6fd16",
"body": "Avoid infinite recursion when a PDF has a loop in the ancestorsof a Page object",
"is_bot": false,
"headline": "Merge pull request #567 from yob/fix-stack-overflow-in-page-ancestors",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-13T11:48:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "64023495ff94796eb49911d08dd3218bea1732c9",
"body": "…age object\n\nEach page in a PDF is a single Object in a tree of Pages->Pages->Page objects, and\nthe page inherits properties from its ancestors right up the tree.\n\nWe had not protection against a malicious (or incompetently generated) PDF\nthat built an ancestor tree with a cyclic loop. As a result, \n[…]\n it, when walking up the tree to find all the ancestors keep a record\nof the ones we have seen and raise an error if we see one again.\n\nThanks to Yann @ Riema Labs for finding and reporting the issue.",
"is_bot": false,
"headline": "Avoid infinite recursion when a PDF has a loop in the ancestorsof a P…",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T10:57:21Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b26c5694d5c0f3ca80873545464308864384c3d9",
"body": "This is getting easier now that I can do the types inline.\n\nI haven't changed the file to `typed: srtict` yet, because sorbet will insist\nthat I make this class a collection that's compatible with generics, and\nI'm not ready for teh changes that will require across the codebase",
"is_bot": false,
"headline": "Add more type annotations to ObjectHash class",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T10:30:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c08babcc90e413ada3c5e34835af6f5096116938",
"body": "No changes to the code required",
"is_bot": false,
"headline": "change Parse to `typed: strict`",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T07:19:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e98890fb8383cb1c3057d88ad83659b4945541c3",
"body": "Now that the types are inline this is nice and easy",
"is_bot": false,
"headline": "Change FontDescriptor to `typed: strict`",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T07:17:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bb13a4db3d5e3ca6119f007e5d42fb8e3a2c7618",
"body": "Change xref.rb to `typed: strict`",
"is_bot": false,
"headline": "Merge pull request #568 from yob/typed-xref",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T07:08:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18afd04b2128a40d337012fa5cf7d5622ccdd71d",
"body": "Now that the types are inline, this is much easier to do",
"is_bot": false,
"headline": "Change xref.rb to `typed: strict`",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T06:56:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f25a3421a19f2af1e81372d000499cbf8f378d4b",
"body": null,
"is_bot": false,
"headline": "change WidthCalculator from `typed: true` to `typed: strict`",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T02:58:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3a919df5be7842194863fda28697abe612ffe245",
"body": null,
"is_bot": false,
"headline": "Change StandardKeyBuilder to `typed: strict`",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T02:17:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "13b2f135d99b3cb82b8e38b7d8e0ead6a65281ad",
"body": "Convert two classes from `typed: true` to `typed: strict`",
"is_bot": false,
"headline": "Merge pull request #566 from yob/strict-typing",
"author_name": "James Healy",
"author_login": "yob",
"committed_at": "2025-08-10T01:59:36Z",
"body_truncated": false,
"is_coding_agent": false
}
],
"releases_count": 64,
"commits_last_year": 121,
"latest_release_at": "2025-12-28T02:03:22Z",
"latest_release_tag": "v2.15.1",
"releases_from_tags": true,
"days_since_last_push": 6,
"active_weeks_last_year": 15,
"days_since_latest_release": 205,
"mean_days_between_releases": 158.1
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": false,
"health_percentage": 42,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "pdf-reader",
"exists": true,
"license": "MIT",
"keywords": [],
"ecosystem": "rubygems",
"matches_repo": true,
"registry_url": "https://rubygems.org/gems/pdf-reader",
"is_deprecated": false,
"latest_version": "2.15.1",
"repository_url": "https://github.com/yob/pdf-reader/tree/v2.15.1",
"versions_count": 64,
"total_downloads": 109562630,
"dependents_count": null,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": null,
"first_published_at": "2009-07-25T18:03:28Z",
"latest_published_at": "2025-12-28T02:06:54.937000Z",
"latest_version_yanked": null,
"days_since_latest_publish": 205
}
]
},
"popularity": {
"forks": 284,
"stars": 1925,
"watchers": 46,
"fork_history": {
"days": [
{
"date": "2009-03-16",
"count": 1
},
{
"date": "2009-06-17",
"count": 1
},
{
"date": "2009-09-14",
"count": 1
},
{
"date": "2009-12-13",
"count": 1
},
{
"date": "2010-02-12",
"count": 1
},
{
"date": "2010-03-09",
"count": 1
},
{
"date": "2010-04-24",
"count": 1
},
{
"date": "2010-08-04",
"count": 1
},
{
"date": "2010-11-03",
"count": 1
},
{
"date": "2011-02-09",
"count": 1
},
{
"date": "2011-02-14",
"count": 1
},
{
"date": "2011-03-28",
"count": 1
},
{
"date": "2011-06-05",
"count": 1
},
{
"date": "2011-07-22",
"count": 1
},
{
"date": "2011-07-27",
"count": 1
},
{
"date": "2011-08-13",
"count": 1
},
{
"date": "2011-08-17",
"count": 1
},
{
"date": "2011-08-19",
"count": 1
},
{
"date": "2011-09-20",
"count": 1
},
{
"date": "2011-10-06",
"count": 1
},
{
"date": "2011-11-06",
"count": 1
},
{
"date": "2011-11-15",
"count": 1
},
{
"date": "2011-12-04",
"count": 1
},
{
"date": "2011-12-19",
"count": 1
},
{
"date": "2012-01-09",
"count": 1
},
{
"date": "2012-01-14",
"count": 1
},
{
"date": "2012-01-26",
"count": 1
},
{
"date": "2012-02-17",
"count": 1
},
{
"date": "2012-04-17",
"count": 1
},
{
"date": "2012-04-20",
"count": 1
},
{
"date": "2012-05-12",
"count": 1
},
{
"date": "2012-06-11",
"count": 1
},
{
"date": "2012-07-05",
"count": 1
},
{
"date": "2012-07-24",
"count": 1
},
{
"date": "2012-07-27",
"count": 1
},
{
"date": "2012-07-29",
"count": 1
},
{
"date": "2012-08-07",
"count": 1
},
{
"date": "2012-09-10",
"count": 1
},
{
"date": "2012-09-18",
"count": 1
},
{
"date": "2012-11-06",
"count": 2
},
{
"date": "2012-12-17",
"count": 1
},
{
"date": "2013-01-02",
"count": 1
},
{
"date": "2013-01-13",
"count": 1
},
{
"date": "2013-01-14",
"count": 1
},
{
"date": "2013-01-28",
"count": 1
},
{
"date": "2013-02-15",
"count": 1
},
{
"date": "2013-03-06",
"count": 1
},
{
"date": "2013-03-22",
"count": 1
},
{
"date": "2013-03-24",
"count": 1
},
{
"date": "2013-04-25",
"count": 1
},
{
"date": "2013-04-27",
"count": 1
},
{
"date": "2013-05-08",
"count": 1
},
{
"date": "2013-05-16",
"count": 1
},
{
"date": "2013-05-24",
"count": 1
},
{
"date": "2013-06-23",
"count": 1
},
{
"date": "2013-07-02",
"count": 1
},
{
"date": "2013-07-15",
"count": 1
},
{
"date": "2013-07-19",
"count": 1
},
{
"date": "2013-08-01",
"count": 1
},
{
"date": "2013-08-13",
"count": 1
},
{
"date": "2013-08-22",
"count": 1
},
{
"date": "2013-09-06",
"count": 1
},
{
"date": "2013-10-01",
"count": 1
},
{
"date": "2013-10-16",
"count": 1
},
{
"date": "2013-10-30",
"count": 1
},
{
"date": "2013-12-31",
"count": 1
},
{
"date": "2014-01-20",
"count": 1
},
{
"date": "2014-02-17",
"count": 1
},
{
"date": "2014-03-10",
"count": 1
},
{
"date": "2014-03-28",
"count": 1
},
{
"date": "2014-04-10",
"count": 1
},
{
"date": "2014-04-21",
"count": 1
},
{
"date": "2014-05-15",
"count": 1
},
{
"date": "2014-05-19",
"count": 1
},
{
"date": "2014-06-03",
"count": 1
},
{
"date": "2014-06-13",
"count": 1
},
{
"date": "2014-06-25",
"count": 1
},
{
"date": "2014-07-27",
"count": 1
},
{
"date": "2014-08-03",
"count": 1
},
{
"date": "2014-08-09",
"count": 1
},
{
"date": "2014-08-21",
"count": 1
},
{
"date": "2014-09-16",
"count": 1
},
{
"date": "2014-10-10",
"count": 1
},
{
"date": "2014-10-12",
"count": 1
},
{
"date": "2014-10-16",
"count": 1
},
{
"date": "2014-11-10",
"count": 1
},
{
"date": "2014-12-09",
"count": 1
},
{
"date": "2014-12-17",
"count": 1
},
{
"date": "2014-12-22",
"count": 1
},
{
"date": "2014-12-25",
"count": 1
},
{
"date": "2015-01-08",
"count": 1
},
{
"date": "2015-03-08",
"count": 1
},
{
"date": "2015-03-11",
"count": 1
},
{
"date": "2015-03-18",
"count": 1
},
{
"date": "2015-03-20",
"count": 1
},
{
"date": "2015-03-26",
"count": 1
},
{
"date": "2015-04-04",
"count": 1
},
{
"date": "2015-04-08",
"count": 1
},
{
"date": "2015-06-19",
"count": 1
},
{
"date": "2015-06-30",
"count": 1
},
{
"date": "2015-07-07",
"count": 1
},
{
"date": "2015-07-24",
"count": 1
},
{
"date": "2015-08-08",
"count": 1
},
{
"date": "2015-08-18",
"count": 1
},
{
"date": "2015-09-23",
"count": 1
},
{
"date": "2015-10-05",
"count": 1
},
{
"date": "2015-10-06",
"count": 1
},
{
"date": "2015-10-15",
"count": 1
},
{
"date": "2015-10-22",
"count": 1
},
{
"date": "2015-11-11",
"count": 1
},
{
"date": "2015-11-22",
"count": 1
},
{
"date": "2015-12-07",
"count": 1
},
{
"date": "2015-12-14",
"count": 1
},
{
"date": "2015-12-21",
"count": 1
},
{
"date": "2015-12-27",
"count": 1
},
{
"date": "2016-01-02",
"count": 1
},
{
"date": "2016-02-16",
"count": 1
},
{
"date": "2016-02-19",
"count": 1
},
{
"date": "2016-03-07",
"count": 1
},
{
"date": "2016-03-19",
"count": 1
},
{
"date": "2016-03-22",
"count": 1
},
{
"date": "2016-03-29",
"count": 1
},
{
"date": "2016-04-10",
"count": 1
},
{
"date": "2016-04-22",
"count": 1
},
{
"date": "2016-06-02",
"count": 1
},
{
"date": "2016-07-14",
"count": 1
},
{
"date": "2016-08-30",
"count": 1
},
{
"date": "2016-09-11",
"count": 1
},
{
"date": "2016-09-20",
"count": 1
},
{
"date": "2016-10-03",
"count": 1
},
{
"date": "2016-10-31",
"count": 1
},
{
"date": "2016-11-17",
"count": 1
},
{
"date": "2016-12-20",
"count": 2
},
{
"date": "2016-12-24",
"count": 1
},
{
"date": "2017-02-22",
"count": 1
},
{
"date": "2017-03-14",
"count": 1
},
{
"date": "2017-03-16",
"count": 1
},
{
"date": "2017-03-18",
"count": 1
},
{
"date": "2017-03-31",
"count": 1
},
{
"date": "2017-04-18",
"count": 1
},
{
"date": "2017-05-20",
"count": 1
},
{
"date": "2017-05-30",
"count": 2
},
{
"date": "2017-06-01",
"count": 1
},
{
"date": "2017-07-04",
"count": 1
},
{
"date": "2017-07-07",
"count": 1
},
{
"date": "2017-07-12",
"count": 1
},
{
"date": "2017-08-10",
"count": 1
},
{
"date": "2017-08-14",
"count": 1
},
{
"date": "2017-08-15",
"count": 1
},
{
"date": "2017-09-15",
"count": 2
},
{
"date": "2017-09-28",
"count": 1
},
{
"date": "2017-09-29",
"count": 1
},
{
"date": "2017-10-18",
"count": 1
},
{
"date": "2017-10-31",
"count": 1
},
{
"date": "2017-11-11",
"count": 1
},
{
"date": "2017-12-13",
"count": 1
},
{
"date": "2017-12-20",
"count": 1
},
{
"date": "2018-02-02",
"count": 1
},
{
"date": "2018-02-21",
"count": 1
},
{
"date": "2018-02-28",
"count": 1
},
{
"date": "2018-03-02",
"count": 1
},
{
"date": "2018-03-27",
"count": 1
},
{
"date": "2018-07-03",
"count": 1
},
{
"date": "2018-07-24",
"count": 1
},
{
"date": "2018-09-06",
"count": 1
},
{
"date": "2018-11-25",
"count": 1
},
{
"date": "2018-11-27",
"count": 1
},
{
"date": "2019-01-01",
"count": 1
},
{
"date": "2019-01-07",
"count": 1
},
{
"date": "2019-01-08",
"count": 1
},
{
"date": "2019-02-20",
"count": 1
},
{
"date": "2019-04-24",
"count": 1
},
{
"date": "2019-06-03",
"count": 1
},
{
"date": "2019-06-25",
"count": 3
},
{
"date": "2019-06-26",
"count": 1
},
{
"date": "2019-07-06",
"count": 1
},
{
"date": "2019-07-12",
"count": 1
},
{
"date": "2019-07-18",
"count": 1
},
{
"date": "2019-08-02",
"count": 1
},
{
"date": "2019-08-22",
"count": 1
},
{
"date": "2019-11-11",
"count": 1
},
{
"date": "2019-11-15",
"count": 1
},
{
"date": "2019-11-19",
"count": 1
},
{
"date": "2019-11-24",
"count": 1
},
{
"date": "2019-11-26",
"count": 1
},
{
"date": "2019-12-15",
"count": 1
},
{
"date": "2020-01-02",
"count": 1
},
{
"date": "2020-01-30",
"count": 1
},
{
"date": "2020-02-11",
"count": 1
},
{
"date": "2020-02-17",
"count": 1
},
{
"date": "2020-03-19",
"count": 1
},
{
"date": "2020-04-10",
"count": 1
},
{
"date": "2020-05-31",
"count": 1
},
{
"date": "2020-07-28",
"count": 1
},
{
"date": "2020-09-05",
"count": 1
},
{
"date": "2020-11-19",
"count": 1
},
{
"date": "2021-01-30",
"count": 1
},
{
"date": "2021-06-01",
"count": 1
},
{
"date": "2021-06-03",
"count": 1
},
{
"date": "2021-08-16",
"count": 1
},
{
"date": "2021-09-03",
"count": 1
},
{
"date": "2021-10-03",
"count": 1
},
{
"date": "2021-10-06",
"count": 1
},
{
"date": "2021-10-16",
"count": 1
},
{
"date": "2021-10-21",
"count": 1
},
{
"date": "2022-01-07",
"count": 1
},
{
"date": "2022-01-12",
"count": 1
},
{
"date": "2022-02-05",
"count": 1
},
{
"date": "2022-02-10",
"count": 1
},
{
"date": "2022-02-21",
"count": 1
},
{
"date": "2022-03-02",
"count": 1
},
{
"date": "2022-03-25",
"count": 1
},
{
"date": "2022-04-03",
"count": 1
},
{
"date": "2022-04-27",
"count": 1
},
{
"date": "2022-05-04",
"count": 1
},
{
"date": "2022-05-11",
"count": 1
},
{
"date": "2022-05-31",
"count": 1
},
{
"date": "2022-07-12",
"count": 1
},
{
"date": "2022-08-10",
"count": 1
},
{
"date": "2022-08-23",
"count": 1
},
{
"date": "2022-08-24",
"count": 1
},
{
"date": "2022-10-05",
"count": 1
},
{
"date": "2022-10-09",
"count": 1
},
{
"date": "2022-11-29",
"count": 1
},
{
"date": "2023-01-13",
"count": 1
},
{
"date": "2023-03-16",
"count": 1
},
{
"date": "2023-04-05",
"count": 1
},
{
"date": "2023-04-18",
"count": 1
},
{
"date": "2023-05-11",
"count": 1
},
{
"date": "2023-05-18",
"count": 1
},
{
"date": "2023-07-28",
"count": 1
},
{
"date": "2023-10-10",
"count": 1
},
{
"date": "2023-10-19",
"count": 1
},
{
"date": "2023-10-25",
"count": 1
},
{
"date": "2023-11-21",
"count": 1
},
{
"date": "2023-12-14",
"count": 1
},
{
"date": "2023-12-15",
"count": 1
},
{
"date": "2024-01-26",
"count": 1
},
{
"date": "2024-02-05",
"count": 1
},
{
"date": "2024-02-20",
"count": 1
},
{
"date": "2024-03-15",
"count": 1
},
{
"date": "2024-03-25",
"count": 1
},
{
"date": "2024-05-30",
"count": 1
},
{
"date": "2024-06-05",
"count": 1
},
{
"date": "2024-06-28",
"count": 1
},
{
"date": "2024-08-28",
"count": 1
},
{
"date": "2024-09-17",
"count": 1
},
{
"date": "2024-12-01",
"count": 1
},
{
"date": "2024-12-12",
"count": 1
},
{
"date": "2025-01-11",
"count": 1
},
{
"date": "2025-01-30",
"count": 1
},
{
"date": "2025-02-03",
"count": 1
},
{
"date": "2025-03-13",
"count": 1
},
{
"date": "2025-04-14",
"count": 1
},
{
"date": "2025-04-23",
"count": 1
},
{
"date": "2025-06-13",
"count": 1
},
{
"date": "2025-06-24",
"count": 1
},
{
"date": "2025-07-19",
"count": 1
},
{
"date": "2025-07-24",
"count": 1
},
{
"date": "2025-08-06",
"count": 1
},
{
"date": "2025-11-11",
"count": 2
},
{
"date": "2025-11-21",
"count": 1
},
{
"date": "2026-02-19",
"count": 1
},
{
"date": "2026-04-07",
"count": 1
},
{
"date": "2026-04-20",
"count": 1
},
{
"date": "2026-04-23",
"count": 1
},
{
"date": "2026-07-10",
"count": 1
}
],
"complete": true,
"collected": 274,
"total_forks": 284
},
"star_history": {
"days": [
{
"date": "2017-04-12",
"count": 1
},
{
"date": "2017-04-18",
"count": 1
},
{
"date": "2017-04-22",
"count": 1
},
{
"date": "2017-04-25",
"count": 1
},
{
"date": "2017-04-26",
"count": 1
},
{
"date": "2017-04-27",
"count": 1
},
{
"date": "2017-04-28",
"count": 1
},
{
"date": "2017-04-29",
"count": 1
},
{
"date": "2017-05-01",
"count": 2
},
{
"date": "2017-05-09",
"count": 1
},
{
"date": "2017-05-17",
"count": 1
},
{
"date": "2017-05-24",
"count": 2
},
{
"date": "2017-05-26",
"count": 1
},
{
"date": "2017-06-03",
"count": 1
},
{
"date": "2017-06-05",
"count": 1
},
{
"date": "2017-06-06",
"count": 1
},
{
"date": "2017-06-07",
"count": 1
},
{
"date": "2017-06-08",
"count": 2
},
{
"date": "2017-06-11",
"count": 1
},
{
"date": "2017-06-12",
"count": 2
},
{
"date": "2017-06-16",
"count": 3
},
{
"date": "2017-06-18",
"count": 1
},
{
"date": "2017-06-22",
"count": 2
},
{
"date": "2017-06-24",
"count": 1
},
{
"date": "2017-06-29",
"count": 1
},
{
"date": "2017-07-03",
"count": 1
},
{
"date": "2017-07-07",
"count": 2
},
{
"date": "2017-07-08",
"count": 2
},
{
"date": "2017-07-09",
"count": 1
},
{
"date": "2017-07-13",
"count": 1
},
{
"date": "2017-07-15",
"count": 1
},
{
"date": "2017-07-20",
"count": 1
},
{
"date": "2017-07-24",
"count": 1
},
{
"date": "2017-07-25",
"count": 1
},
{
"date": "2017-07-26",
"count": 2
},
{
"date": "2017-07-27",
"count": 1
},
{
"date": "2017-07-30",
"count": 1
},
{
"date": "2017-08-01",
"count": 3
},
{
"date": "2017-08-05",
"count": 1
},
{
"date": "2017-08-08",
"count": 1
},
{
"date": "2017-08-14",
"count": 1
},
{
"date": "2017-08-15",
"count": 2
},
{
"date": "2017-08-22",
"count": 1
},
{
"date": "2017-08-24",
"count": 1
},
{
"date": "2017-08-25",
"count": 1
},
{
"date": "2017-09-01",
"count": 1
},
{
"date": "2017-09-04",
"count": 1
},
{
"date": "2017-09-05",
"count": 3
},
{
"date": "2017-09-07",
"count": 1
},
{
"date": "2017-09-17",
"count": 1
},
{
"date": "2017-09-22",
"count": 1
},
{
"date": "2017-09-27",
"count": 1
},
{
"date": "2017-10-07",
"count": 1
},
{
"date": "2017-10-11",
"count": 1
},
{
"date": "2017-10-19",
"count": 1
},
{
"date": "2017-10-23",
"count": 1
},
{
"date": "2017-10-24",
"count": 1
},
{
"date": "2017-10-25",
"count": 1
},
{
"date": "2017-10-26",
"count": 1
},
{
"date": "2017-10-29",
"count": 1
},
{
"date": "2017-10-31",
"count": 1
},
{
"date": "2017-11-03",
"count": 1
},
{
"date": "2017-11-07",
"count": 1
},
{
"date": "2017-11-08",
"count": 2
},
{
"date": "2017-11-10",
"count": 1
},
{
"date": "2017-11-13",
"count": 1
},
{
"date": "2017-11-14",
"count": 1
},
{
"date": "2017-11-16",
"count": 3
},
{
"date": "2017-11-18",
"count": 1
},
{
"date": "2017-11-22",
"count": 1
},
{
"date": "2017-11-24",
"count": 2
},
{
"date": "2017-11-28",
"count": 1
},
{
"date": "2017-12-01",
"count": 1
},
{
"date": "2017-12-05",
"count": 1
},
{
"date": "2017-12-08",
"count": 1
},
{
"date": "2017-12-09",
"count": 1
},
{
"date": "2017-12-13",
"count": 1
},
{
"date": "2017-12-25",
"count": 1
},
{
"date": "2017-12-29",
"count": 1
},
{
"date": "2018-01-08",
"count": 1
},
{
"date": "2018-01-12",
"count": 1
},
{
"date": "2018-01-14",
"count": 1
},
{
"date": "2018-01-17",
"count": 2
},
{
"date": "2018-01-22",
"count": 2
},
{
"date": "2018-01-23",
"count": 1
},
{
"date": "2018-01-24",
"count": 1
},
{
"date": "2018-01-25",
"count": 1
},
{
"date": "2018-01-28",
"count": 2
},
{
"date": "2018-01-30",
"count": 1
},
{
"date": "2018-02-01",
"count": 1
},
{
"date": "2018-02-02",
"count": 1
},
{
"date": "2018-02-06",
"count": 1
},
{
"date": "2018-02-07",
"count": 1
},
{
"date": "2018-02-10",
"count": 1
},
{
"date": "2018-02-17",
"count": 1
},
{
"date": "2018-02-19",
"count": 1
},
{
"date": "2018-02-22",
"count": 1
},
{
"date": "2018-02-26",
"count": 1
},
{
"date": "2018-02-28",
"count": 1
},
{
"date": "2018-03-01",
"count": 1
},
{
"date": "2018-03-02",
"count": 3
},
{
"date": "2018-03-07",
"count": 1
},
{
"date": "2018-03-11",
"count": 1
},
{
"date": "2018-03-14",
"count": 1
},
{
"date": "2018-03-15",
"count": 1
},
{
"date": "2018-03-21",
"count": 2
},
{
"date": "2018-03-22",
"count": 1
},
{
"date": "2018-03-23",
"count": 1
},
{
"date": "2018-03-25",
"count": 1
},
{
"date": "2018-03-26",
"count": 1
},
{
"date": "2018-04-02",
"count": 2
},
{
"date": "2018-04-04",
"count": 1
},
{
"date": "2018-04-07",
"count": 1
},
{
"date": "2018-04-13",
"count": 1
},
{
"date": "2018-04-15",
"count": 1
},
{
"date": "2018-04-16",
"count": 1
},
{
"date": "2018-04-19",
"count": 2
},
{
"date": "2018-04-23",
"count": 1
},
{
"date": "2018-04-24",
"count": 1
},
{
"date": "2018-04-25",
"count": 2
},
{
"date": "2018-04-29",
"count": 1
},
{
"date": "2018-05-01",
"count": 1
},
{
"date": "2018-05-02",
"count": 2
},
{
"date": "2018-05-07",
"count": 1
},
{
"date": "2018-05-09",
"count": 1
},
{
"date": "2018-05-11",
"count": 1
},
{
"date": "2018-05-14",
"count": 1
},
{
"date": "2018-05-15",
"count": 1
},
{
"date": "2018-05-16",
"count": 1
},
{
"date": "2018-05-17",
"count": 1
},
{
"date": "2018-05-18",
"count": 1
},
{
"date": "2018-05-25",
"count": 1
},
{
"date": "2018-05-29",
"count": 1
},
{
"date": "2018-05-30",
"count": 2
},
{
"date": "2018-05-31",
"count": 1
},
{
"date": "2018-06-01",
"count": 3
},
{
"date": "2018-06-05",
"count": 2
},
{
"date": "2018-06-07",
"count": 1
},
{
"date": "2018-06-09",
"count": 1
},
{
"date": "2018-06-11",
"count": 1
},
{
"date": "2018-06-12",
"count": 1
},
{
"date": "2018-06-13",
"count": 1
},
{
"date": "2018-06-15",
"count": 1
},
{
"date": "2018-06-20",
"count": 1
},
{
"date": "2018-06-21",
"count": 4
},
{
"date": "2018-06-22",
"count": 1
},
{
"date": "2018-06-25",
"count": 1
},
{
"date": "2018-07-02",
"count": 1
},
{
"date": "2018-07-04",
"count": 1
},
{
"date": "2018-07-12",
"count": 1
},
{
"date": "2018-07-14",
"count": 1
},
{
"date": "2018-07-18",
"count": 1
},
{
"date": "2018-07-24",
"count": 1
},
{
"date": "2018-07-31",
"count": 1
},
{
"date": "2018-08-01",
"count": 1
},
{
"date": "2018-08-09",
"count": 1
},
{
"date": "2018-08-14",
"count": 1
},
{
"date": "2018-08-16",
"count": 1
},
{
"date": "2018-08-19",
"count": 1
},
{
"date": "2018-08-20",
"count": 1
},
{
"date": "2018-08-22",
"count": 2
},
{
"date": "2018-09-01",
"count": 1
},
{
"date": "2018-09-04",
"count": 1
},
{
"date": "2018-09-05",
"count": 2
},
{
"date": "2018-09-08",
"count": 1
},
{
"date": "2018-09-11",
"count": 1
},
{
"date": "2018-09-12",
"count": 2
},
{
"date": "2018-09-14",
"count": 2
},
{
"date": "2018-09-16",
"count": 1
},
{
"date": "2018-09-26",
"count": 1
},
{
"date": "2018-09-28",
"count": 1
},
{
"date": "2018-10-09",
"count": 1
},
{
"date": "2018-10-12",
"count": 1
},
{
"date": "2018-10-18",
"count": 1
},
{
"date": "2018-10-19",
"count": 1
},
{
"date": "2018-10-25",
"count": 1
},
{
"date": "2018-11-01",
"count": 1
},
{
"date": "2018-11-06",
"count": 1
},
{
"date": "2018-11-09",
"count": 1
},
{
"date": "2018-11-17",
"count": 1
},
{
"date": "2018-11-25",
"count": 1
},
{
"date": "2018-11-27",
"count": 1
},
{
"date": "2018-12-04",
"count": 3
},
{
"date": "2018-12-07",
"count": 1
},
{
"date": "2018-12-13",
"count": 1
},
{
"date": "2018-12-14",
"count": 1
},
{
"date": "2018-12-21",
"count": 1
},
{
"date": "2018-12-24",
"count": 1
},
{
"date": "2018-12-28",
"count": 2
},
{
"date": "2019-01-01",
"count": 2
},
{
"date": "2019-01-02",
"count": 1
},
{
"date": "2019-01-05",
"count": 1
},
{
"date": "2019-01-06",
"count": 1
},
{
"date": "2019-01-11",
"count": 1
},
{
"date": "2019-01-14",
"count": 2
},
{
"date": "2019-01-15",
"count": 1
},
{
"date": "2019-01-19",
"count": 1
},
{
"date": "2019-01-20",
"count": 1
},
{
"date": "2019-01-21",
"count": 1
},
{
"date": "2019-01-24",
"count": 2
},
{
"date": "2019-01-28",
"count": 1
},
{
"date": "2019-02-01",
"count": 1
},
{
"date": "2019-02-06",
"count": 1
},
{
"date": "2019-02-09",
"count": 1
},
{
"date": "2019-02-10",
"count": 1
},
{
"date": "2019-02-11",
"count": 1
},
{
"date": "2019-02-12",
"count": 1
},
{
"date": "2019-02-15",
"count": 1
},
{
"date": "2019-02-22",
"count": 1
},
{
"date": "2019-02-23",
"count": 1
},
{
"date": "2019-02-25",
"count": 2
},
{
"date": "2019-03-01",
"count": 1
},
{
"date": "2019-03-03",
"count": 2
},
{
"date": "2019-03-05",
"count": 1
},
{
"date": "2019-03-14",
"count": 2
},
{
"date": "2019-03-28",
"count": 1
},
{
"date": "2019-04-14",
"count": 1
},
{
"date": "2019-04-23",
"count": 1
},
{
"date": "2019-04-24",
"count": 1
},
{
"date": "2019-04-26",
"count": 2
},
{
"date": "2019-05-04",
"count": 1
},
{
"date": "2019-05-07",
"count": 2
},
{
"date": "2019-05-08",
"count": 1
},
{
"date": "2019-05-11",
"count": 1
},
{
"date": "2019-05-21",
"count": 1
},
{
"date": "2019-05-26",
"count": 1
},
{
"date": "2019-05-28",
"count": 2
},
{
"date": "2019-05-29",
"count": 2
},
{
"date": "2019-06-01",
"count": 1
},
{
"date": "2019-06-02",
"count": 1
},
{
"date": "2019-06-05",
"count": 2
},
{
"date": "2019-06-06",
"count": 1
},
{
"date": "2019-06-08",
"count": 2
},
{
"date": "2019-06-11",
"count": 1
},
{
"date": "2019-06-18",
"count": 1
},
{
"date": "2019-06-23",
"count": 1
},
{
"date": "2019-06-26",
"count": 4
},
{
"date": "2019-06-27",
"count": 7
},
{
"date": "2019-06-28",
"count": 1
},
{
"date": "2019-06-29",
"count": 3
},
{
"date": "2019-07-02",
"count": 2
},
{
"date": "2019-07-05",
"count": 2
},
{
"date": "2019-07-06",
"count": 1
},
{
"date": "2019-07-11",
"count": 1
},
{
"date": "2019-07-15",
"count": 2
},
{
"date": "2019-07-16",
"count": 1
},
{
"date": "2019-07-19",
"count": 1
},
{
"date": "2019-07-22",
"count": 1
},
{
"date": "2019-07-23",
"count": 1
},
{
"date": "2019-07-31",
"count": 3
},
{
"date": "2019-08-02",
"count": 1
},
{
"date": "2019-08-03",
"count": 1
},
{
"date": "2019-08-06",
"count": 1
},
{
"date": "2019-08-12",
"count": 1
},
{
"date": "2019-08-16",
"count": 1
},
{
"date": "2019-08-17",
"count": 1
},
{
"date": "2019-08-18",
"count": 1
},
{
"date": "2019-08-19",
"count": 1
},
{
"date": "2019-08-20",
"count": 1
},
{
"date": "2019-08-27",
"count": 1
},
{
"date": "2019-08-29",
"count": 1
},
{
"date": "2019-09-10",
"count": 1
},
{
"date": "2019-09-11",
"count": 1
},
{
"date": "2019-09-13",
"count": 2
},
{
"date": "2019-09-22",
"count": 1
},
{
"date": "2019-09-24",
"count": 1
},
{
"date": "2019-09-26",
"count": 1
},
{
"date": "2019-09-28",
"count": 1
},
{
"date": "2019-10-03",
"count": 1
},
{
"date": "2019-10-07",
"count": 1
},
{
"date": "2019-10-11",
"count": 2
},
{
"date": "2019-10-15",
"count": 2
},
{
"date": "2019-10-19",
"count": 1
},
{
"date": "2019-10-21",
"count": 1
},
{
"date": "2019-10-22",
"count": 1
},
{
"date": "2019-10-23",
"count": 1
},
{
"date": "2019-10-24",
"count": 1
},
{
"date": "2019-10-26",
"count": 1
},
{
"date": "2019-10-28",
"count": 2
},
{
"date": "2019-11-02",
"count": 1
},
{
"date": "2019-11-03",
"count": 1
},
{
"date": "2019-11-04",
"count": 1
},
{
"date": "2019-11-06",
"count": 1
},
{
"date": "2019-11-09",
"count": 1
},
{
"date": "2019-11-11",
"count": 1
},
{
"date": "2019-11-12",
"count": 1
},
{
"date": "2019-11-13",
"count": 2
},
{
"date": "2019-11-14",
"count": 1
},
{
"date": "2019-11-16",
"count": 1
},
{
"date": "2019-11-20",
"count": 1
},
{
"date": "2019-11-22",
"count": 1
},
{
"date": "2019-11-25",
"count": 1
},
{
"date": "2019-11-26",
"count": 1
},
{
"date": "2019-12-01",
"count": 1
},
{
"date": "2019-12-05",
"count": 1
},
{
"date": "2019-12-10",
"count": 1
},
{
"date": "2019-12-11",
"count": 1
},
{
"date": "2019-12-12",
"count": 1
},
{
"date": "2019-12-16",
"count": 1
},
{
"date": "2019-12-17",
"count": 2
},
{
"date": "2019-12-20",
"count": 1
},
{
"date": "2019-12-22",
"count": 1
},
{
"date": "2019-12-25",
"count": 1
},
{
"date": "2020-01-01",
"count": 1
},
{
"date": "2020-01-12",
"count": 2
},
{
"date": "2020-01-13",
"count": 1
},
{
"date": "2020-01-19",
"count": 1
},
{
"date": "2020-01-22",
"count": 1
},
{
"date": "2020-01-25",
"count": 1
},
{
"date": "2020-01-26",
"count": 1
},
{
"date": "2020-02-03",
"count": 1
},
{
"date": "2020-02-07",
"count": 1
},
{
"date": "2020-02-10",
"count": 1
},
{
"date": "2020-02-11",
"count": 1
},
{
"date": "2020-02-18",
"count": 1
},
{
"date": "2020-02-22",
"count": 1
},
{
"date": "2020-02-28",
"count": 1
},
{
"date": "2020-02-29",
"count": 1
},
{
"date": "2020-03-05",
"count": 1
},
{
"date": "2020-03-06",
"count": 1
},
{
"date": "2020-03-08",
"count": 1
},
{
"date": "2020-03-09",
"count": 1
},
{
"date": "2020-03-10",
"count": 1
},
{
"date": "2020-03-15",
"count": 1
},
{
"date": "2020-03-16",
"count": 1
},
{
"date": "2020-03-20",
"count": 1
},
{
"date": "2020-03-21",
"count": 1
},
{
"date": "2020-03-22",
"count": 2
},
{
"date": "2020-03-23",
"count": 1
},
{
"date": "2020-03-24",
"count": 1
},
{
"date": "2020-03-25",
"count": 1
},
{
"date": "2020-03-29",
"count": 2
},
{
"date": "2020-04-02",
"count": 1
},
{
"date": "2020-04-05",
"count": 1
},
{
"date": "2020-04-06",
"count": 1
},
{
"date": "2020-04-07",
"count": 1
},
{
"date": "2020-04-08",
"count": 1
},
{
"date": "2020-04-09",
"count": 1
},
{
"date": "2020-04-27",
"count": 1
},
{
"date": "2020-04-30",
"count": 2
},
{
"date": "2020-05-04",
"count": 3
},
{
"date": "2020-05-15",
"count": 1
},
{
"date": "2020-05-17",
"count": 1
},
{
"date": "2020-05-20",
"count": 1
},
{
"date": "2020-05-21",
"count": 1
},
{
"date": "2020-05-31",
"count": 2
},
{
"date": "2020-06-06",
"count": 1
},
{
"date": "2020-06-09",
"count": 2
},
{
"date": "2020-06-10",
"count": 1
},
{
"date": "2020-06-11",
"count": 1
},
{
"date": "2020-06-12",
"count": 1
},
{
"date": "2020-06-18",
"count": 1
},
{
"date": "2020-06-26",
"count": 1
},
{
"date": "2020-06-30",
"count": 1
},
{
"date": "2020-07-02",
"count": 1
},
{
"date": "2020-07-03",
"count": 1
},
{
"date": "2020-07-06",
"count": 1
},
{
"date": "2020-07-09",
"count": 1
},
{
"date": "2020-07-10",
"count": 1
},
{
"date": "2020-07-11",
"count": 1
},
{
"date": "2020-07-16",
"count": 1
},
{
"date": "2020-07-19",
"count": 1
},
{
"date": "2020-07-20",
"count": 1
},
{
"date": "2020-08-01",
"count": 1
},
{
"date": "2020-08-05",
"count": 1
},
{
"date": "2020-08-08",
"count": 1
},
{
"date": "2020-08-23",
"count": 1
},
{
"date": "2020-09-02",
"count": 1
},
{
"date": "2020-09-04",
"count": 2
},
{
"date": "2020-09-05",
"count": 1
},
{
"date": "2020-09-17",
"count": 1
},
{
"date": "2020-09-25",
"count": 1
},
{
"date": "2020-10-08",
"count": 1
},
{
"date": "2020-10-10",
"count": 1
},
{
"date": "2020-10-13",
"count": 1
},
{
"date": "2020-10-20",
"count": 1
},
{
"date": "2020-10-25",
"count": 1
},
{
"date": "2020-10-26",
"count": 1
},
{
"date": "2020-10-30",
"count": 1
},
{
"date": "2020-11-07",
"count": 1
},
{
"date": "2020-11-14",
"count": 1
},
{
"date": "2020-11-16",
"count": 1
},
{
"date": "2020-11-19",
"count": 2
},
{
"date": "2020-11-23",
"count": 1
},
{
"date": "2020-11-25",
"count": 1
},
{
"date": "2020-12-03",
"count": 1
},
{
"date": "2020-12-07",
"count": 1
},
{
"date": "2020-12-08",
"count": 1
},
{
"date": "2020-12-09",
"count": 1
},
{
"date": "2020-12-16",
"count": 2
},
{
"date": "2020-12-21",
"count": 1
},
{
"date": "2020-12-22",
"count": 1
},
{
"date": "2020-12-23",
"count": 1
},
{
"date": "2020-12-25",
"count": 2
},
{
"date": "2020-12-27",
"count": 1
},
{
"date": "2021-01-01",
"count": 1
},
{
"date": "2021-01-03",
"count": 1
},
{
"date": "2021-01-04",
"count": 2
},
{
"date": "2021-01-05",
"count": 1
},
{
"date": "2021-01-06",
"count": 1
},
{
"date": "2021-01-08",
"count": 1
},
{
"date": "2021-01-16",
"count": 1
},
{
"date": "2021-01-17",
"count": 1
},
{
"date": "2021-02-02",
"count": 1
},
{
"date": "2021-02-17",
"count": 1
},
{
"date": "2021-02-20",
"count": 1
},
{
"date": "2021-02-21",
"count": 1
},
{
"date": "2021-03-03",
"count": 1
},
{
"date": "2021-03-07",
"count": 1
},
{
"date": "2021-03-09",
"count": 1
},
{
"date": "2021-03-11",
"count": 1
},
{
"date": "2021-03-13",
"count": 1
},
{
"date": "2021-03-16",
"count": 2
},
{
"date": "2021-03-17",
"count": 1
},
{
"date": "2021-03-24",
"count": 1
},
{
"date": "2021-04-07",
"count": 1
},
{
"date": "2021-04-16",
"count": 1
},
{
"date": "2021-04-19",
"count": 1
},
{
"date": "2021-04-23",
"count": 1
},
{
"date": "2021-04-25",
"count": 1
},
{
"date": "2021-04-26",
"count": 1
},
{
"date": "2021-05-06",
"count": 1
},
{
"date": "2021-05-09",
"count": 1
},
{
"date": "2021-05-10",
"count": 1
},
{
"date": "2021-05-18",
"count": 1
},
{
"date": "2021-05-19",
"count": 1
},
{
"date": "2021-05-20",
"count": 1
},
{
"date": "2021-05-22",
"count": 1
},
{
"date": "2021-05-23",
"count": 1
},
{
"date": "2021-06-03",
"count": 1
},
{
"date": "2021-06-08",
"count": 1
},
{
"date": "2021-06-16",
"count": 2
},
{
"date": "2021-06-30",
"count": 1
},
{
"date": "2021-07-01",
"count": 1
},
{
"date": "2021-07-02",
"count": 2
},
{
"date": "2021-07-16",
"count": 1
},
{
"date": "2021-07-25",
"count": 1
},
{
"date": "2021-07-30",
"count": 1
},
{
"date": "2021-08-03",
"count": 1
},
{
"date": "2021-08-05",
"count": 2
},
{
"date": "2021-08-10",
"count": 2
},
{
"date": "2021-08-18",
"count": 1
},
{
"date": "2021-08-27",
"count": 1
},
{
"date": "2021-08-31",
"count": 1
},
{
"date": "2021-09-04",
"count": 1
},
{
"date": "2021-09-06",
"count": 1
},
{
"date": "2021-09-09",
"count": 1
},
{
"date": "2021-09-28",
"count": 1
},
{
"date": "2021-10-07",
"count": 1
},
{
"date": "2021-10-14",
"count": 1
},
{
"date": "2021-10-21",
"count": 1
},
{
"date": "2021-10-22",
"count": 1
},
{
"date": "2021-10-26",
"count": 1
},
{
"date": "2021-10-28",
"count": 1
},
{
"date": "2021-11-02",
"count": 1
},
{
"date": "2021-11-18",
"count": 13
},
{
"date": "2021-11-19",
"count": 6
},
{
"date": "2021-11-20",
"count": 4
},
{
"date": "2021-11-21",
"count": 1
},
{
"date": "2021-11-22",
"count": 3
},
{
"date": "2021-11-23",
"count": 4
},
{
"date": "2021-11-24",
"count": 1
},
{
"date": "2021-11-26",
"count": 1
},
{
"date": "2021-12-02",
"count": 1
},
{
"date": "2021-12-04",
"count": 1
},
{
"date": "2021-12-06",
"count": 2
},
{
"date": "2021-12-14",
"count": 1
},
{
"date": "2021-12-15",
"count": 1
},
{
"date": "2021-12-21",
"count": 1
},
{
"date": "2021-12-25",
"count": 1
},
{
"date": "2022-01-03",
"count": 2
},
{
"date": "2022-01-12",
"count": 1
},
{
"date": "2022-01-13",
"count": 1
},
{
"date": "2022-01-16",
"count": 1
},
{
"date": "2022-01-18",
"count": 1
},
{
"date": "2022-01-19",
"count": 1
},
{
"date": "2022-01-22",
"count": 1
},
{
"date": "2022-01-23",
"count": 1
},
{
"date": "2022-01-24",
"count": 1
},
{
"date": "2022-02-02",
"count": 1
},
{
"date": "2022-02-07",
"count": 1
},
{
"date": "2022-02-11",
"count": 2
},
{
"date": "2022-02-12",
"count": 1
},
{
"date": "2022-02-15",
"count": 1
},
{
"date": "2022-02-21",
"count": 1
},
{
"date": "2022-02-22",
"count": 2
},
{
"date": "2022-02-23",
"count": 1
},
{
"date": "2022-03-06",
"count": 1
},
{
"date": "2022-03-10",
"count": 2
},
{
"date": "2022-03-15",
"count": 1
},
{
"date": "2022-03-25",
"count": 2
},
{
"date": "2022-04-08",
"count": 1
},
{
"date": "2022-04-17",
"count": 3
},
{
"date": "2022-04-20",
"count": 1
},
{
"date": "2022-04-21",
"count": 1
},
{
"date": "2022-04-24",
"count": 1
},
{
"date": "2022-04-27",
"count": 1
},
{
"date": "2022-04-28",
"count": 1
},
{
"date": "2022-05-05",
"count": 1
},
{
"date": "2022-05-12",
"count": 5
},
{
"date": "2022-05-13",
"count": 4
},
{
"date": "2022-05-14",
"count": 2
},
{
"date": "2022-05-15",
"count": 1
},
{
"date": "2022-05-16",
"count": 1
},
{
"date": "2022-05-19",
"count": 1
},
{
"date": "2022-05-20",
"count": 1
},
{
"date": "2022-05-24",
"count": 1
},
{
"date": "2022-05-27",
"count": 1
},
{
"date": "2022-05-28",
"count": 1
},
{
"date": "2022-06-01",
"count": 2
},
{
"date": "2022-06-05",
"count": 1
},
{
"date": "2022-06-08",
"count": 1
},
{
"date": "2022-06-10",
"count": 1
},
{
"date": "2022-06-13",
"count": 1
},
{
"date": "2022-06-25",
"count": 2
},
{
"date": "2022-07-06",
"count": 2
},
{
"date": "2022-07-13",
"count": 1
},
{
"date": "2022-07-19",
"count": 1
},
{
"date": "2022-07-22",
"count": 1
},
{
"date": "2022-07-31",
"count": 1
},
{
"date": "2022-08-01",
"count": 1
},
{
"date": "2022-08-02",
"count": 1
},
{
"date": "2022-08-08",
"count": 1
},
{
"date": "2022-08-10",
"count": 1
},
{
"date": "2022-08-11",
"count": 1
},
{
"date": "2022-08-21",
"count": 1
},
{
"date": "2022-08-22",
"count": 1
},
{
"date": "2022-08-26",
"count": 1
},
{
"date": "2022-08-31",
"count": 1
},
{
"date": "2022-09-02",
"count": 1
},
{
"date": "2022-09-05",
"count": 1
},
{
"date": "2022-09-07",
"count": 1
},
{
"date": "2022-09-11",
"count": 1
},
{
"date": "2022-09-22",
"count": 2
},
{
"date": "2022-10-09",
"count": 1
},
{
"date": "2022-10-11",
"count": 1
},
{
"date": "2022-10-12",
"count": 1
},
{
"date": "2022-10-17",
"count": 1
},
{
"date": "2022-11-03",
"count": 1
},
{
"date": "2022-11-08",
"count": 1
},
{
"date": "2022-11-09",
"count": 1
},
{
"date": "2022-11-11",
"count": 2
},
{
"date": "2022-11-16",
"count": 1
},
{
"date": "2022-11-19",
"count": 1
},
{
"date": "2022-12-05",
"count": 1
},
{
"date": "2022-12-09",
"count": 1
},
{
"date": "2022-12-11",
"count": 1
},
{
"date": "2022-12-17",
"count": 1
},
{
"date": "2022-12-19",
"count": 1
},
{
"date": "2022-12-20",
"count": 1
},
{
"date": "2022-12-27",
"count": 1
},
{
"date": "2022-12-28",
"count": 1
},
{
"date": "2023-01-04",
"count": 1
},
{
"date": "2023-01-05",
"count": 1
},
{
"date": "2023-01-09",
"count": 1
},
{
"date": "2023-01-10",
"count": 2
},
{
"date": "2023-01-17",
"count": 2
},
{
"date": "2023-01-20",
"count": 1
},
{
"date": "2023-01-22",
"count": 1
},
{
"date": "2023-02-03",
"count": 1
},
{
"date": "2023-02-09",
"count": 1
},
{
"date": "2023-02-10",
"count": 1
},
{
"date": "2023-02-13",
"count": 4
},
{
"date": "2023-02-18",
"count": 1
},
{
"date": "2023-02-21",
"count": 1
},
{
"date": "2023-03-02",
"count": 1
},
{
"date": "2023-03-07",
"count": 1
},
{
"date": "2023-03-10",
"count": 1
},
{
"date": "2023-03-11",
"count": 1
},
{
"date": "2023-03-12",
"count": 1
},
{
"date": "2023-03-13",
"count": 2
},
{
"date": "2023-03-14",
"count": 2
},
{
"date": "2023-03-16",
"count": 1
},
{
"date": "2023-03-17",
"count": 1
},
{
"date": "2023-03-27",
"count": 1
},
{
"date": "2023-03-29",
"count": 1
},
{
"date": "2023-04-08",
"count": 1
},
{
"date": "2023-04-09",
"count": 1
},
{
"date": "2023-04-10",
"count": 1
},
{
"date": "2023-04-12",
"count": 3
},
{
"date": "2023-04-17",
"count": 1
},
{
"date": "2023-04-18",
"count": 1
},
{
"date": "2023-05-03",
"count": 2
},
{
"date": "2023-05-10",
"count": 2
},
{
"date": "2023-05-11",
"count": 1
},
{
"date": "2023-05-12",
"count": 1
},
{
"date": "2023-05-15",
"count": 1
},
{
"date": "2023-05-17",
"count": 1
},
{
"date": "2023-05-21",
"count": 1
},
{
"date": "2023-05-24",
"count": 3
},
{
"date": "2023-05-31",
"count": 1
},
{
"date": "2023-06-02",
"count": 1
},
{
"date": "2023-06-07",
"count": 1
},
{
"date": "2023-06-16",
"count": 1
},
{
"date": "2023-06-19",
"count": 1
},
{
"date": "2023-06-22",
"count": 1
},
{
"date": "2023-06-27",
"count": 1
},
{
"date": "2023-07-06",
"count": 1
},
{
"date": "2023-07-09",
"count": 1
},
{
"date": "2023-07-14",
"count": 1
},
{
"date": "2023-07-15",
"count": 1
},
{
"date": "2023-07-17",
"count": 1
},
{
"date": "2023-07-19",
"count": 1
},
{
"date": "2023-07-23",
"count": 1
},
{
"date": "2023-07-28",
"count": 1
},
{
"date": "2023-07-31",
"count": 1
},
{
"date": "2023-08-04",
"count": 1
},
{
"date": "2023-08-06",
"count": 1
},
{
"date": "2023-08-24",
"count": 1
},
{
"date": "2023-08-29",
"count": 1
},
{
"date": "2023-08-30",
"count": 4
},
{
"date": "2023-09-06",
"count": 2
},
{
"date": "2023-09-09",
"count": 1
},
{
"date": "2023-09-26",
"count": 1
},
{
"date": "2023-10-05",
"count": 1
},
{
"date": "2023-10-07",
"count": 1
},
{
"date": "2023-10-08",
"count": 1
},
{
"date": "2023-10-12",
"count": 1
},
{
"date": "2023-10-18",
"count": 1
},
{
"date": "2023-10-20",
"count": 1
},
{
"date": "2023-10-27",
"count": 1
},
{
"date": "2023-10-30",
"count": 2
},
{
"date": "2023-11-01",
"count": 2
},
{
"date": "2023-11-06",
"count": 1
},
{
"date": "2023-11-09",
"count": 1
},
{
"date": "2023-11-11",
"count": 1
},
{
"date": "2023-11-22",
"count": 1
},
{
"date": "2023-11-28",
"count": 1
},
{
"date": "2023-11-29",
"count": 1
},
{
"date": "2023-12-04",
"count": 3
},
{
"date": "2023-12-12",
"count": 1
},
{
"date": "2023-12-13",
"count": 1
},
{
"date": "2023-12-15",
"count": 1
},
{
"date": "2023-12-16",
"count": 1
},
{
"date": "2023-12-18",
"count": 1
},
{
"date": "2023-12-23",
"count": 1
},
{
"date": "2023-12-24",
"count": 2
},
{
"date": "2024-01-06",
"count": 1
},
{
"date": "2024-01-10",
"count": 1
},
{
"date": "2024-01-14",
"count": 1
},
{
"date": "2024-01-17",
"count": 1
},
{
"date": "2024-01-19",
"count": 1
},
{
"date": "2024-01-22",
"count": 1
},
{
"date": "2024-01-24",
"count": 1
},
{
"date": "2024-01-30",
"count": 1
},
{
"date": "2024-02-03",
"count": 1
},
{
"date": "2024-02-12",
"count": 1
},
{
"date": "2024-02-13",
"count": 1
},
{
"date": "2024-02-14",
"count": 1
},
{
"date": "2024-02-15",
"count": 1
},
{
"date": "2024-02-16",
"count": 1
},
{
"date": "2024-03-02",
"count": 1
},
{
"date": "2024-03-03",
"count": 1
},
{
"date": "2024-03-04",
"count": 1
},
{
"date": "2024-03-06",
"count": 2
},
{
"date": "2024-03-07",
"count": 2
},
{
"date": "2024-03-11",
"count": 1
},
{
"date": "2024-03-17",
"count": 1
},
{
"date": "2024-03-18",
"count": 1
},
{
"date": "2024-03-23",
"count": 1
},
{
"date": "2024-04-17",
"count": 1
},
{
"date": "2024-04-22",
"count": 1
},
{
"date": "2024-04-30",
"count": 1
},
{
"date": "2024-05-03",
"count": 2
},
{
"date": "2024-05-13",
"count": 1
},
{
"date": "2024-05-14",
"count": 1
},
{
"date": "2024-05-21",
"count": 1
},
{
"date": "2024-05-23",
"count": 1
},
{
"date": "2024-05-30",
"count": 1
},
{
"date": "2024-06-02",
"count": 1
},
{
"date": "2024-06-05",
"count": 1
},
{
"date": "2024-06-06",
"count": 1
},
{
"date": "2024-06-13",
"count": 1
},
{
"date": "2024-06-14",
"count": 1
},
{
"date": "2024-06-19",
"count": 1
},
{
"date": "2024-07-06",
"count": 1
},
{
"date": "2024-07-11",
"count": 1
},
{
"date": "2024-07-18",
"count": 1
},
{
"date": "2024-07-19",
"count": 2
},
{
"date": "2024-07-21",
"count": 1
},
{
"date": "2024-07-23",
"count": 1
},
{
"date": "2024-07-30",
"count": 2
},
{
"date": "2024-08-05",
"count": 1
},
{
"date": "2024-08-13",
"count": 1
},
{
"date": "2024-08-14",
"count": 1
},
{
"date": "2024-08-19",
"count": 1
},
{
"date": "2024-08-20",
"count": 1
},
{
"date": "2024-08-21",
"count": 1
},
{
"date": "2024-08-25",
"count": 1
},
{
"date": "2024-08-27",
"count": 1
},
{
"date": "2024-09-03",
"count": 2
},
{
"date": "2024-09-08",
"count": 1
},
{
"date": "2024-09-12",
"count": 1
},
{
"date": "2024-09-16",
"count": 1
},
{
"date": "2024-09-19",
"count": 1
},
{
"date": "2024-09-24",
"count": 1
},
{
"date": "2024-09-25",
"count": 1
},
{
"date": "2024-10-02",
"count": 1
},
{
"date": "2024-10-05",
"count": 1
},
{
"date": "2024-10-09",
"count": 1
},
{
"date": "2024-10-10",
"count": 1
},
{
"date": "2024-10-19",
"count": 1
},
{
"date": "2024-10-23",
"count": 1
},
{
"date": "2024-10-28",
"count": 2
},
{
"date": "2024-11-01",
"count": 2
},
{
"date": "2024-11-02",
"count": 1
},
{
"date": "2024-11-03",
"count": 1
},
{
"date": "2024-11-07",
"count": 1
},
{
"date": "2024-11-16",
"count": 1
},
{
"date": "2024-11-20",
"count": 1
},
{
"date": "2024-11-24",
"count": 1
},
{
"date": "2024-11-27",
"count": 1
},
{
"date": "2024-11-29",
"count": 1
},
{
"date": "2024-12-02",
"count": 1
},
{
"date": "2024-12-04",
"count": 1
},
{
"date": "2024-12-12",
"count": 1
},
{
"date": "2024-12-16",
"count": 1
},
{
"date": "2024-12-20",
"count": 1
},
{
"date": "2024-12-31",
"count": 1
},
{
"date": "2025-01-01",
"count": 1
},
{
"date": "2025-01-02",
"count": 1
},
{
"date": "2025-01-07",
"count": 1
},
{
"date": "2025-01-08",
"count": 1
},
{
"date": "2025-01-10",
"count": 1
},
{
"date": "2025-01-14",
"count": 1
},
{
"date": "2025-01-15",
"count": 2
},
{
"date": "2025-01-28",
"count": 1
},
{
"date": "2025-01-30",
"count": 1
},
{
"date": "2025-02-11",
"count": 1
},
{
"date": "2025-02-17",
"count": 1
},
{
"date": "2025-02-19",
"count": 2
},
{
"date": "2025-02-24",
"count": 1
},
{
"date": "2025-02-28",
"count": 1
},
{
"date": "2025-03-04",
"count": 1
},
{
"date": "2025-03-05",
"count": 1
},
{
"date": "2025-03-06",
"count": 2
},
{
"date": "2025-03-13",
"count": 1
},
{
"date": "2025-03-24",
"count": 1
},
{
"date": "2025-03-27",
"count": 1
},
{
"date": "2025-04-04",
"count": 1
},
{
"date": "2025-04-06",
"count": 1
},
{
"date": "2025-04-08",
"count": 1
},
{
"date": "2025-04-16",
"count": 1
},
{
"date": "2025-04-22",
"count": 2
},
{
"date": "2025-05-08",
"count": 1
},
{
"date": "2025-05-14",
"count": 1
},
{
"date": "2025-05-15",
"count": 1
},
{
"date": "2025-05-16",
"count": 2
},
{
"date": "2025-05-17",
"count": 1
},
{
"date": "2025-05-22",
"count": 1
},
{
"date": "2025-06-01",
"count": 1
},
{
"date": "2025-06-04",
"count": 1
},
{
"date": "2025-06-06",
"count": 1
},
{
"date": "2025-06-10",
"count": 1
},
{
"date": "2025-06-11",
"count": 1
},
{
"date": "2025-06-14",
"count": 1
},
{
"date": "2025-06-19",
"count": 2
},
{
"date": "2025-06-20",
"count": 1
},
{
"date": "2025-06-25",
"count": 1
},
{
"date": "2025-06-29",
"count": 1
},
{
"date": "2025-06-30",
"count": 1
},
{
"date": "2025-07-11",
"count": 1
},
{
"date": "2025-07-15",
"count": 1
},
{
"date": "2025-07-23",
"count": 1
},
{
"date": "2025-07-31",
"count": 2
},
{
"date": "2025-08-01",
"count": 1
},
{
"date": "2025-08-12",
"count": 2
},
{
"date": "2025-08-14",
"count": 1
},
{
"date": "2025-08-29",
"count": 2
},
{
"date": "2025-09-04",
"count": 1
},
{
"date": "2025-09-06",
"count": 1
},
{
"date": "2025-09-15",
"count": 1
},
{
"date": "2025-09-18",
"count": 1
},
{
"date": "2025-09-19",
"count": 1
},
{
"date": "2025-09-21",
"count": 1
},
{
"date": "2025-10-09",
"count": 1
},
{
"date": "2025-11-09",
"count": 1
},
{
"date": "2025-12-02",
"count": 1
},
{
"date": "2025-12-07",
"count": 1
},
{
"date": "2025-12-10",
"count": 2
},
{
"date": "2025-12-20",
"count": 1
},
{
"date": "2025-12-23",
"count": 1
},
{
"date": "2026-01-04",
"count": 1
},
{
"date": "2026-01-14",
"count": 1
},
{
"date": "2026-01-16",
"count": 2
},
{
"date": "2026-01-20",
"count": 1
},
{
"date": "2026-01-28",
"count": 1
},
{
"date": "2026-02-01",
"count": 1
},
{
"date": "2026-02-03",
"count": 2
},
{
"date": "2026-02-05",
"count": 1
},
{
"date": "2026-02-13",
"count": 1
},
{
"date": "2026-03-10",
"count": 1
},
{
"date": "2026-03-26",
"count": 1
},
{
"date": "2026-03-28",
"count": 1
},
{
"date": "2026-04-12",
"count": 1
},
{
"date": "2026-04-15",
"count": 2
},
{
"date": "2026-04-17",
"count": 1
},
{
"date": "2026-05-01",
"count": 1
},
{
"date": "2026-05-19",
"count": 1
},
{
"date": "2026-05-23",
"count": 1
},
{
"date": "2026-05-24",
"count": 1
},
{
"date": "2026-05-25",
"count": 7
},
{
"date": "2026-05-30",
"count": 1
},
{
"date": "2026-06-13",
"count": 1
},
{
"date": "2026-06-24",
"count": 1
},
{
"date": "2026-06-29",
"count": 1
},
{
"date": "2026-07-09",
"count": 1
},
{
"date": "2026-07-11",
"count": 1
},
{
"date": "2026-07-14",
"count": 1
},
{
"date": "2026-07-21",
"count": 1
}
],
"complete": false,
"collected": 1000,
"total_stars": 1925
},
"open_issues_and_prs": 65
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [
"examples"
],
"has_llms_txt": false,
"has_dockerfile": true,
"has_mcp_signal": false,
"bootstrap_files": [],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [],
"toolchain_manifests": [],
"largest_source_bytes": 63485,
"source_files_sampled": 133,
"oversized_source_files": 1,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"dependencies": {
"manifests": [
"Gemfile"
],
"advisories": {
"error": null,
"scope": "repository_graph",
"source": "osv",
"findings": [],
"collected": true,
"malicious": [],
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 2,
"malicious_count": 0,
"assessed_package": null,
"unassessed_count": 13,
"direct_affected_count": 0
},
"ecosystems": [
"rubygems"
],
"dependencies": [
{
"name": "sorbet",
"manifest": "Gemfile",
"ecosystem": "rubygems",
"version_constraint": "0.6.12872"
},
{
"name": "tapioca",
"manifest": "Gemfile",
"ecosystem": "rubygems",
"version_constraint": "0.17.10"
},
{
"name": "spoom",
"manifest": "Gemfile",
"ecosystem": "rubygems",
"version_constraint": null
},
{
"name": "rbs-inline",
"manifest": "Gemfile",
"ecosystem": "rubygems",
"version_constraint": null
},
{
"name": "webrick",
"manifest": "Gemfile",
"ecosystem": "rubygems",
"version_constraint": null
}
],
"all_dependencies": {
"error": null,
"source": "github-sbom",
"packages": [
{
"name": "rbs-inline",
"direct": true,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "sorbet",
"direct": true,
"version": "0.6.12872",
"ecosystem": "rubygems"
},
{
"name": "spoom",
"direct": true,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "tapioca",
"direct": true,
"version": "0.17.10",
"ecosystem": "rubygems"
},
{
"name": "webrick",
"direct": true,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "afm",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "cane",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "hashery",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "morecane",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "pry",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "rake",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "rdoc",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "rspec",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "ruby-rc4",
"direct": false,
"version": null,
"ecosystem": "rubygems"
},
{
"name": "ttfunk",
"direct": false,
"version": null,
"ecosystem": "rubygems"
}
],
"collected": true,
"truncated": false,
"total_count": 15,
"direct_count": 5,
"indirect_count": 10
}
},
"maintainership": {
"issues": {
"open_prs": 4,
"merged_prs": 282,
"open_issues": 61,
"closed_ratio": 0.767,
"closed_issues": 201,
"closed_unmerged_prs": 57
},
"bus_factor": 1,
"bot_contributors": 0,
"top_contributors": [
{
"type": "User",
"login": "yob",
"commits": 1481,
"avatar_url": "https://avatars.githubusercontent.com/u/8132?v=4"
},
{
"type": "User",
"login": "sebbASF",
"commits": 28,
"avatar_url": "https://avatars.githubusercontent.com/u/16689231?v=4"
},
{
"type": "User",
"login": "bernerdschaefer",
"commits": 22,
"avatar_url": "https://avatars.githubusercontent.com/u/193?v=4"
},
{
"type": "User",
"login": "alexdowad",
"commits": 12,
"avatar_url": "https://avatars.githubusercontent.com/u/1067359?v=4"
},
{
"type": "User",
"login": "3jb",
"commits": 12,
"avatar_url": "https://avatars.githubusercontent.com/u/784728?v=4"
},
{
"type": "User",
"login": "gyuchang",
"commits": 9,
"avatar_url": "https://avatars.githubusercontent.com/u/813607?v=4"
},
{
"type": "User",
"login": "sbilharz",
"commits": 9,
"avatar_url": "https://avatars.githubusercontent.com/u/11405612?v=4"
},
{
"type": "User",
"login": "tardate",
"commits": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/16432?v=4"
},
{
"type": "User",
"login": "koriroys",
"commits": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/1118323?v=4"
},
{
"type": "User",
"login": "amatsuda",
"commits": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/11493?v=4"
}
],
"contributors_sampled": 61,
"top_contributor_share": 0.893
},
"quality_signals": {
"has_ci": false,
"has_tests": true,
"ci_workflows": [],
"has_docs_dir": false,
"linter_configs": [],
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": 0,
"reason": "branch protection not enabled on development/release branches",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": 8,
"reason": "8 out of 9 merged PRs checked by a CI test -- score normalized to 8",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 0,
"reason": "Found 1/11 approved changesets -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 10,
"reason": "project has 26 contributing companies or organizations",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": null,
"reason": "no workflows found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 0,
"reason": "no update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 0,
"reason": "project is not fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 10,
"reason": "30 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": null,
"reason": "packaging workflow not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": 0,
"reason": "dependency not pinned by hash detected -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 0,
"reason": "SAST tool is not run on all commits -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 0,
"reason": "security policy file not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": null,
"reason": "no releases found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": null,
"reason": "No tokens found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 10,
"reason": "0 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "578db3d49b9cfc458b186915896ae29f62ccd126",
"ran_at": "2026-07-21T23:33:27Z",
"aggregate_score": 3.9,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": false,
"has_security_policy": false,
"has_dependabot_config": false
},
"contribution_flow": {
"collected": true,
"ci_last_run_at": "2026-07-15T02:30:48Z",
"oldest_open_prs": [
{
"number": 209,
"created_at": "2017-03-13T01:21:23Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 301,
"created_at": "2019-10-18T04:44:28Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 326,
"created_at": "2020-01-31T09:41:40Z",
"last_comment_at": "2020-02-26T08:00:05Z",
"last_comment_author": "bwl21"
},
{
"number": 524,
"created_at": "2023-10-25T20:11:29Z",
"last_comment_at": "2025-03-13T08:19:15Z",
"last_comment_author": "rgarner"
}
],
"last_merged_pr_at": "2026-07-15T02:30:47Z",
"ci_last_conclusion": null,
"oldest_open_issues": [
{
"number": 45,
"created_at": "2012-02-06T00:30:45Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 55,
"created_at": "2012-05-24T21:30:59Z",
"last_comment_at": "2012-06-11T19:30:02Z",
"last_comment_author": "andrewajo"
},
{
"number": 72,
"created_at": "2013-01-01T17:45:39Z",
"last_comment_at": "2014-04-24T03:39:02Z",
"last_comment_author": "zachary-wq"
},
{
"number": 76,
"created_at": "2013-01-18T06:12:44Z",
"last_comment_at": "2013-02-25T14:24:59Z",
"last_comment_author": "sandsfish"
},
{
"number": 84,
"created_at": "2013-02-16T15:30:01Z",
"last_comment_at": "2013-02-26T04:03:50Z",
"last_comment_author": "utensil"
},
{
"number": 93,
"created_at": "2013-04-10T11:33:02Z",
"last_comment_at": "2013-04-10T16:37:07Z",
"last_comment_author": "dpzaba"
},
{
"number": 100,
"created_at": "2013-05-09T11:17:50Z",
"last_comment_at": "2013-05-13T07:39:01Z",
"last_comment_author": "yob"
},
{
"number": 114,
"created_at": "2013-10-06T11:23:27Z",
"last_comment_at": "2020-01-12T15:30:03Z",
"last_comment_author": "toy"
},
{
"number": 115,
"created_at": "2013-10-27T11:26:06Z",
"last_comment_at": "2013-10-27T21:05:37Z",
"last_comment_author": "mejibyte"
},
{
"number": 118,
"created_at": "2014-01-07T18:17:47Z",
"last_comment_at": "2017-02-14T14:01:16Z",
"last_comment_author": "yob"
},
{
"number": 119,
"created_at": "2014-02-03T01:47:25Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 127,
"created_at": "2014-05-05T05:45:04Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 132,
"created_at": "2014-06-21T20:38:48Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 137,
"created_at": "2014-12-04T18:38:10Z",
"last_comment_at": "2021-10-17T21:39:50Z",
"last_comment_author": "sebbASF"
},
{
"number": 170,
"created_at": "2016-03-18T13:31:06Z",
"last_comment_at": "2017-02-14T13:47:23Z",
"last_comment_author": "yob"
},
{
"number": 205,
"created_at": "2017-03-05T19:31:37Z",
"last_comment_at": "2017-03-05T22:29:06Z",
"last_comment_author": "yob"
},
{
"number": 214,
"created_at": "2017-04-03T12:50:04Z",
"last_comment_at": "2022-01-29T23:49:17Z",
"last_comment_author": "yob"
},
{
"number": 216,
"created_at": "2017-05-02T12:53:37Z",
"last_comment_at": "2022-01-13T00:02:38Z",
"last_comment_author": "yob"
},
{
"number": 260,
"created_at": "2017-11-11T19:02:46Z",
"last_comment_at": "2019-10-26T12:09:56Z",
"last_comment_author": "yob"
},
{
"number": 272,
"created_at": "2018-02-03T22:20:43Z",
"last_comment_at": "2019-03-26T02:54:08Z",
"last_comment_author": "rockorequin"
}
]
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/yob/pdf-reader",
"host": "github.com",
"name": "pdf-reader",
"owner": "yob"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": null,
"notes": [],
"value": 59,
"inputs": {
"security": 51,
"vitality": 64,
"community": 76,
"governance": 62,
"engineering": 40
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "moderate",
"name": "Vitality",
"value": 64,
"weight": 0.22,
"metrics": [
{
"key": "development_activity",
"band": "good",
"name": "Development activity",
"note": null,
"notes": [],
"value": 74,
"inputs": {
"commits_last_year": 121,
"human_commit_share": 1,
"days_since_last_push": 6,
"active_weeks_last_year": 15
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 6 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "push_recency",
"params": {
"days": 6
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "15/52 weeks with commits",
"points": 10.4,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 15
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "121 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 121
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "30 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "moderate",
"name": "Release discipline",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 50,
"inputs": {
"releases_count": 64,
"latest_release_tag": "v2.15.1",
"releases_from_tags": true,
"days_since_latest_release": 205,
"mean_days_between_releases": 158.1
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "64 version tags (no GitHub releases)",
"points": 16.2,
"status": "partial",
"details": [
{
"code": "version_tags_no_releases",
"params": {
"count": 64
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 205 days ago",
"points": 16.2,
"status": "partial",
"details": [
{
"code": "release_recency",
"params": {
"days": 205
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~158.1 days",
"points": 12.6,
"status": "partial",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 158.1
}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "excellent",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "maintained",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": null,
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": 6,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "last human commit 6 days ago",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_maintained",
"params": {
"days": 6
}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "good",
"name": "Community & Adoption",
"value": 76,
"weight": 0.18,
"metrics": [
{
"key": "popularity",
"band": "good",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 83,
"inputs": {
"forks": 284,
"stars": 1925,
"watchers": 46,
"growth_state": "organic",
"growth_factor_pct": 100
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "1,925 stars",
"points": 53.3,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 1925
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "284 forks",
"points": 20.4,
"status": "partial",
"details": [
{
"code": "forks",
"params": {
"count": 284
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "46 watchers",
"points": 9.2,
"status": "partial",
"details": [
{
"code": "watchers",
"params": {
"count": 46
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "moderate",
"name": "Community health",
"note": null,
"notes": [],
"value": 50,
"inputs": {
"has_readme": true,
"has_license": true,
"has_contributing": false,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (MIT)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "MIT"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
},
{
"key": "ecosystem_adoption",
"band": "excellent",
"name": "Ecosystem adoption (downloads)",
"note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"registry_dependents"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 100,
"inputs": {
"packages": [
"pdf-reader"
],
"dependents": null,
"ecosystems": "rubygems",
"total_downloads": 109562630,
"monthly_downloads": null
},
"components": [
{
"key": "total_downloads",
"name": "Total downloads",
"detail": "109,562,630 downloads all-time across rubygems",
"points": 80,
"status": "met",
"details": [
{
"code": "downloads_total",
"params": {
"count": 109562630,
"ecosystems": "rubygems"
}
}
],
"max_points": 80
},
{
"key": "registry_dependents",
"name": "Registry dependents",
"detail": "not reported by this ecosystem",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_reported_by_this_ecosystem",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "moderate",
"name": "Sustainability & Governance",
"value": 62,
"weight": 0.24,
"metrics": [
{
"key": "maintainer_resilience",
"band": "at_risk",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 35,
"inputs": {
"bus_factor": 1,
"contributors_sampled": 61,
"top_contributor_share": 0.893
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "1 contributor(s) cover half of all commits",
"points": 9,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 1
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 89% of commits",
"points": 2.4,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 89
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "61 contributors",
"points": 13.5,
"status": "met",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 61
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 26 contributing companies or organizations",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "moderate",
"name": "Issue & PR responsiveness",
"note": null,
"notes": [],
"value": 68,
"inputs": {
"merged_prs": 282,
"open_issues": 61,
"closed_issues": 201,
"issue_closed_ratio": 0.767,
"closed_unmerged_prs": 57
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "77% of issues closed",
"points": 35.9,
"status": "partial",
"details": [
{
"code": "issues_closed_share",
"params": {
"share": 77
}
}
],
"max_points": 46.75
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "282/339 decided PRs merged",
"points": 31.8,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 282,
"decided": 339
}
}
],
"max_points": 38.25
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 1/11 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "moderate",
"name": "Ownership & stewardship",
"note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"verified_domain"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 65,
"inputs": {
"followers": 236,
"owner_type": "User",
"is_verified": null,
"owner_login": "yob",
"public_repos": 143,
"account_age_days": 6664
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "personal (user) account",
"points": 10,
"status": "partial",
"details": [
{
"code": "owner_personal",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": "not applicable to user accounts",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_applicable_to_user_accounts",
"params": {}
}
],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "236 followers of yob",
"points": 17.1,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 236,
"login": "yob"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "143 public repos, account ~18 yr old",
"points": 25,
"status": "met",
"details": [
{
"code": "public_repos",
"params": {
"count": 143
}
},
{
"code": "account_age_years",
"params": {
"years": 18
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "excellent",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 91,
"inputs": {
"packages": [
"pdf-reader"
],
"ecosystems": "rubygems",
"any_deprecated": false,
"min_days_since_publish": 205
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on rubygems",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "rubygems"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 205 days ago",
"points": 26,
"status": "partial",
"details": [
{
"code": "publish_recency",
"params": {
"days": 205
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "64 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 64
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "at_risk",
"name": "Engineering Quality",
"value": 40,
"weight": 0.2,
"metrics": [
{
"key": "engineering_practices",
"band": "at_risk",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"has_ci": false,
"has_tests": true,
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "8 out of 9 merged PRs checked by a CI test -- score normalized to 8",
"points": 16,
"status": "partial",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "at_risk",
"name": "Documentation",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"topics": [],
"has_wiki": false,
"homepage": null,
"has_readme": true,
"has_docs_dir": false,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "moderate",
"name": "Security",
"value": 51,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "at_risk",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): Dangerous-Workflow, Packaging, Signed-Releases, Token-Permissions. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"dangerous_workflow",
"packaging",
"signed_releases",
"token_permissions"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 39,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 14,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 4,
"scorecard_aggregate": 3.9
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "branch protection not enabled on development/release branches",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "8 out of 9 merged PRs checked by a CI test -- score normalized to 8",
"points": 2,
"status": "partial",
"details": [],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 1/11 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 26 contributing companies or organizations",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no workflows found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "no update tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is not fuzzed",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "30 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow not detected",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is not run on all commits -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file not detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "No tokens found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "0 existing vulnerabilities detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "dependency_advisories",
"band": "excellent",
"name": "Dependency advisories",
"note": "Excluded from scoring (no data or not applicable): Indirect dependencies free of known advisories, No advisories left outstanding. Remaining weights renormalized. Matched 2 resolved dependencies against OSV; 13 could not be assessed (no resolved version, an unsupported ecosystem, or beyond the reported package list). This repository publishes no package the index resolves, so the repository dependency graph was assessed instead. That graph mixes development and test pins with shipped dependencies, so only the declared runtime dependencies are scored; transitive findings are reported as context and excluded from the score. Reachability is not analyzed.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"indirect_dependencies_free_of_known_advisories",
"no_advisories_left_outstanding"
]
}
},
{
"code": "weights_renormalized",
"params": {}
},
{
"code": "advisories_scope_repository",
"params": {
"assessed": 2
}
},
{
"code": "advisories_unassessed",
"params": {
"count": 13
}
},
{
"code": "advisories_repo_graph_caveat",
"params": {}
},
{
"code": "advisories_reachability",
"params": {}
}
],
"value": 100,
"inputs": {
"source": "osv",
"advisories": 0,
"affected_packages": 0,
"assessed_packages": 2,
"unassessed_packages": 13,
"affected_by_severity": "none",
"direct_affected_packages": 0
},
"components": [
{
"key": "direct_dependencies_free_of_known_advisories",
"name": "Direct dependencies free of known advisories",
"detail": "no direct dependency carries a known advisory",
"points": 35,
"status": "met",
"details": [
{
"code": "no_direct_advisories",
"params": {}
}
],
"max_points": 35
},
{
"key": "indirect_dependencies_free_of_known_advisories",
"name": "Indirect dependencies free of known advisories",
"detail": "transitive set not separable from development and test dependencies in this scope",
"points": 0,
"status": "excluded",
"details": [
{
"code": "advisories_scope_not_separable",
"params": {}
}
],
"max_points": 25
},
{
"key": "no_advisories_left_outstanding",
"name": "No advisories left outstanding",
"detail": "no advisory carries a publication date",
"points": 0,
"status": "excluded",
"details": [
{
"code": "advisories_no_publication_date",
"params": {}
}
],
"max_points": 40
}
]
},
{
"key": "malicious_dependencies",
"band": "excellent",
"name": "Malicious dependencies",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"source": "osv",
"meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
"packages": [],
"red_flag": false,
"assessed_packages": 2,
"malicious_packages": 0,
"direct_malicious_packages": 0,
"withdrawn_malicious_packages": 0,
"installable_malicious_packages": 0
},
"components": [
{
"key": "no_dependency_reported_as_a_malicious_package",
"name": "No dependency reported as a malicious package",
"detail": "no dependency is reported as a malicious package",
"points": 100,
"status": "met",
"details": [
{
"code": "no_malicious_dependencies",
"params": {}
}
],
"max_points": 100
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "excellent",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"review_only_matches": 0,
"assessed_self_published_locations": 15
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "at_risk",
"name": "AI Readiness",
"value": 40,
"weight": 0,
"metrics": [
{
"key": "ai_agent_context",
"band": "at_risk",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.75,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": "no CLAUDE.md / AGENTS.md / editor rules",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_instructions",
"params": {}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "75 of 100 human commits state their intent (structured subject or explanatory body)",
"points": 40,
"status": "met",
"details": [
{
"code": "legible_history",
"params": {
"legible": 75,
"sampled": 100
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "at_risk",
"name": "Verify loop (build / test / typecheck)",
"note": null,
"notes": [],
"value": 34,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [],
"has_dockerfile": true,
"typed_language": false,
"bootstrap_files": [],
"has_devcontainer": false,
"has_linter_config": false,
"typecheck_configs": [],
"agent_commit_share": 0.01,
"toolchain_manifests": [],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": "Dockerfile",
"points": 10,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "Dockerfile"
}
}
],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "1 of the last 100 commits agent-authored or agent-credited",
"points": 2,
"status": "partial",
"details": [
{
"code": "agent_authored_commits",
"params": {
"count": 1,
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "no automated dependency updates observed",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_dependency_automation",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "moderate",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 55,
"inputs": {
"primary_language": "Ruby",
"largest_source_bytes": 63485,
"source_files_sampled": 133,
"oversized_source_files": 1
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "Ruby without a type-check config",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_typecheck_config_language",
"params": {
"language": "Ruby"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "1/133 source files over 60KB",
"points": 54.6,
"status": "partial",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 133,
"oversized": 1
}
}
],
"max_points": 55
}
]
},
{
"key": "ai_interfaces",
"band": "at_risk",
"name": "Machine-readable interfaces",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"example_dirs": [
"examples"
],
"has_mcp_signal": false,
"api_schema_files": []
},
"components": [
{
"key": "api_schema_openapi_graphql_proto",
"name": "API schema (OpenAPI/GraphQL/proto)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 40
},
{
"key": "mcp_server",
"name": "MCP server",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "runnable_examples",
"name": "Runnable examples",
"detail": "examples",
"points": 40,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "examples"
}
}
],
"max_points": 40
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
}
],
"metrics_version": "1.13.0"
},
"warnings": [],
"report_type": "repository",
"generated_at": "2026-07-21T23:33:54.042750Z",
"schema_version": "0.26.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/y/yob/pdf-reader.svg",
"full_name": "yob/pdf-reader",
"license_state": "standard",
"license_spdx": "MIT"
}