@unbrained/pm-cli 2026.10.15 → 2026.10.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/AGENTS.md +1 -0
  4. package/CHANGELOG.md +45 -0
  5. package/dist/cli/extension-command-options.d.ts +3 -1
  6. package/dist/cli/extension-command-options.js +1 -1
  7. package/dist/cli/help-content.js +2 -2
  8. package/dist/cli/register-list-query.js +2 -2
  9. package/dist/cli/register-mutation.js +2 -2
  10. package/dist/cli/register-operations.js +1 -1
  11. package/dist/cli/register-structured-mutation.d.ts +0 -5
  12. package/dist/cli/register-structured-mutation.js +1 -1
  13. package/dist/cli/runtime/invocation-options.js +1 -1
  14. package/dist/cli-bundle/bundle-manifest.json +1 -1
  15. package/dist/cli-bundle/chunks/{chunk-7VHINXZP.js → chunk-4GA4MUTE.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-RJ5N44WY.js → chunk-4V6ONLI6.js} +2 -2
  17. package/dist/cli-bundle/chunks/{chunk-LNKLQYBG.js → chunk-6VZKYJW7.js} +2 -2
  18. package/dist/cli-bundle/chunks/chunk-7PATQWTH.js +20 -0
  19. package/dist/cli-bundle/chunks/chunk-7WIEAGDS.js +2 -0
  20. package/dist/cli-bundle/chunks/{chunk-52EZH2QQ.js → chunk-AETP4JEM.js} +2 -2
  21. package/dist/cli-bundle/chunks/{chunk-WBBW3WQX.js → chunk-FIJOSX2C.js} +28 -28
  22. package/dist/cli-bundle/chunks/chunk-LUKVNL72.js +2 -0
  23. package/dist/cli-bundle/chunks/{chunk-NC3QVGDD.js → chunk-OJ77ZQN3.js} +2 -2
  24. package/dist/cli-bundle/chunks/chunk-PYRREEUB.js +30 -0
  25. package/dist/cli-bundle/chunks/chunk-RHQJ6VWC.js +237 -0
  26. package/dist/cli-bundle/chunks/{chunk-N56CAQBU.js → chunk-RNABUEFB.js} +52 -52
  27. package/dist/cli-bundle/chunks/chunk-TO6CIV4C.js +2 -0
  28. package/dist/cli-bundle/chunks/{chunk-JGKTOBVB.js → chunk-ZZED447K.js} +4 -4
  29. package/dist/cli-bundle/chunks/{register-list-query-FKKFQ66D.js → register-list-query-PUCVYYBD.js} +7 -7
  30. package/dist/cli-bundle/chunks/register-mutation-GGGVBXD5.js +20 -0
  31. package/dist/cli-bundle/chunks/{register-operations-GEQ2H7PS.js → register-operations-NRZ6Q4QE.js} +2 -2
  32. package/dist/cli-bundle/chunks/{register-setup-PSJTQBBO.js → register-setup-MV3P3YSF.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-2MPQUMTF.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-H3P77KVU.js → chunk-5UKCSZWT.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-4KQFMOM7.js → chunk-A4ZHHTEX.js} +5 -5
  36. package/dist/cli-bundle/focused-chunks/{chunk-5OO54BIV.js → chunk-B4YLP5ZB.js} +3 -3
  37. package/dist/cli-bundle/focused-chunks/chunk-BFEMT5U3.js +20 -0
  38. package/dist/cli-bundle/focused-chunks/{chunk-THYQR3ON.js → chunk-FW7CSSKL.js} +61 -61
  39. package/dist/cli-bundle/focused-chunks/{chunk-3RNUMVK4.js → chunk-HYPRPSYD.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-FQKPOZKS.js → chunk-IRVL4UXR.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-Q4VNCTR2.js → chunk-L55KQ2KT.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/{chunk-52I2SNKJ.js → chunk-MASIOFPG.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/chunk-RJ3EIHDU.js +26 -0
  44. package/dist/cli-bundle/focused-chunks/chunk-SEL4Z2OO.js +167 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-TYLG5VEB.js → chunk-TB7F4FCR.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-KIIFVULU.js → chunk-TZUIBG3P.js} +2 -2
  47. package/dist/cli-bundle/focused-chunks/{chunk-N6FUKTZS.js → chunk-Z7YIFCNE.js} +2 -2
  48. package/dist/cli-bundle/focused-chunks/{chunk-5KRACGBK.js → chunk-ZDBS2DCT.js} +3 -3
  49. package/dist/cli-bundle/main.js +27 -27
  50. package/dist/cli-bundle/sdk-authoring.js +1 -1
  51. package/dist/cli-bundle/sdk-contracts.js +2 -2
  52. package/dist/cli-bundle/sdk-core.js +23 -23
  53. package/dist/cli-bundle/sdk-governance.js +1 -1
  54. package/dist/cli-bundle/sdk-graph.js +1 -1
  55. package/dist/cli-bundle/sdk-merge.js +55 -54
  56. package/dist/cli-bundle/sdk-query.js +1 -1
  57. package/dist/cli-bundle/sdk-runtime.js +1 -1
  58. package/dist/cli-bundle/sdk-testing.js +1 -1
  59. package/dist/cli-bundle/sdk.js +7 -7
  60. package/dist/core/extensions/extension-types.d.ts +15 -0
  61. package/dist/core/extensions/extension-types.js +1 -1
  62. package/dist/core/extensions/flag-value-types.d.ts +16 -0
  63. package/dist/core/extensions/flag-value-types.js +1 -1
  64. package/dist/core/extensions/registration-contracts.js +1 -1
  65. package/dist/core/history/event-index.d.ts +8 -1
  66. package/dist/core/history/event-index.js +19 -18
  67. package/dist/core/item/item-format.js +3 -3
  68. package/dist/core/lock/lock.d.ts +8 -1
  69. package/dist/core/lock/lock.js +3 -3
  70. package/dist/core/output/mutation-projection.d.ts +0 -21
  71. package/dist/core/output/mutation-projection.js +1 -1
  72. package/dist/core/output/output.js +2 -2
  73. package/dist/core/schema/type-workflows.d.ts +1 -1
  74. package/dist/core/schema/type-workflows.js +1 -1
  75. package/dist/core/shared/text-normalization.d.ts +2 -0
  76. package/dist/core/shared/text-normalization.js +1 -1
  77. package/dist/core/store/item-mutation-precondition.d.ts +11 -0
  78. package/dist/core/store/item-mutation-precondition.js +2 -0
  79. package/dist/core/store/item-store.js +1 -1
  80. package/dist/core/store/paths.js +1 -1
  81. package/dist/core/telemetry/policy.d.ts +2 -0
  82. package/dist/core/telemetry/policy.js +1 -1
  83. package/dist/core/telemetry/runtime.d.ts +5 -3
  84. package/dist/core/telemetry/runtime.js +3 -3
  85. package/dist/mcp/server.js +1 -1
  86. package/dist/mcp/tool-definitions.js +1 -1
  87. package/dist/sdk/cli-contracts/agent-output-contracts.js +1 -1
  88. package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
  89. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
  90. package/dist/sdk/cli-contracts/registration-helpers.d.ts +1 -1
  91. package/dist/sdk/cli-contracts/registration-helpers.js +1 -1
  92. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  93. package/dist/sdk/cli-contracts/tool-schema.js +1 -1
  94. package/dist/sdk/context-intent-contracts.js +1 -1
  95. package/dist/sdk/core.d.ts +2 -1
  96. package/dist/sdk/core.js +1 -1
  97. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  98. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  99. package/dist/sdk/governance/health/projection.d.ts +37 -0
  100. package/dist/sdk/governance/health/projection.js +2 -0
  101. package/dist/sdk/governance/health.d.ts +7 -18
  102. package/dist/sdk/governance/health.js +2 -2
  103. package/dist/sdk/guide-topics.js +1 -1
  104. package/dist/sdk/index.d.ts +2 -1
  105. package/dist/sdk/index.js +1 -1
  106. package/dist/sdk/item-transaction.d.ts +72 -9
  107. package/dist/sdk/item-transaction.js +1 -1
  108. package/dist/sdk/lifecycle/claim.d.ts +15 -1
  109. package/dist/sdk/lifecycle/claim.js +1 -1
  110. package/dist/sdk/lifecycle/close.d.ts +2 -8
  111. package/dist/sdk/lifecycle/close.js +1 -1
  112. package/dist/sdk/lifecycle/core-item-fields.d.ts +6 -0
  113. package/dist/sdk/lifecycle/core-item-fields.js +2 -0
  114. package/dist/sdk/lifecycle/mcp-actions.js +1 -1
  115. package/dist/sdk/lifecycle/mutation-option-validation.d.ts +2 -0
  116. package/dist/sdk/lifecycle/mutation-option-validation.js +2 -0
  117. package/dist/sdk/lifecycle/release-handoff.d.ts +12 -0
  118. package/dist/sdk/lifecycle/release-handoff.js +2 -0
  119. package/dist/sdk/lifecycle/structured-mutation-plan.d.ts +33 -0
  120. package/dist/sdk/lifecycle/structured-mutation-plan.js +2 -0
  121. package/dist/sdk/lifecycle/task-composition.d.ts +2 -2
  122. package/dist/sdk/lifecycle/task-composition.js +1 -1
  123. package/dist/sdk/lifecycle/update.d.ts +3 -11
  124. package/dist/sdk/lifecycle/update.js +1 -1
  125. package/dist/sdk/lifecycle/workflow-transition.d.ts +13 -0
  126. package/dist/sdk/lifecycle/workflow-transition.js +2 -0
  127. package/dist/sdk/lifecycle-policy.d.ts +12 -2
  128. package/dist/sdk/lifecycle-policy.js +1 -1
  129. package/dist/sdk/mutation-events.js +1 -1
  130. package/dist/sdk/public-surface.json +1 -1
  131. package/dist/sdk/query/list.d.ts +2 -0
  132. package/dist/sdk/query/list.js +1 -1
  133. package/dist/sdk/read-output-budget.js +1 -1
  134. package/dist/sdk/runtime-input.js +1 -1
  135. package/dist/sdk/runtime-primitives.d.ts +1 -1
  136. package/dist/sdk/runtime-primitives.js +1 -1
  137. package/dist/sdk/runtime.d.ts +8 -8
  138. package/dist/sdk/runtime.js +1 -1
  139. package/dist/sdk/structured-mutations.d.ts +52 -1
  140. package/dist/sdk/structured-mutations.js +1 -1
  141. package/dist/sdk/workspace-transaction.js +2 -2
  142. package/dist/types.d.ts +2 -2
  143. package/dist/types.js +1 -1
  144. package/docs/AGENT_GUIDE.md +33 -4
  145. package/docs/CLOSE_EVIDENCE_RECOVERY.md +16 -7
  146. package/docs/COMMANDS.md +111 -10
  147. package/docs/CONFIGURATION.md +22 -1
  148. package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +22 -3
  149. package/docs/ECOSYSTEM_PLANNING_REVIEW.md +5 -1
  150. package/docs/EXTENSIONS.md +1 -1
  151. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +10 -0
  152. package/docs/RELEASING.md +8 -1
  153. package/docs/SDK.md +114 -2
  154. package/docs/SDK_CONTEXT_COORDINATION.md +71 -7
  155. package/docs/SDK_STORAGE_LIFECYCLE.md +24 -2
  156. package/docs/TESTING.md +23 -0
  157. package/docs/examples/ci/github-actions-pm-extension-gate.yml +1 -1
  158. package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +1 -1
  159. package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +1 -1
  160. package/docs/generated/FLAG_LEXICON_BUDGETS.md +1 -1
  161. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +16 -3
  162. package/marketplace.json +1 -1
  163. package/node_modules/@opentelemetry/core/package.json +2 -1
  164. package/node_modules/@opentelemetry/instrumentation/package.json +2 -1
  165. package/node_modules/@opentelemetry/resources/package.json +2 -1
  166. package/node_modules/@opentelemetry/sdk-trace/package.json +2 -1
  167. package/node_modules/@opentelemetry/sdk-trace-base/package.json +2 -1
  168. package/node_modules/@sentry/core/build/cjs/client.js +5 -4
  169. package/node_modules/@sentry/core/build/cjs/tracing/ai/gen-ai-attributes.js +0 -4
  170. package/node_modules/@sentry/core/build/cjs/tracing/ai/utils.js +33 -23
  171. package/node_modules/@sentry/core/build/cjs/tracing/anthropic-ai/index.js +7 -6
  172. package/node_modules/@sentry/core/build/cjs/tracing/anthropic-ai/streaming.js +3 -1
  173. package/node_modules/@sentry/core/build/cjs/tracing/google-genai/index.js +13 -14
  174. package/node_modules/@sentry/core/build/cjs/tracing/google-genai/streaming.js +6 -1
  175. package/node_modules/@sentry/core/build/cjs/tracing/langchain/utils.js +71 -21
  176. package/node_modules/@sentry/core/build/cjs/tracing/langgraph/utils.js +23 -33
  177. package/node_modules/@sentry/core/build/cjs/tracing/openai/streaming.js +6 -0
  178. package/node_modules/@sentry/core/build/cjs/tracing/openai/utils.js +11 -0
  179. package/node_modules/@sentry/core/build/cjs/tracing/workers-ai/utils.js +1 -1
  180. package/node_modules/@sentry/core/build/cjs/utils/eventUtils.js +4 -0
  181. package/node_modules/@sentry/core/build/cjs/utils/prepareEvent.js +2 -2
  182. package/node_modules/@sentry/core/build/cjs/utils/version.js +1 -1
  183. package/node_modules/@sentry/core/build/esm/client.js +6 -5
  184. package/node_modules/@sentry/core/build/esm/package.json +1 -1
  185. package/node_modules/@sentry/core/build/esm/tracing/ai/gen-ai-attributes.js +1 -3
  186. package/node_modules/@sentry/core/build/esm/tracing/ai/utils.js +34 -24
  187. package/node_modules/@sentry/core/build/esm/tracing/anthropic-ai/index.js +8 -7
  188. package/node_modules/@sentry/core/build/esm/tracing/anthropic-ai/streaming.js +3 -1
  189. package/node_modules/@sentry/core/build/esm/tracing/google-genai/index.js +15 -16
  190. package/node_modules/@sentry/core/build/esm/tracing/google-genai/streaming.js +6 -1
  191. package/node_modules/@sentry/core/build/esm/tracing/langchain/utils.js +73 -24
  192. package/node_modules/@sentry/core/build/esm/tracing/langgraph/utils.js +24 -34
  193. package/node_modules/@sentry/core/build/esm/tracing/openai/streaming.js +6 -0
  194. package/node_modules/@sentry/core/build/esm/tracing/openai/utils.js +11 -0
  195. package/node_modules/@sentry/core/build/esm/tracing/workers-ai/utils.js +2 -2
  196. package/node_modules/@sentry/core/build/esm/utils/eventUtils.js +4 -1
  197. package/node_modules/@sentry/core/build/esm/utils/prepareEvent.js +2 -2
  198. package/node_modules/@sentry/core/build/esm/utils/version.js +1 -1
  199. package/node_modules/@sentry/core/build/types/tracing/ai/utils.d.ts +3 -7
  200. package/node_modules/@sentry/core/build/types/tracing/anthropic-ai/types.d.ts +2 -2
  201. package/node_modules/@sentry/core/build/types/tracing/langchain/types.d.ts +15 -0
  202. package/node_modules/@sentry/core/build/types/tracing/langchain/utils.d.ts +1 -0
  203. package/node_modules/@sentry/core/build/types/tracing/langgraph/utils.d.ts +2 -0
  204. package/node_modules/@sentry/core/build/types/tracing/openai/types.d.ts +2 -0
  205. package/node_modules/@sentry/core/build/types/utils/eventUtils.d.ts +2 -1
  206. package/node_modules/@sentry/core/build/types-ts3.8/tracing/ai/utils.d.ts +3 -7
  207. package/node_modules/@sentry/core/build/types-ts3.8/tracing/anthropic-ai/types.d.ts +2 -2
  208. package/node_modules/@sentry/core/build/types-ts3.8/tracing/langchain/types.d.ts +15 -0
  209. package/node_modules/@sentry/core/build/types-ts3.8/tracing/langchain/utils.d.ts +1 -0
  210. package/node_modules/@sentry/core/build/types-ts3.8/tracing/langgraph/utils.d.ts +2 -0
  211. package/node_modules/@sentry/core/build/types-ts3.8/tracing/openai/types.d.ts +2 -0
  212. package/node_modules/@sentry/core/build/types-ts3.8/utils/eventUtils.d.ts +2 -1
  213. package/node_modules/@sentry/core/package.json +1 -1
  214. package/node_modules/@sentry/node/build/esm/package.json +1 -1
  215. package/node_modules/@sentry/node/package.json +5 -5
  216. package/node_modules/@sentry/node-core/build/cjs/integrations/anr/index.js +1 -1
  217. package/node_modules/@sentry/node-core/build/cjs/integrations/local-variables/local-variables-async.js +1 -1
  218. package/node_modules/@sentry/node-core/build/esm/integrations/anr/index.js +1 -1
  219. package/node_modules/@sentry/node-core/build/esm/integrations/anr/worker-script.js +2 -2
  220. package/node_modules/@sentry/node-core/build/esm/integrations/local-variables/local-variables-async.js +1 -1
  221. package/node_modules/@sentry/node-core/build/esm/integrations/local-variables/worker-script.js +1 -1
  222. package/node_modules/@sentry/node-core/build/esm/package.json +1 -1
  223. package/node_modules/@sentry/node-core/package.json +10 -4
  224. package/node_modules/@sentry/opentelemetry/build/esm/package.json +1 -1
  225. package/node_modules/@sentry/opentelemetry/package.json +5 -2
  226. package/node_modules/@sentry/server-utils/build/THIRD-PARTY-LICENSES.txt +55 -55
  227. package/node_modules/@sentry/server-utils/build/esm/package.json +1 -1
  228. package/node_modules/@sentry/server-utils/package.json +2 -2
  229. package/node_modules/@toon-format/toon/README.md +10 -41
  230. package/node_modules/@toon-format/toon/dist/index.d.mts +12 -36
  231. package/node_modules/@toon-format/toon/dist/index.mjs +134 -202
  232. package/node_modules/@toon-format/toon/package.json +2 -2
  233. package/node_modules/debug/package.json +3 -0
  234. package/node_modules/fdir/package.json +3 -0
  235. package/node_modules/has-flag/index.d.ts +39 -0
  236. package/node_modules/has-flag/index.js +8 -0
  237. package/node_modules/has-flag/license +9 -0
  238. package/node_modules/has-flag/package.json +46 -0
  239. package/node_modules/has-flag/readme.md +89 -0
  240. package/node_modules/supports-color/browser.js +5 -0
  241. package/node_modules/supports-color/index.js +135 -0
  242. package/node_modules/supports-color/license +9 -0
  243. package/node_modules/supports-color/package.json +53 -0
  244. package/node_modules/supports-color/readme.md +76 -0
  245. package/package.json +5 -4
  246. package/packages/pm-beads/package.json +1 -1
  247. package/packages/pm-calendar/package.json +1 -1
  248. package/packages/pm-command-kit/package.json +1 -1
  249. package/packages/pm-digital-twin/package.json +1 -1
  250. package/packages/pm-governance-audit/package.json +1 -1
  251. package/packages/pm-guide-shell/package.json +1 -1
  252. package/packages/pm-kanban/package.json +1 -1
  253. package/packages/pm-lifecycle-hooks/package.json +1 -1
  254. package/packages/pm-linked-test-adapters/package.json +1 -1
  255. package/packages/pm-search-advanced/package.json +1 -1
  256. package/packages/pm-templates/package.json +1 -1
  257. package/packages/pm-todos/package.json +1 -1
  258. package/packages/pm-vcs/package.json +1 -1
  259. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  260. package/plugins/pm-claude/package.json +1 -1
  261. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  262. package/plugins/pm-codex/package.json +1 -1
  263. package/plugins/pm-codex/plugin.json +1 -1
  264. package/runtime-dependencies.json +1 -1
  265. package/dist/cli-bundle/chunks/chunk-7OEGNA4X.js +0 -20
  266. package/dist/cli-bundle/chunks/chunk-EMEEHV2Y.js +0 -2
  267. package/dist/cli-bundle/chunks/chunk-JSUJWIHQ.js +0 -30
  268. package/dist/cli-bundle/chunks/chunk-VIMQCFCK.js +0 -2
  269. package/dist/cli-bundle/chunks/chunk-X2H6UR26.js +0 -236
  270. package/dist/cli-bundle/chunks/register-mutation-EWMMVZJB.js +0 -20
  271. package/dist/cli-bundle/focused-chunks/chunk-5TBAVZDR.js +0 -20
  272. package/dist/cli-bundle/focused-chunks/chunk-HTA3TNC3.js +0 -26
  273. package/dist/cli-bundle/focused-chunks/chunk-JKBH7YGX.js +0 -2
  274. package/dist/cli-bundle/focused-chunks/chunk-PDYE7QDX.js +0 -166
  275. package/docs/ecosystem-review-2026-09-28.json +0 -1
@@ -294,27 +294,28 @@ function isNumericLiteral(token) {
294
294
  * strict-mode policy: callers decide how to treat `invalid` and `strictError`.
295
295
  */
296
296
  function parseArrayHeaderLine(content, defaultDelimiter) {
297
- const trimmedToken = content.trimStart();
298
297
  let bracketStart = -1;
299
- if (trimmedToken.startsWith("\"")) {
300
- const closingQuoteIndex = findClosingQuote(trimmedToken, 0);
298
+ if (content.startsWith("\"")) {
299
+ const closingQuoteIndex = findClosingQuote(content, 0);
301
300
  if (closingQuoteIndex === -1) return { kind: "notHeader" };
302
- if (!trimmedToken.slice(closingQuoteIndex + 1).startsWith("[")) return { kind: "notHeader" };
303
- const keyEndIndex = content.length - trimmedToken.length + closingQuoteIndex + 1;
304
- bracketStart = content.indexOf("[", keyEndIndex);
301
+ if (content[closingQuoteIndex + 1] !== "[") return { kind: "notHeader" };
302
+ bracketStart = closingQuoteIndex + 1;
305
303
  } else bracketStart = findUnquotedChar(content, "[");
306
304
  if (bracketStart === -1) return { kind: "notHeader" };
307
305
  const firstColonIndex = findUnquotedChar(content, ":");
308
- if (firstColonIndex !== -1 && firstColonIndex < bracketStart) return { kind: "notHeader" };
306
+ if (firstColonIndex === -1 || firstColonIndex < bracketStart) return { kind: "notHeader" };
309
307
  const bracketEnd = findUnquotedChar(content, "]", bracketStart);
310
- if (bracketEnd === -1) return { kind: "notHeader" };
308
+ if (bracketEnd === -1) return {
309
+ kind: "invalid",
310
+ reason: "Unterminated bracket segment"
311
+ };
311
312
  let colonIndex = bracketEnd + 1;
312
313
  let braceEnd = colonIndex;
313
314
  const braceStart = findUnquotedChar(content, "{", bracketEnd);
314
315
  if (braceStart !== -1 && braceStart < findUnquotedChar(content, ":", bracketEnd)) {
315
316
  const gapBeforeBrace = content.slice(bracketEnd + 1, braceStart);
316
317
  if (gapBeforeBrace !== "") {
317
- const trimmedGap = gapBeforeBrace.trim();
318
+ const trimmedGap = trimWhitespace(gapBeforeBrace);
318
319
  return {
319
320
  kind: "invalid",
320
321
  reason: trimmedGap === "" ? `Unexpected whitespace between bracket segment and field list` : `Unexpected content "${trimmedGap}" between bracket segment and field list`
@@ -324,11 +325,14 @@ function parseArrayHeaderLine(content, defaultDelimiter) {
324
325
  if (foundBraceEnd !== -1) braceEnd = foundBraceEnd + 1;
325
326
  }
326
327
  colonIndex = findUnquotedChar(content, ":", Math.max(bracketEnd, braceEnd));
327
- if (colonIndex === -1) return { kind: "notHeader" };
328
+ if (colonIndex === -1) return {
329
+ kind: "invalid",
330
+ reason: "Missing colon after array header"
331
+ };
328
332
  const gapStart = Math.max(bracketEnd + 1, braceEnd);
329
333
  const gapBeforeColon = content.slice(gapStart, colonIndex);
330
334
  if (gapBeforeColon !== "") {
331
- const trimmedGap = gapBeforeColon.trim();
335
+ const trimmedGap = trimWhitespace(gapBeforeColon);
332
336
  return {
333
337
  kind: "invalid",
334
338
  reason: trimmedGap === "" ? `Unexpected whitespace between bracket segment and colon` : `Unexpected content "${trimmedGap}" between bracket segment and colon`
@@ -337,7 +341,7 @@ function parseArrayHeaderLine(content, defaultDelimiter) {
337
341
  let key;
338
342
  if (bracketStart > 0) {
339
343
  const rawKey = content.slice(0, bracketStart);
340
- if (rawKey !== rawKey.trimEnd()) return {
344
+ if (endsWithWhitespace(rawKey)) return {
341
345
  kind: "invalid",
342
346
  reason: "Unexpected whitespace between key and bracket segment"
343
347
  };
@@ -426,8 +430,8 @@ function parseBracketSegment(seg, defaultDelimiter) {
426
430
  * descending into nested field groups (`field{sub1,sub2}`).
427
431
  *
428
432
  * @remarks
429
- * Throws on empty segments, empty names, unmatched braces, and content
430
- * after a nested group's closing brace; callers decide strict fallthrough.
433
+ * Throws on empty segments, empty names, whitespace before a nested group,
434
+ * unmatched braces, and content after a nested group's closing brace.
431
435
  */
432
436
  function parseFieldEntries(fieldsContent, delimiter) {
433
437
  return splitFieldEntries(fieldsContent, delimiter).map((entry) => {
@@ -435,8 +439,9 @@ function parseFieldEntries(fieldsContent, delimiter) {
435
439
  if (!trimmedEntry) throw new SyntaxError("Empty field name in field list");
436
440
  const groupStart = findUnquotedChar(trimmedEntry, "{");
437
441
  if (groupStart === -1) return { name: parseStringLiteral(trimmedEntry) };
438
- const namePart = trimSpaces(trimmedEntry.slice(0, groupStart));
442
+ const namePart = trimmedEntry.slice(0, groupStart);
439
443
  if (!namePart) throw new SyntaxError("Missing field name before nested field group");
444
+ if (namePart.endsWith(" ")) throw new SyntaxError("Unexpected whitespace before nested field group");
440
445
  const groupEnd = findMatchingBrace(trimmedEntry, groupStart);
441
446
  if (groupEnd === -1) throw new SyntaxError("Unmatched brace in field list");
442
447
  if (groupEnd !== trimmedEntry.length - 1) throw new SyntaxError("Unexpected content after nested field group");
@@ -623,6 +628,7 @@ function parseQuotedKey(content, start) {
623
628
  if (closingQuoteIndex === -1) throw new SyntaxError("Unterminated quoted key");
624
629
  const key = unescapeString(content.slice(start + 1, closingQuoteIndex));
625
630
  let parsePosition = closingQuoteIndex + 1;
631
+ while (content[parsePosition] === " ") parsePosition++;
626
632
  if (parsePosition >= content.length || content[parsePosition] !== ":") throw new SyntaxError("Missing colon after key");
627
633
  parsePosition++;
628
634
  return {
@@ -633,16 +639,22 @@ function parseQuotedKey(content, start) {
633
639
  function parseKeyToken(content, start) {
634
640
  return content[start] === "\"" ? parseQuotedKey(content, start) : parseUnquotedKey(content, start);
635
641
  }
642
+ function endsWithWhitespace(value) {
643
+ return value.endsWith(" ") || value.endsWith(" ");
644
+ }
645
+ function trimWhitespace(value) {
646
+ return value.replace(/^[ \t]+|[ \t]+$/g, "");
647
+ }
636
648
  function isArrayHeaderContent(content) {
637
- return content.trim().startsWith("[") && findUnquotedChar(content, ":") !== -1;
649
+ return trimSpaces(content).startsWith("[") && findUnquotedChar(content, ":") !== -1;
638
650
  }
639
651
  function isKeyValueContent(content) {
640
652
  return findUnquotedChar(content, ":") !== -1;
641
653
  }
642
654
  //#endregion
643
655
  //#region src/decode/validation.ts
644
- function assertExpectedCount(actual, expected, itemType, options, line) {
645
- if (options.strict && actual !== expected) throw new ToonDecodeError(`Expected ${expected} ${itemType}, but got ${actual}`, {
656
+ function assertExpectedCount(actual, expected, itemType, line) {
657
+ if (actual !== expected) throw new ToonDecodeError(`Expected ${expected} ${itemType}, but got ${actual}`, {
646
658
  line: line.lineNumber,
647
659
  source: line.raw
648
660
  });
@@ -659,8 +671,8 @@ function validateNoExtraTabularRows(nextLine, rowDepth, header) {
659
671
  source: nextLine.raw
660
672
  });
661
673
  }
662
- function validateNoBlankLinesInRange(startLine, endLine, blankLines, strict, context) {
663
- if (!strict) return;
674
+ function validateNoBlankLinesInRange(startLine, endLine, blankLines, context) {
675
+ if (startLine === void 0 || endLine === void 0) return;
664
676
  const firstBlank = blankLines.find((blank) => blank.lineNumber > startLine && blank.lineNumber < endLine);
665
677
  if (firstBlank) throw new ToonDecodeError(`Blank lines inside ${context} are not allowed in strict mode`, { line: firstBlank.lineNumber });
666
678
  }
@@ -690,6 +702,7 @@ function decodeStream$1(source, options) {
690
702
  }
691
703
  function* decodeDocument(reader, options) {
692
704
  const first = yield* peekLine(reader);
705
+ if (first && first.depth !== 0) throw overIndentedLineError(first, 0);
693
706
  if (!first) {
694
707
  yield { type: "startObject" };
695
708
  yield { type: "endObject" };
@@ -702,7 +715,7 @@ function* decodeDocument(reader, options) {
702
715
  length: 0
703
716
  };
704
717
  yield { type: "endArray" };
705
- yield* assertFullyConsumed(reader, options.strict);
718
+ yield* assertFullyConsumed(reader);
706
719
  return;
707
720
  }
708
721
  if (isArrayHeaderContent(first.content)) {
@@ -710,20 +723,20 @@ function* decodeDocument(reader, options) {
710
723
  if (headerInfo) {
711
724
  yield* readLine(reader);
712
725
  yield* decodeArrayFromHeader(headerInfo.header, headerInfo.inlineValues, reader, 0, options, first);
713
- yield* assertFullyConsumed(reader, options.strict);
726
+ yield* assertFullyConsumed(reader);
714
727
  return;
715
728
  }
716
729
  }
717
730
  yield* readLine(reader);
718
731
  const following = yield* peekLine(reader);
719
- if (!(following !== void 0) && !isKeyValueLine(first)) {
732
+ if (!(following !== void 0) && !isKeyValueContent(first.content)) {
720
733
  yield {
721
734
  type: "primitive",
722
735
  value: withLine(first, () => parsePrimitiveToken(first.content))
723
736
  };
724
737
  return;
725
738
  }
726
- if (!isKeyValueLine(first) && following?.depth === 0) throw new ToonDecodeError("Top-level document must start with a key-value or array-header line", {
739
+ if (!isKeyValueContent(first.content) && following?.depth === 0) throw new ToonDecodeError("Top-level document must start with a key-value or array-header line", {
727
740
  line: first.lineNumber,
728
741
  source: first.raw
729
742
  });
@@ -733,12 +746,7 @@ function* decodeDocument(reader, options) {
733
746
  while (true) {
734
747
  const line = yield* peekLine(reader);
735
748
  if (!line) break;
736
- if (line.depth !== 0) {
737
- if (options.strict) throw overIndentedLineError(line, 0);
738
- assertNotScalarLine(line);
739
- yield* readLine(reader);
740
- continue;
741
- }
749
+ if (line.depth !== 0) throw overIndentedLineError(line, 0);
742
750
  yield* readLine(reader);
743
751
  yield* decodeKeyValue(line, reader, 0, options, rootSeenKeys);
744
752
  }
@@ -756,13 +764,6 @@ function overIndentedLineError(line, expectedDepth) {
756
764
  source: line.raw
757
765
  });
758
766
  }
759
- function assertNotScalarLine(line) {
760
- if (line.content.startsWith("- ") || line.content === "-" || findUnquotedChar(line.content, ":") !== -1) return;
761
- throw new ToonDecodeError("Unexpected bare token line outside root primitive position", {
762
- line: line.lineNumber,
763
- source: line.raw
764
- });
765
- }
766
767
  function keylessKeyedError(line) {
767
768
  return new ToonDecodeError("Keyless keyed header is only valid at the document root", {
768
769
  line: line.lineNumber,
@@ -781,8 +782,7 @@ function keylessFieldsHeaderError(line) {
781
782
  source: line.raw
782
783
  });
783
784
  }
784
- function* assertFullyConsumed(reader, strict) {
785
- if (!strict) return;
785
+ function* assertFullyConsumed(reader) {
786
786
  const line = yield* peekLine(reader);
787
787
  if (line) throw new ToonDecodeError("Unexpected content after the document root", {
788
788
  line: line.lineNumber,
@@ -809,7 +809,7 @@ function* decodeKeyValue(line, reader, baseDepth, options, seenKeys) {
809
809
  yield* decodeArrayFromHeader(arrayHeader.header, arrayHeader.inlineValues, reader, baseDepth, options, line);
810
810
  return;
811
811
  }
812
- if (arrayHeader && arrayHeader.header.key === void 0 && options.strict) throw arrayHeader.header.keyed ? keylessKeyedError(line) : keylessHeaderError(line);
812
+ if (arrayHeader && arrayHeader.header.key === void 0) throw arrayHeader.header.keyed ? keylessKeyedError(line) : keylessHeaderError(line);
813
813
  const { key, end } = withLine(line, () => parseKeyToken(content, 0));
814
814
  const rest = trimSpaces(content.slice(end));
815
815
  assertNoDuplicateKey(key, line, seenKeys);
@@ -849,17 +849,18 @@ function* decodeObjectFields(reader, baseDepth, options) {
849
849
  while (true) {
850
850
  const line = yield* peekLine(reader);
851
851
  if (!line || line.depth < baseDepth) break;
852
- if (computedDepth === void 0 && line.depth >= baseDepth) computedDepth = line.depth;
853
- if (line.depth === computedDepth) {
854
- yield* readLine(reader);
855
- yield* decodeKeyValue(line, reader, computedDepth, options, seenKeys);
856
- } else if (computedDepth !== void 0 && line.depth > computedDepth) {
857
- if (options.strict) throw overIndentedLineError(line, computedDepth);
858
- assertNotScalarLine(line);
859
- yield* readLine(reader);
860
- } else break;
852
+ computedDepth ??= line.depth;
853
+ if (line.depth !== computedDepth) throw overIndentedLineError(line, computedDepth);
854
+ yield* readLine(reader);
855
+ yield* decodeKeyValue(line, reader, computedDepth, options, seenKeys);
861
856
  }
862
857
  }
858
+ function* scopeContentDepth(reader, baseDepth, strict) {
859
+ const first = yield* peekLine(reader);
860
+ if (!first || first.depth <= baseDepth + 1) return baseDepth + 1;
861
+ assertNoDepthJump(first, baseDepth, strict);
862
+ return first.depth;
863
+ }
863
864
  function* decodeArrayFromHeader(header, inlineValues, reader, baseDepth, options, headerLine) {
864
865
  if (header.keyed) {
865
866
  yield* decodeKeyedObject(header, reader, baseDepth, options, headerLine);
@@ -883,20 +884,16 @@ function* decodeArrayFromHeader(header, inlineValues, reader, baseDepth, options
883
884
  yield { type: "endArray" };
884
885
  }
885
886
  function* decodeInlinePrimitiveArray(header, inlineValues, options, headerLine) {
886
- if (!trimSpaces(inlineValues)) {
887
- assertExpectedCount(0, header.length, "inline-form values", options, headerLine);
888
- return;
889
- }
890
887
  const values = withLine(headerLine, () => parseDelimitedValues(inlineValues, header.delimiter));
891
888
  const primitives = withLine(headerLine, () => mapRowValuesToPrimitives(values));
892
- assertExpectedCount(primitives.length, header.length, "inline-form values", options, headerLine);
889
+ if (options.strict) assertExpectedCount(primitives.length, header.length, "inline-form values", headerLine);
893
890
  for (const primitive of primitives) yield {
894
891
  type: "primitive",
895
892
  value: primitive
896
893
  };
897
894
  }
898
895
  function* decodeKeyedObject(header, reader, baseDepth, options, headerLine) {
899
- const entryDepth = baseDepth + 1;
896
+ const entryDepth = yield* scopeContentDepth(reader, baseDepth, options.strict);
900
897
  const leafFieldCount = countLeafFields(header.fields);
901
898
  const seenEntryKeys = options.strict ? /* @__PURE__ */ new Set() : void 0;
902
899
  let entryCount = 0;
@@ -907,22 +904,11 @@ function* decodeKeyedObject(header, reader, baseDepth, options, headerLine) {
907
904
  while (true) {
908
905
  const line = yield* peekLine(reader);
909
906
  if (!line || line.depth <= baseDepth) break;
910
- if (line.depth > entryDepth) {
911
- if (options.strict) throw new ToonDecodeError("Unexpected indentation inside keyed tabular object", {
912
- line: line.lineNumber,
913
- source: line.raw
914
- });
915
- yield* readLine(reader);
916
- continue;
917
- }
918
- if (findUnquotedChar(line.content, ":") === -1) {
919
- if (options.strict) throw new ToonDecodeError("Expected entry row inside keyed tabular object", {
920
- line: line.lineNumber,
921
- source: line.raw
922
- });
923
- yield* readLine(reader);
924
- continue;
925
- }
907
+ if (line.depth !== entryDepth) throw overIndentedLineError(line, entryDepth);
908
+ if (findUnquotedChar(line.content, ":") === -1) throw new ToonDecodeError("Expected entry row inside keyed tabular object", {
909
+ line: line.lineNumber,
910
+ source: line.raw
911
+ });
926
912
  yield* readLine(reader);
927
913
  if (startLine === void 0) startLine = line.lineNumber;
928
914
  endLine = line.lineNumber;
@@ -935,67 +921,71 @@ function* decodeKeyedObject(header, reader, baseDepth, options, headerLine) {
935
921
  };
936
922
  const cellsContent = trimSpaces(line.content.slice(end));
937
923
  const values = cellsContent === "" ? [] : withLine(line, () => parseDelimitedValues(cellsContent, header.delimiter));
938
- assertExpectedCount(values.length, leafFieldCount, "keyed entry cells", options, line);
924
+ assertExpectedCount(values.length, leafFieldCount, "keyed entry cells", line);
939
925
  const primitives = withLine(line, () => mapRowValuesToPrimitives(values));
940
926
  yield* yieldObjectFromFields(header.fields, primitives);
941
927
  entryCount++;
942
928
  }
943
- assertExpectedCount(entryCount, header.length, "keyed entries", options, lastEntryLine);
944
- if (options.strict && startLine !== void 0 && endLine !== void 0) validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, options.strict, "keyed tabular object");
929
+ if (options.strict) {
930
+ assertExpectedCount(entryCount, header.length, "keyed entries", lastEntryLine);
931
+ validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, "keyed tabular object");
932
+ }
945
933
  yield { type: "endObject" };
946
934
  }
947
935
  function* decodeTabularArray(header, reader, baseDepth, options, headerLine) {
948
- const rowDepth = baseDepth + 1;
936
+ const rowDepth = yield* scopeContentDepth(reader, baseDepth, options.strict);
949
937
  let rowCount = 0;
950
938
  let startLine;
951
939
  let endLine;
952
940
  let lastRowLine = headerLine;
953
941
  while (!options.strict || rowCount < header.length) {
954
942
  const line = yield* peekLine(reader);
955
- if (!line || line.depth < rowDepth) break;
956
- if (line.depth === rowDepth) {
957
- if (!isDataRow(line.content, header.delimiter)) break;
958
- if (startLine === void 0) startLine = line.lineNumber;
959
- endLine = line.lineNumber;
960
- lastRowLine = line;
961
- yield* readLine(reader);
962
- const values = withLine(line, () => parseDelimitedValues(line.content, header.delimiter));
963
- assertExpectedCount(values.length, countLeafFields(header.fields), "tabular row values", options, line);
964
- const primitives = withLine(line, () => mapRowValuesToPrimitives(values));
965
- yield* yieldObjectFromFields(header.fields, primitives);
966
- rowCount++;
967
- } else break;
943
+ if (!line || line.depth <= baseDepth) break;
944
+ if (line.depth !== rowDepth) throw overIndentedLineError(line, rowDepth);
945
+ if (!isDataRow(line.content, header.delimiter)) break;
946
+ if (startLine === void 0) startLine = line.lineNumber;
947
+ endLine = line.lineNumber;
948
+ lastRowLine = line;
949
+ yield* readLine(reader);
950
+ const values = withLine(line, () => parseDelimitedValues(line.content, header.delimiter));
951
+ assertExpectedCount(values.length, countLeafFields(header.fields), "tabular row values", line);
952
+ const primitives = withLine(line, () => mapRowValuesToPrimitives(values));
953
+ yield* yieldObjectFromFields(header.fields, primitives);
954
+ rowCount++;
955
+ }
956
+ if (options.strict) {
957
+ assertExpectedCount(rowCount, header.length, "tabular rows", lastRowLine);
958
+ validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, "tabular array");
959
+ validateNoExtraTabularRows(yield* peekLine(reader), rowDepth, header);
968
960
  }
969
- assertExpectedCount(rowCount, header.length, "tabular rows", options, lastRowLine);
970
- if (options.strict && startLine !== void 0 && endLine !== void 0) validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, options.strict, "tabular array");
971
- if (options.strict) validateNoExtraTabularRows(yield* peekLine(reader), rowDepth, header);
972
961
  }
973
962
  function* decodeListArray(header, reader, baseDepth, options, headerLine) {
974
- const itemDepth = baseDepth + 1;
963
+ const itemDepth = yield* scopeContentDepth(reader, baseDepth, options.strict);
975
964
  let itemCount = 0;
976
965
  let startLine;
977
966
  let endLine;
978
967
  let lastItemLine = headerLine;
979
968
  while (!options.strict || itemCount < header.length) {
980
969
  const line = yield* peekLine(reader);
981
- if (!line || line.depth < itemDepth) break;
982
- const isListItem = line.content.startsWith("- ") || line.content === "-";
983
- if (line.depth === itemDepth && isListItem) {
984
- if (startLine === void 0) startLine = line.lineNumber;
985
- endLine = line.lineNumber;
986
- lastItemLine = line;
987
- yield* decodeListItem(reader, itemDepth, options);
988
- const lastConsumedLine = reader.lastLine;
989
- if (lastConsumedLine) {
990
- endLine = lastConsumedLine.lineNumber;
991
- lastItemLine = lastConsumedLine;
992
- }
993
- itemCount++;
994
- } else break;
970
+ if (!line || line.depth <= baseDepth) break;
971
+ if (line.depth !== itemDepth) throw overIndentedLineError(line, itemDepth);
972
+ if (!(line.content.startsWith("- ") || line.content === "-")) break;
973
+ if (startLine === void 0) startLine = line.lineNumber;
974
+ endLine = line.lineNumber;
975
+ lastItemLine = line;
976
+ yield* decodeListItem(reader, itemDepth, options);
977
+ const lastConsumedLine = reader.lastLine;
978
+ if (lastConsumedLine) {
979
+ endLine = lastConsumedLine.lineNumber;
980
+ lastItemLine = lastConsumedLine;
981
+ }
982
+ itemCount++;
983
+ }
984
+ if (options.strict) {
985
+ assertExpectedCount(itemCount, header.length, "list-form items", lastItemLine);
986
+ validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, "list-form array");
987
+ validateNoExtraListItems(yield* peekLine(reader), itemDepth, header.length);
995
988
  }
996
- assertExpectedCount(itemCount, header.length, "list-form items", options, lastItemLine);
997
- if (options.strict && startLine !== void 0 && endLine !== void 0) validateNoBlankLinesInRange(startLine, endLine, reader.scanState.blankLines, options.strict, "list-form array");
998
- if (options.strict) validateNoExtraListItems(yield* peekLine(reader), itemDepth, header.length);
999
989
  }
1000
990
  function* decodeListItem(reader, baseDepth, options) {
1001
991
  const line = yield* readLine(reader);
@@ -1005,7 +995,7 @@ function* decodeListItem(reader, baseDepth, options) {
1005
995
  yield { type: "startObject" };
1006
996
  yield { type: "endObject" };
1007
997
  return;
1008
- } else if (line.content.startsWith("- ")) afterHyphen = line.content.slice(2);
998
+ } else if (line.content.startsWith("- ")) afterHyphen = line.content.slice(2).replace(/^ +/, "");
1009
999
  else throw new ToonDecodeError(`Expected list item to start with "- "`, {
1010
1000
  line: line.lineNumber,
1011
1001
  source: line.raw
@@ -1029,9 +1019,8 @@ function* decodeListItem(reader, baseDepth, options) {
1029
1019
  };
1030
1020
  if (isArrayHeaderContent(afterHyphen)) {
1031
1021
  const arrayHeader = withLine(itemLine, () => resolveArrayHeader(parseArrayHeaderLine(afterHyphen, DEFAULT_DELIMITER), options.strict));
1032
- if (arrayHeader) if (arrayHeader.header.keyed || arrayHeader.header.fields !== void 0) {
1033
- if (options.strict) throw arrayHeader.header.keyed ? keylessKeyedError(itemLine) : keylessFieldsHeaderError(itemLine);
1034
- } else {
1022
+ if (arrayHeader) {
1023
+ if (arrayHeader.header.keyed || arrayHeader.header.fields !== void 0) throw arrayHeader.header.keyed ? keylessKeyedError(itemLine) : keylessFieldsHeaderError(itemLine);
1035
1024
  yield* decodeArrayFromHeader(arrayHeader.header, arrayHeader.inlineValues, reader, baseDepth, options, itemLine);
1036
1025
  return;
1037
1026
  }
@@ -1067,26 +1056,14 @@ function* followSiblingFields(reader, followDepth, options, seenKeys) {
1067
1056
  while (true) {
1068
1057
  const nextLine = yield* peekLine(reader);
1069
1058
  if (!nextLine || nextLine.depth < followDepth) break;
1070
- if (nextLine.depth === followDepth && !nextLine.content.startsWith("- ")) {
1071
- yield* readLine(reader);
1072
- yield* decodeKeyValue(nextLine, reader, followDepth, options, seenKeys);
1073
- } else break;
1059
+ if (nextLine.depth !== followDepth) throw overIndentedLineError(nextLine, followDepth);
1060
+ yield* readLine(reader);
1061
+ yield* decodeKeyValue(nextLine, reader, followDepth, options, seenKeys);
1074
1062
  }
1075
1063
  }
1076
- function isKeyValueLine(line) {
1077
- const content = line.content;
1078
- if (content.startsWith("\"")) {
1079
- const closingQuoteIndex = findClosingQuote(content, 0);
1080
- if (closingQuoteIndex === -1) return false;
1081
- return content.slice(closingQuoteIndex + 1).includes(":");
1082
- } else return content.includes(":");
1083
- }
1084
1064
  function resolveArrayHeader(result, strict) {
1085
1065
  if (result.kind === "notHeader") return;
1086
- if (result.kind === "invalid") {
1087
- if (strict) throw new SyntaxError(result.reason);
1088
- return;
1089
- }
1066
+ if (result.kind === "invalid") throw new SyntaxError(result.reason);
1090
1067
  if (strict && result.strictError !== void 0) throw new SyntaxError(result.strictError);
1091
1068
  return {
1092
1069
  header: result.header,
@@ -1098,7 +1075,6 @@ function* yieldObjectFromFields(fields, primitives) {
1098
1075
  function* walkFieldGroup(nodes) {
1099
1076
  yield { type: "startObject" };
1100
1077
  for (const node of nodes) {
1101
- if (!node.children && cellIndex >= primitives.length) continue;
1102
1078
  yield {
1103
1079
  type: "key",
1104
1080
  key: node.name
@@ -1209,17 +1185,15 @@ function applyEvent(state, event) {
1209
1185
  parent.currentKey = event.key;
1210
1186
  break;
1211
1187
  }
1212
- case "primitive":
1213
- if (stack.length === 0) state.root = event.value;
1214
- else {
1215
- const parent = stack[stack.length - 1];
1216
- if (parent.type === "object") {
1217
- if (parent.currentKey === void 0) throw new Error("Primitive event without preceding key in object");
1218
- setOwnProperty(parent.obj, parent.currentKey, event.value);
1219
- parent.currentKey = void 0;
1220
- } else if (parent.type === "array") parent.arr.push(event.value);
1221
- }
1222
- break;
1188
+ case "primitive": if (stack.length === 0) state.root = event.value;
1189
+ else {
1190
+ const parent = stack[stack.length - 1];
1191
+ if (parent.type === "object") {
1192
+ if (parent.currentKey === void 0) throw new Error("Primitive event without preceding key in object");
1193
+ setOwnProperty(parent.obj, parent.currentKey, event.value);
1194
+ parent.currentKey = void 0;
1195
+ } else if (parent.type === "array") parent.arr.push(event.value);
1196
+ }
1223
1197
  }
1224
1198
  }
1225
1199
  function finalizeState(state) {
@@ -1290,7 +1264,7 @@ function normalizeValue(value) {
1290
1264
  return value.toString();
1291
1265
  }
1292
1266
  if (value instanceof Date) return value.toISOString();
1293
- if (Array.isArray(value)) return value.map(normalizeValue);
1267
+ if (Array.isArray(value)) return Array.from(value, normalizeValue);
1294
1268
  if (value instanceof Set) return Array.from(value).map(normalizeValue);
1295
1269
  if (value instanceof Map) return Object.fromEntries(Array.from(value, ([k, v]) => [String(k), normalizeValue(v)]));
1296
1270
  if (isPlainObject(value)) {
@@ -1363,21 +1337,7 @@ function assertValidDelimiter(delimiter) {
1363
1337
  function isValidUnquotedKey(key) {
1364
1338
  return /^[A-Z_][\w.]*$/i.test(key);
1365
1339
  }
1366
- /**
1367
- * Determines if a string value can be safely encoded without quotes.
1368
- *
1369
- * @remarks
1370
- * A string needs quoting if it:
1371
- * - Is empty
1372
- * - Has leading or trailing whitespace
1373
- * - Could be confused with a literal (boolean, null, number)
1374
- * - Contains structural characters (colons, brackets, braces)
1375
- * - Contains quotes or backslashes (need escaping)
1376
- * - Contains control characters (newlines, tabs, etc.)
1377
- * - Contains the active delimiter
1378
- * - Starts with a list marker (hyphen)
1379
- * - Starts with a comment marker (#)
1380
- */
1340
+ /** Determines if a string value can be safely encoded without quotes. */
1381
1341
  function isSafeUnquoted(value, delimiter = DEFAULT_DELIMITER) {
1382
1342
  if (!value) return false;
1383
1343
  if (/^[ \t]|[ \t]$/.test(value)) return false;
@@ -1481,7 +1441,7 @@ function collectLeafValues(row, fields, leaves) {
1481
1441
  //#region src/encode/encoders.ts
1482
1442
  function* encodeJsonValue(value, options, depth) {
1483
1443
  if (isEncodablePrimitive(value)) {
1484
- const encodedPrimitive = encodePrimitive(value, options.delimiter);
1444
+ const encodedPrimitive = typeof value === "string" && value.startsWith("") ? `"${escapeString(value)}"` : encodePrimitive(value, options.delimiter);
1485
1445
  if (encodedPrimitive !== "") yield encodedPrimitive;
1486
1446
  return;
1487
1447
  }
@@ -1626,13 +1586,14 @@ function* encodeObjectAsListItemLines(obj, depth, options) {
1626
1586
  }
1627
1587
  const encodedKey = encodeKey(firstKey);
1628
1588
  if (isEncodablePrimitive(firstValue)) yield indentedListItem(depth, `${encodedKey}: ${encodePrimitive(firstValue, options.delimiter)}`, options.indentSize);
1629
- else if (isJsonArray(firstValue)) if (firstValue.length === 0) yield indentedListItem(depth, `${encodedKey}: []`, options.indentSize);
1630
- else if (isArrayOfPrimitives(firstValue)) yield indentedListItem(depth, `${encodedKey}${encodeInlineArrayLine(firstValue, options.delimiter)}`, options.indentSize);
1631
- else {
1632
- yield indentedListItem(depth, `${encodedKey}${formatHeader(firstValue.length, { delimiter: options.delimiter })}`, options.indentSize);
1633
- for (const item of firstValue) yield* encodeListItemValueLines(item, depth + 2, options);
1634
- }
1635
- else if (isJsonObject(firstValue)) {
1589
+ else if (isJsonArray(firstValue)) {
1590
+ if (firstValue.length === 0) yield indentedListItem(depth, `${encodedKey}: []`, options.indentSize);
1591
+ else if (isArrayOfPrimitives(firstValue)) yield indentedListItem(depth, `${encodedKey}${encodeInlineArrayLine(firstValue, options.delimiter)}`, options.indentSize);
1592
+ else {
1593
+ yield indentedListItem(depth, `${encodedKey}${formatHeader(firstValue.length, { delimiter: options.delimiter })}`, options.indentSize);
1594
+ for (const item of firstValue) yield* encodeListItemValueLines(item, depth + 2, options);
1595
+ }
1596
+ } else if (isJsonObject(firstValue)) {
1636
1597
  yield indentedListItem(depth, `${encodedKey}:`, options.indentSize);
1637
1598
  if (!isEmptyObject(firstValue)) yield* encodeObjectLines(firstValue, depth + 2, options);
1638
1599
  }
@@ -1640,12 +1601,13 @@ function* encodeObjectAsListItemLines(obj, depth, options) {
1640
1601
  }
1641
1602
  function* encodeListItemValueLines(value, depth, options) {
1642
1603
  if (isEncodablePrimitive(value)) yield indentedListItem(depth, encodePrimitive(value, options.delimiter), options.indentSize);
1643
- else if (isJsonArray(value)) if (isArrayOfPrimitives(value)) yield indentedListItem(depth, encodeInlineArrayLine(value, options.delimiter), options.indentSize);
1644
- else {
1645
- yield indentedListItem(depth, formatHeader(value.length, { delimiter: options.delimiter }), options.indentSize);
1646
- for (const item of value) yield* encodeListItemValueLines(item, depth + 1, options);
1647
- }
1648
- else if (isJsonObject(value)) yield* encodeObjectAsListItemLines(value, depth, options);
1604
+ else if (isJsonArray(value)) {
1605
+ if (isArrayOfPrimitives(value)) yield indentedListItem(depth, encodeInlineArrayLine(value, options.delimiter), options.indentSize);
1606
+ else {
1607
+ yield indentedListItem(depth, formatHeader(value.length, { delimiter: options.delimiter }), options.indentSize);
1608
+ for (const item of value) yield* encodeListItemValueLines(item, depth + 1, options);
1609
+ }
1610
+ } else if (isJsonObject(value)) yield* encodeObjectAsListItemLines(value, depth, options);
1649
1611
  }
1650
1612
  function indentedLine(depth, content, indentSize) {
1651
1613
  return " ".repeat(indentSize * depth) + content;
@@ -1707,10 +1669,6 @@ function transformArray(arr, replacer, path) {
1707
1669
  /**
1708
1670
  * Encodes a JavaScript value into TOON format string.
1709
1671
  *
1710
- * @param input Any JavaScript value (objects, arrays, primitives)
1711
- * @param options Optional encoding configuration
1712
- * @returns TOON formatted string
1713
- *
1714
1672
  * @example
1715
1673
  * ```ts
1716
1674
  * encode({ name: 'Ada', age: 30 })
@@ -1734,10 +1692,6 @@ function encode(input, options) {
1734
1692
  /**
1735
1693
  * Decodes a TOON format string into a JavaScript value.
1736
1694
  *
1737
- * @param input TOON formatted string
1738
- * @param options Optional decoding configuration
1739
- * @returns Parsed JavaScript value (object, array, or primitive)
1740
- *
1741
1695
  * @example
1742
1696
  * ```ts
1743
1697
  * decode('name: Ada\nage: 30')
@@ -1761,10 +1715,6 @@ function decode(input, options) {
1761
1715
  * This function yields TOON lines one at a time without building the full string,
1762
1716
  * making it suitable for streaming large outputs to files, HTTP responses, or process stdout.
1763
1717
  *
1764
- * @param input Any JavaScript value (objects, arrays, primitives)
1765
- * @param options Optional encoding configuration
1766
- * @returns Iterable of TOON lines (without trailing newlines)
1767
- *
1768
1718
  * @example
1769
1719
  * ```ts
1770
1720
  * // Stream to stdout
@@ -1790,10 +1740,6 @@ function encodeLines(input, options) {
1790
1740
  * Convenience wrapper around the streaming decoder that builds the full
1791
1741
  * value in memory.
1792
1742
  *
1793
- * @param lines Iterable of TOON lines (without newlines)
1794
- * @param options Optional decoding configuration
1795
- * @returns Parsed JavaScript value (object, array, or primitive)
1796
- *
1797
1743
  * @example
1798
1744
  * ```ts
1799
1745
  * const lines = ['name: Ada', 'age: 30']
@@ -1802,7 +1748,7 @@ function encodeLines(input, options) {
1802
1748
  * ```
1803
1749
  */
1804
1750
  function decodeFromLines(lines, options) {
1805
- return buildValueFromEvents(decodeStreamSync$1(lines, resolveDecodeOptions(options)));
1751
+ return buildValueFromEvents(decodeStreamSync$1(lines, options));
1806
1752
  }
1807
1753
  /**
1808
1754
  * Synchronously decodes TOON lines into a stream of JSON events.
@@ -1810,10 +1756,6 @@ function decodeFromLines(lines, options) {
1810
1756
  * Yields structured events (startObject, endObject, startArray, endArray, key,
1811
1757
  * primitive) that represent the JSON data model without building the full value tree.
1812
1758
  *
1813
- * @param lines Iterable of TOON lines (without newlines)
1814
- * @param options Optional decoding configuration
1815
- * @returns Iterable of JSON stream events
1816
- *
1817
1759
  * @example
1818
1760
  * ```ts
1819
1761
  * const lines = ['name: Ada', 'age: 30']
@@ -1836,10 +1778,6 @@ function decodeStreamSync(lines, options) {
1836
1778
  * primitive) that represent the JSON data model without building the full value tree.
1837
1779
  * Supports both sync and async iterables.
1838
1780
  *
1839
- * @param source Async or sync iterable of TOON lines (without newlines)
1840
- * @param options Optional decoding configuration
1841
- * @returns Async iterable of JSON stream events
1842
- *
1843
1781
  * @example
1844
1782
  * ```ts
1845
1783
  * const fileStream = createReadStream('data.toon', 'utf-8')
@@ -1866,11 +1804,5 @@ function resolveOptions(options) {
1866
1804
  replacer: options?.replacer
1867
1805
  };
1868
1806
  }
1869
- function resolveDecodeOptions(options) {
1870
- return {
1871
- indentSize: options?.indentSize ?? options?.indent ?? 2,
1872
- strict: options?.strict ?? true
1873
- };
1874
- }
1875
1807
  //#endregion
1876
1808
  export { DEFAULT_DELIMITER, DELIMITERS, ToonDecodeError, decode, decodeFromLines, decodeStream, decodeStreamSync, encode, encodeLines, escapeString, rawString };