gitoriaLog in with ident

tickets

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commitc7bd2645c7bd2645tickets: Hybriel master 73267707 (#122 fixed); compactNow workaround removed (#110 covered)mrec7bd2645/jsoncheck.hl

4.5 KB

  1. // jsoncheck.hl — WORKAROUND for hybriel ticket #12 (no soft JSON.parse): JSON.parse throws on
  2. // invalid input and a Hybriel program cannot catch it, so the framework answers 500 with the
  3. // source location. The API therefore checks the body's JSON SYNTAX by hand first (RFC 8259
  4. // grammar, nesting depth capped at 64, NO LONE \u surrogate escapes — hl's JSON.parse still
  5. // refuses those; a valid pair parses since hybriel#15) and answers 400. REMOVE this file and its one call in
  6. // api.hl (readBody) once JSON.parse has a failure path (#12).
  7. //
  8. // jsonErrorAt(text) → -1 when `text` is one valid JSON value (surrounded by whitespace),
  9. // else the 0-based character position of the first offence.
  10. static maxDepth = 64
  11. static isWs = (c) => { return c == 32 || c == 9 || c == 10 || c == 13 }
  12. static isDigit = (c) => { return c >= 48 && c <= 57 }
  13. static isHex = (c) => { return (c >= 48 && c <= 57) || (c >= 65 && c <= 70) || (c >= 97 && c <= 102) }
  14. static hexValue = (c) => { return c <= 57 ? c - 48 : (c <= 70 ? c - 55 : c - 87) }
  15. // the 4 hex digits at `at` as a number, or -1
  16. static hexAt = (s, at) => {
  17. if (at + 4 > s.length) { return -1 }
  18. let v = 0
  19. let k = 0
  20. while (k < 4) {
  21. let c = s.charCodeAt(at + k)
  22. if (!isHex(c)) { return -1 }
  23. v = v * 16 + hexValue(c)
  24. k = k + 1
  25. }
  26. return v
  27. }
  28. static skipWs = (s, at) => {
  29. let i = at
  30. while (i < s.length && isWs(s.charCodeAt(i))) { i = i + 1 }
  31. return i
  32. }
  33. // each scanner answers the position after what it read, or -(position + 1) of an offence
  34. static bad = (i) => { return -(i + 1) }
  35. static scanString = (s, at) => {
  36. let i = at
  37. i = i + 1
  38. while (i < s.length) {
  39. let c = s.charCodeAt(i)
  40. if (c == 34) { return i + 1 }
  41. if (c < 32) { return bad(i) }
  42. if (c == 92) {
  43. i = i + 1
  44. if (i >= s.length) { return bad(i) }
  45. let e = s.charCodeAt(i)
  46. if (e == 117) {
  47. let u = hexAt(s, i + 1)
  48. if (u < 0) { return bad(i) }
  49. // SURROGATES (mission 036): a valid pair \ud83d\ude00 parses since hybriel#15; a LONE
  50. // one (a high without a following \u low, or a low alone) still aborts hl's
  51. // JSON.parse → refused here: 400, not 500.
  52. if (u >= 55296 && u <= 56319) {
  53. if (i + 6 >= s.length || s.charCodeAt(i + 5) != 92 || s.charCodeAt(i + 6) != 117) { return bad(i) }
  54. let lo = hexAt(s, i + 7)
  55. if (lo < 56320 || lo > 57343) { return bad(i) }
  56. i = i + 6
  57. } else if (u >= 56320 && u <= 57343) { return bad(i) }
  58. i = i + 4
  59. } else if (!(e == 34 || e == 92 || e == 47 || e == 98 || e == 102 || e == 110 || e == 114 || e == 116)) {
  60. return bad(i)
  61. }
  62. }
  63. i = i + 1
  64. }
  65. return bad(i)
  66. }
  67. static scanDigits = (s, at) => {
  68. let i = at
  69. let start = i
  70. while (i < s.length && isDigit(s.charCodeAt(i))) { i = i + 1 }
  71. return i == start ? bad(i) : i
  72. }
  73. static scanNumber = (s, at) => {
  74. let i = at
  75. if (s.charCodeAt(i) == 45) { i = i + 1 }
  76. if (i >= s.length || !isDigit(s.charCodeAt(i))) { return bad(i) }
  77. if (s.charCodeAt(i) == 48) { i = i + 1 } else { i = scanDigits(s, i) }
  78. if (i < s.length && s.charCodeAt(i) == 46) {
  79. i = scanDigits(s, i + 1)
  80. if (i < 0) { return i }
  81. }
  82. if (i < s.length && (s.charCodeAt(i) == 101 || s.charCodeAt(i) == 69)) {
  83. i = i + 1
  84. if (i < s.length && (s.charCodeAt(i) == 43 || s.charCodeAt(i) == 45)) { i = i + 1 }
  85. i = scanDigits(s, i)
  86. }
  87. return i
  88. }
  89. static scanWord = (s, i, word) => {
  90. return s.slice(i, i + word.length) == word ? i + word.length : bad(i)
  91. }
  92. static scanValue = (s, at, depth) => {
  93. let i = at
  94. i = skipWs(s, i)
  95. if (i >= s.length) { return bad(i) }
  96. let c = s.charCodeAt(i)
  97. if (c == 34) { return scanString(s, i) }
  98. if (c == 45 || isDigit(c)) { return scanNumber(s, i) }
  99. if (c == 116) { return scanWord(s, i, 'true') }
  100. if (c == 102) { return scanWord(s, i, 'false') }
  101. if (c == 110) { return scanWord(s, i, 'null') }
  102. if (c == 123 || c == 91) {
  103. if (depth >= maxDepth) { return bad(i) }
  104. let close = c == 123 ? 125 : 93
  105. i = skipWs(s, i + 1)
  106. if (i < s.length && s.charCodeAt(i) == close) { return i + 1 }
  107. while (true) {
  108. if (c == 123) {
  109. i = skipWs(s, i)
  110. if (i >= s.length || s.charCodeAt(i) != 34) { return bad(i) }
  111. i = scanString(s, i)
  112. if (i < 0) { return i }
  113. i = skipWs(s, i)
  114. if (i >= s.length || s.charCodeAt(i) != 58) { return bad(i) }
  115. i = i + 1
  116. }
  117. i = scanValue(s, i, depth + 1)
  118. if (i < 0) { return i }
  119. i = skipWs(s, i)
  120. if (i >= s.length) { return bad(i) }
  121. let d = s.charCodeAt(i)
  122. if (d == close) { return i + 1 }
  123. if (d != 44) { return bad(i) }
  124. i = i + 1
  125. }
  126. }
  127. return bad(i)
  128. }
  129. static jsonErrorAt = (text) => {
  130. let i = scanValue(text, 0, 0)
  131. if (i < 0) { return -i - 1 }
  132. i = skipWs(text, i)
  133. return i < text.length ? i : -1
  134. }

Branches

Latest commits

  • c7bd2645tickets: Hybriel master 73267707 (#122 fixed); compactNow workaround removed (#110 covered)mre
  • 2ab91ee9tickets: gate checks rows appear once (session sync); re-vendor to ff51cf46 stopped on hybriel#122, stays 837fe120mre
  • e01c2b1dtickets#24: installable app (manifest, service worker, offline list), own icon; gate waits for the hello's pongmre
  • 752fbb7fdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • 38bdd5e4deploy.sh: never send .git or .gitignore to Byrodinmre
  • f12fa1bcState of 2026-09-27, before the move to gitoriamre