@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.
- package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +45 -0
- package/dist/cli/extension-command-options.d.ts +3 -1
- package/dist/cli/extension-command-options.js +1 -1
- package/dist/cli/help-content.js +2 -2
- package/dist/cli/register-list-query.js +2 -2
- package/dist/cli/register-mutation.js +2 -2
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli/register-structured-mutation.d.ts +0 -5
- package/dist/cli/register-structured-mutation.js +1 -1
- package/dist/cli/runtime/invocation-options.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/{chunk-7VHINXZP.js → chunk-4GA4MUTE.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-RJ5N44WY.js → chunk-4V6ONLI6.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-LNKLQYBG.js → chunk-6VZKYJW7.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-7PATQWTH.js +20 -0
- package/dist/cli-bundle/chunks/chunk-7WIEAGDS.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-52EZH2QQ.js → chunk-AETP4JEM.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-WBBW3WQX.js → chunk-FIJOSX2C.js} +28 -28
- package/dist/cli-bundle/chunks/chunk-LUKVNL72.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-NC3QVGDD.js → chunk-OJ77ZQN3.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-PYRREEUB.js +30 -0
- package/dist/cli-bundle/chunks/chunk-RHQJ6VWC.js +237 -0
- package/dist/cli-bundle/chunks/{chunk-N56CAQBU.js → chunk-RNABUEFB.js} +52 -52
- package/dist/cli-bundle/chunks/chunk-TO6CIV4C.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-JGKTOBVB.js → chunk-ZZED447K.js} +4 -4
- package/dist/cli-bundle/chunks/{register-list-query-FKKFQ66D.js → register-list-query-PUCVYYBD.js} +7 -7
- package/dist/cli-bundle/chunks/register-mutation-GGGVBXD5.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-GEQ2H7PS.js → register-operations-NRZ6Q4QE.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-PSJTQBBO.js → register-setup-MV3P3YSF.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-2MPQUMTF.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-H3P77KVU.js → chunk-5UKCSZWT.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-4KQFMOM7.js → chunk-A4ZHHTEX.js} +5 -5
- package/dist/cli-bundle/focused-chunks/{chunk-5OO54BIV.js → chunk-B4YLP5ZB.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-BFEMT5U3.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-THYQR3ON.js → chunk-FW7CSSKL.js} +61 -61
- package/dist/cli-bundle/focused-chunks/{chunk-3RNUMVK4.js → chunk-HYPRPSYD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FQKPOZKS.js → chunk-IRVL4UXR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-Q4VNCTR2.js → chunk-L55KQ2KT.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-52I2SNKJ.js → chunk-MASIOFPG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-RJ3EIHDU.js +26 -0
- package/dist/cli-bundle/focused-chunks/chunk-SEL4Z2OO.js +167 -0
- package/dist/cli-bundle/focused-chunks/{chunk-TYLG5VEB.js → chunk-TB7F4FCR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KIIFVULU.js → chunk-TZUIBG3P.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-N6FUKTZS.js → chunk-Z7YIFCNE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-5KRACGBK.js → chunk-ZDBS2DCT.js} +3 -3
- package/dist/cli-bundle/main.js +27 -27
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +23 -23
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +55 -54
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +7 -7
- package/dist/core/extensions/extension-types.d.ts +15 -0
- package/dist/core/extensions/extension-types.js +1 -1
- package/dist/core/extensions/flag-value-types.d.ts +16 -0
- package/dist/core/extensions/flag-value-types.js +1 -1
- package/dist/core/extensions/registration-contracts.js +1 -1
- package/dist/core/history/event-index.d.ts +8 -1
- package/dist/core/history/event-index.js +19 -18
- package/dist/core/item/item-format.js +3 -3
- package/dist/core/lock/lock.d.ts +8 -1
- package/dist/core/lock/lock.js +3 -3
- package/dist/core/output/mutation-projection.d.ts +0 -21
- package/dist/core/output/mutation-projection.js +1 -1
- package/dist/core/output/output.js +2 -2
- package/dist/core/schema/type-workflows.d.ts +1 -1
- package/dist/core/schema/type-workflows.js +1 -1
- package/dist/core/shared/text-normalization.d.ts +2 -0
- package/dist/core/shared/text-normalization.js +1 -1
- package/dist/core/store/item-mutation-precondition.d.ts +11 -0
- package/dist/core/store/item-mutation-precondition.js +2 -0
- package/dist/core/store/item-store.js +1 -1
- package/dist/core/store/paths.js +1 -1
- package/dist/core/telemetry/policy.d.ts +2 -0
- package/dist/core/telemetry/policy.js +1 -1
- package/dist/core/telemetry/runtime.d.ts +5 -3
- package/dist/core/telemetry/runtime.js +3 -3
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/tool-definitions.js +1 -1
- package/dist/sdk/cli-contracts/agent-output-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/registration-helpers.d.ts +1 -1
- package/dist/sdk/cli-contracts/registration-helpers.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/context-intent-contracts.js +1 -1
- package/dist/sdk/core.d.ts +2 -1
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/governance/health/projection.d.ts +37 -0
- package/dist/sdk/governance/health/projection.js +2 -0
- package/dist/sdk/governance/health.d.ts +7 -18
- package/dist/sdk/governance/health.js +2 -2
- package/dist/sdk/guide-topics.js +1 -1
- package/dist/sdk/index.d.ts +2 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/item-transaction.d.ts +72 -9
- package/dist/sdk/item-transaction.js +1 -1
- package/dist/sdk/lifecycle/claim.d.ts +15 -1
- package/dist/sdk/lifecycle/claim.js +1 -1
- package/dist/sdk/lifecycle/close.d.ts +2 -8
- package/dist/sdk/lifecycle/close.js +1 -1
- package/dist/sdk/lifecycle/core-item-fields.d.ts +6 -0
- package/dist/sdk/lifecycle/core-item-fields.js +2 -0
- package/dist/sdk/lifecycle/mcp-actions.js +1 -1
- package/dist/sdk/lifecycle/mutation-option-validation.d.ts +2 -0
- package/dist/sdk/lifecycle/mutation-option-validation.js +2 -0
- package/dist/sdk/lifecycle/release-handoff.d.ts +12 -0
- package/dist/sdk/lifecycle/release-handoff.js +2 -0
- package/dist/sdk/lifecycle/structured-mutation-plan.d.ts +33 -0
- package/dist/sdk/lifecycle/structured-mutation-plan.js +2 -0
- package/dist/sdk/lifecycle/task-composition.d.ts +2 -2
- package/dist/sdk/lifecycle/task-composition.js +1 -1
- package/dist/sdk/lifecycle/update.d.ts +3 -11
- package/dist/sdk/lifecycle/update.js +1 -1
- package/dist/sdk/lifecycle/workflow-transition.d.ts +13 -0
- package/dist/sdk/lifecycle/workflow-transition.js +2 -0
- package/dist/sdk/lifecycle-policy.d.ts +12 -2
- package/dist/sdk/lifecycle-policy.js +1 -1
- package/dist/sdk/mutation-events.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/list.d.ts +2 -0
- package/dist/sdk/query/list.js +1 -1
- package/dist/sdk/read-output-budget.js +1 -1
- package/dist/sdk/runtime-input.js +1 -1
- package/dist/sdk/runtime-primitives.d.ts +1 -1
- package/dist/sdk/runtime-primitives.js +1 -1
- package/dist/sdk/runtime.d.ts +8 -8
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/structured-mutations.d.ts +52 -1
- package/dist/sdk/structured-mutations.js +1 -1
- package/dist/sdk/workspace-transaction.js +2 -2
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/docs/AGENT_GUIDE.md +33 -4
- package/docs/CLOSE_EVIDENCE_RECOVERY.md +16 -7
- package/docs/COMMANDS.md +111 -10
- package/docs/CONFIGURATION.md +22 -1
- package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +22 -3
- package/docs/ECOSYSTEM_PLANNING_REVIEW.md +5 -1
- package/docs/EXTENSIONS.md +1 -1
- package/docs/OUTPUT_PROJECTION_CONTRACTS.md +10 -0
- package/docs/RELEASING.md +8 -1
- package/docs/SDK.md +114 -2
- package/docs/SDK_CONTEXT_COORDINATION.md +71 -7
- package/docs/SDK_STORAGE_LIFECYCLE.md +24 -2
- package/docs/TESTING.md +23 -0
- package/docs/examples/ci/github-actions-pm-extension-gate.yml +1 -1
- package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +1 -1
- package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +1 -1
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +1 -1
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +16 -3
- package/marketplace.json +1 -1
- package/node_modules/@opentelemetry/core/package.json +2 -1
- package/node_modules/@opentelemetry/instrumentation/package.json +2 -1
- package/node_modules/@opentelemetry/resources/package.json +2 -1
- package/node_modules/@opentelemetry/sdk-trace/package.json +2 -1
- package/node_modules/@opentelemetry/sdk-trace-base/package.json +2 -1
- package/node_modules/@sentry/core/build/cjs/client.js +5 -4
- package/node_modules/@sentry/core/build/cjs/tracing/ai/gen-ai-attributes.js +0 -4
- package/node_modules/@sentry/core/build/cjs/tracing/ai/utils.js +33 -23
- package/node_modules/@sentry/core/build/cjs/tracing/anthropic-ai/index.js +7 -6
- package/node_modules/@sentry/core/build/cjs/tracing/anthropic-ai/streaming.js +3 -1
- package/node_modules/@sentry/core/build/cjs/tracing/google-genai/index.js +13 -14
- package/node_modules/@sentry/core/build/cjs/tracing/google-genai/streaming.js +6 -1
- package/node_modules/@sentry/core/build/cjs/tracing/langchain/utils.js +71 -21
- package/node_modules/@sentry/core/build/cjs/tracing/langgraph/utils.js +23 -33
- package/node_modules/@sentry/core/build/cjs/tracing/openai/streaming.js +6 -0
- package/node_modules/@sentry/core/build/cjs/tracing/openai/utils.js +11 -0
- package/node_modules/@sentry/core/build/cjs/tracing/workers-ai/utils.js +1 -1
- package/node_modules/@sentry/core/build/cjs/utils/eventUtils.js +4 -0
- package/node_modules/@sentry/core/build/cjs/utils/prepareEvent.js +2 -2
- package/node_modules/@sentry/core/build/cjs/utils/version.js +1 -1
- package/node_modules/@sentry/core/build/esm/client.js +6 -5
- package/node_modules/@sentry/core/build/esm/package.json +1 -1
- package/node_modules/@sentry/core/build/esm/tracing/ai/gen-ai-attributes.js +1 -3
- package/node_modules/@sentry/core/build/esm/tracing/ai/utils.js +34 -24
- package/node_modules/@sentry/core/build/esm/tracing/anthropic-ai/index.js +8 -7
- package/node_modules/@sentry/core/build/esm/tracing/anthropic-ai/streaming.js +3 -1
- package/node_modules/@sentry/core/build/esm/tracing/google-genai/index.js +15 -16
- package/node_modules/@sentry/core/build/esm/tracing/google-genai/streaming.js +6 -1
- package/node_modules/@sentry/core/build/esm/tracing/langchain/utils.js +73 -24
- package/node_modules/@sentry/core/build/esm/tracing/langgraph/utils.js +24 -34
- package/node_modules/@sentry/core/build/esm/tracing/openai/streaming.js +6 -0
- package/node_modules/@sentry/core/build/esm/tracing/openai/utils.js +11 -0
- package/node_modules/@sentry/core/build/esm/tracing/workers-ai/utils.js +2 -2
- package/node_modules/@sentry/core/build/esm/utils/eventUtils.js +4 -1
- package/node_modules/@sentry/core/build/esm/utils/prepareEvent.js +2 -2
- package/node_modules/@sentry/core/build/esm/utils/version.js +1 -1
- package/node_modules/@sentry/core/build/types/tracing/ai/utils.d.ts +3 -7
- package/node_modules/@sentry/core/build/types/tracing/anthropic-ai/types.d.ts +2 -2
- package/node_modules/@sentry/core/build/types/tracing/langchain/types.d.ts +15 -0
- package/node_modules/@sentry/core/build/types/tracing/langchain/utils.d.ts +1 -0
- package/node_modules/@sentry/core/build/types/tracing/langgraph/utils.d.ts +2 -0
- package/node_modules/@sentry/core/build/types/tracing/openai/types.d.ts +2 -0
- package/node_modules/@sentry/core/build/types/utils/eventUtils.d.ts +2 -1
- package/node_modules/@sentry/core/build/types-ts3.8/tracing/ai/utils.d.ts +3 -7
- package/node_modules/@sentry/core/build/types-ts3.8/tracing/anthropic-ai/types.d.ts +2 -2
- package/node_modules/@sentry/core/build/types-ts3.8/tracing/langchain/types.d.ts +15 -0
- package/node_modules/@sentry/core/build/types-ts3.8/tracing/langchain/utils.d.ts +1 -0
- package/node_modules/@sentry/core/build/types-ts3.8/tracing/langgraph/utils.d.ts +2 -0
- package/node_modules/@sentry/core/build/types-ts3.8/tracing/openai/types.d.ts +2 -0
- package/node_modules/@sentry/core/build/types-ts3.8/utils/eventUtils.d.ts +2 -1
- package/node_modules/@sentry/core/package.json +1 -1
- package/node_modules/@sentry/node/build/esm/package.json +1 -1
- package/node_modules/@sentry/node/package.json +5 -5
- package/node_modules/@sentry/node-core/build/cjs/integrations/anr/index.js +1 -1
- package/node_modules/@sentry/node-core/build/cjs/integrations/local-variables/local-variables-async.js +1 -1
- package/node_modules/@sentry/node-core/build/esm/integrations/anr/index.js +1 -1
- package/node_modules/@sentry/node-core/build/esm/integrations/anr/worker-script.js +2 -2
- package/node_modules/@sentry/node-core/build/esm/integrations/local-variables/local-variables-async.js +1 -1
- package/node_modules/@sentry/node-core/build/esm/integrations/local-variables/worker-script.js +1 -1
- package/node_modules/@sentry/node-core/build/esm/package.json +1 -1
- package/node_modules/@sentry/node-core/package.json +10 -4
- package/node_modules/@sentry/opentelemetry/build/esm/package.json +1 -1
- package/node_modules/@sentry/opentelemetry/package.json +5 -2
- package/node_modules/@sentry/server-utils/build/THIRD-PARTY-LICENSES.txt +55 -55
- package/node_modules/@sentry/server-utils/build/esm/package.json +1 -1
- package/node_modules/@sentry/server-utils/package.json +2 -2
- package/node_modules/@toon-format/toon/README.md +10 -41
- package/node_modules/@toon-format/toon/dist/index.d.mts +12 -36
- package/node_modules/@toon-format/toon/dist/index.mjs +134 -202
- package/node_modules/@toon-format/toon/package.json +2 -2
- package/node_modules/debug/package.json +3 -0
- package/node_modules/fdir/package.json +3 -0
- package/node_modules/has-flag/index.d.ts +39 -0
- package/node_modules/has-flag/index.js +8 -0
- package/node_modules/has-flag/license +9 -0
- package/node_modules/has-flag/package.json +46 -0
- package/node_modules/has-flag/readme.md +89 -0
- package/node_modules/supports-color/browser.js +5 -0
- package/node_modules/supports-color/index.js +135 -0
- package/node_modules/supports-color/license +9 -0
- package/node_modules/supports-color/package.json +53 -0
- package/node_modules/supports-color/readme.md +76 -0
- package/package.json +5 -4
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-7OEGNA4X.js +0 -20
- package/dist/cli-bundle/chunks/chunk-EMEEHV2Y.js +0 -2
- package/dist/cli-bundle/chunks/chunk-JSUJWIHQ.js +0 -30
- package/dist/cli-bundle/chunks/chunk-VIMQCFCK.js +0 -2
- package/dist/cli-bundle/chunks/chunk-X2H6UR26.js +0 -236
- package/dist/cli-bundle/chunks/register-mutation-EWMMVZJB.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-5TBAVZDR.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-HTA3TNC3.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-JKBH7YGX.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-PDYE7QDX.js +0 -166
- 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 (
|
|
300
|
-
const closingQuoteIndex = findClosingQuote(
|
|
298
|
+
if (content.startsWith("\"")) {
|
|
299
|
+
const closingQuoteIndex = findClosingQuote(content, 0);
|
|
301
300
|
if (closingQuoteIndex === -1) return { kind: "notHeader" };
|
|
302
|
-
if (
|
|
303
|
-
|
|
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
|
|
306
|
+
if (firstColonIndex === -1 || firstColonIndex < bracketStart) return { kind: "notHeader" };
|
|
309
307
|
const bracketEnd = findUnquotedChar(content, "]", bracketStart);
|
|
310
|
-
if (bracketEnd === -1) return {
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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,
|
|
430
|
-
* after a nested group's closing brace
|
|
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 =
|
|
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
|
|
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,
|
|
645
|
-
if (
|
|
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,
|
|
663
|
-
if (
|
|
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
|
|
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
|
|
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) && !
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
-
|
|
853
|
-
if (line.depth
|
|
854
|
-
|
|
855
|
-
|
|
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",
|
|
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
|
|
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
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
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",
|
|
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
|
-
|
|
944
|
-
|
|
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
|
|
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
|
|
956
|
-
if (line.depth
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
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
|
|
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
|
|
982
|
-
|
|
983
|
-
if (line.
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
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)
|
|
1033
|
-
if (
|
|
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
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
if (parent.
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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
|
|
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))
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
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))
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
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,
|
|
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 };
|