import type { XPathResult } from '@yaak/template-function-xml'; import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; import { JSONPath } from 'jsonpath-plus'; const RETURN_FIRST = 'first'; const RETURN_ALL = 'all'; const RETURN_JOIN = 'join'; export const plugin: PluginDefinition = { templateFunctions: [ { name: 'json.jsonpath', description: 'Filter JSON-formatted text using JSONPath syntax', previewArgs: ['query'], args: [ { type: 'editor', name: 'input', label: 'Input', language: 'json', placeholder: '{ "foo": "bar" }', }, { type: 'h_stack', inputs: [ { type: 'select', name: 'result', label: 'Return Format', defaultValue: RETURN_FIRST, options: [ { label: 'First result', value: RETURN_FIRST }, { label: 'All results', value: RETURN_ALL }, { label: 'Join with separator', value: RETURN_JOIN }, ], }, { name: 'join', type: 'text', label: 'Separator', optional: true, defaultValue: ', ', dynamic(_ctx, args) { return { hidden: args.values.result !== RETURN_JOIN }; }, }, ], }, { type: 'checkbox', name: 'formatted', label: 'Pretty Print', description: 'Format the output as JSON', dynamic(_ctx, args) { return { hidden: args.values.result === RETURN_JOIN }; }, }, { type: 'text', name: 'query', label: 'Query', placeholder: '$..foo' }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { try { return filterJSONPath( String(args.values.input), String(args.values.query), (args.values.result || RETURN_FIRST) as XPathResult, args.values.join == null ? null : String(args.values.join), Boolean(args.values.formatted), ); } catch { return null; } }, }, { name: 'json.escape', description: 'Escape a JSON string, useful when using the output in JSON values', args: [ { type: 'text', name: 'input', label: 'Input', multiLine: true, placeholder: 'Hello "World"', }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { const input = String(args.values.input ?? ''); return input.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); }, }, { name: 'json.minify', description: 'Remove unnecessary whitespace from a valid JSON string.', args: [ { type: 'editor', language: 'json', name: 'input', label: 'Input', placeholder: '{ "foo": "bar" }', }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { const input = String(args.values.input ?? ''); try { return JSON.stringify(JSON.parse(input)); } catch { return input; } }, }, ], }; export type JSONPathResult = 'first' | 'join' | 'all'; export function filterJSONPath( body: string, path: string, result: JSONPathResult, join: string | null, formatted = false, ): string { const parsed = JSON.parse(body); let items = JSONPath({ path, json: parsed }); if (items == null) { return ''; } if (!Array.isArray(items)) { // Already good } else if (result === 'first') { items = items[0] ?? ''; } else if (result === 'join') { items = items.map((i) => objToStr(i, false)).join(join ?? ''); } return objToStr(items, formatted); } function objToStr(o: unknown, formatted = false): string { if ( Object.prototype.toString.call(o) === '[object Array]' || Object.prototype.toString.call(o) === '[object Object]' ) { return formatted ? JSON.stringify(o, null, 2) : JSON.stringify(o); } return String(o); }