Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
safe-stable-stringify
ajv-draft-04
ajv-formats
@moonrepo/cli
Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1364,9 +1364,6 @@
"js-yaml": "4.1.1",
"jsdom": "20.0.1",
"json-schema-to-ts": "3.1.1",
"json-stable-stringify": "1.0.1",
"json-stringify-pretty-compact": "1.2.0",
"json-stringify-safe": "5.0.1",
"jsonwebtoken": "9.0.2",
"jsts": "1.6.2",
"kea": "2.6.0",
Expand Down Expand Up @@ -1462,6 +1459,7 @@
"rison-node": "2.1.1",
"rxjs": "7.8.2",
"safe-squel": "5.12.5",
"safe-stable-stringify": "2.5.0",
"seedrandom": "3.0.5",
"semver": "7.7.3",
"snakecase-keys": "8.0.1",
Expand Down Expand Up @@ -1863,7 +1861,6 @@
"@types/js-yaml": "4.0.9",
"@types/jsdom": "20.0.1",
"@types/json-schema": "7.0.15",
"@types/json-stable-stringify": "1.0.32",
"@types/json5": "2.2.0",
"@types/jsonwebtoken": "9.0.10",
"@types/license-checker": "25.0.6",
Expand Down
6 changes: 3 additions & 3 deletions packages/kbn-optimizer/src/optimizer/diff_cache_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
*/

import { diffStrings } from '@kbn/dev-utils';
import jsonStable from 'json-stable-stringify';
import { stableStringify } from '@kbn/std';

export function diffCacheKey(expected?: unknown, actual?: unknown) {
const expectedJson = jsonStable(expected, {
const expectedJson = stableStringify(expected, {
space: ' ',
});
const actualJson = jsonStable(actual, {
const actualJson = stableStringify(actual, {
space: ' ',
});

Expand Down
61 changes: 20 additions & 41 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,25 @@
"enabled": true,
"minimumReleaseAge": "14 days"
},
{
"groupName": "safe-stable-stringify",
"matchDepNames": [
"safe-stable-stringify"
],
"reviewers": [
"team:kibana-core"
],
"matchBaseBranches": [
"main"
],
"labels": [
"Team:Core",
"release_note:skip",
"backport:all-open"
],
"enabled": true,
"minimumReleaseAge": "14 days"
},
{
"groupName": "@elastic/kibana-core dependencies",
"matchDepNames": [
Expand Down Expand Up @@ -1623,26 +1642,6 @@
"minimumReleaseAge": "14 days",
"enabled": false
},
{
"groupName": "json-stable-stringify",
"matchDepNames": [
"json-stable-stringify",
"@types/json-stable-stringify"
],
"reviewers": [
"team:kibana-operations"
],
"matchBaseBranches": [
"main"
],
"labels": [
"Team:Operations",
"release_note:skip",
"backport:all-open"
],
"minimumReleaseAge": "14 days",
"enabled": true
},
{
"groupName": "jsdom",
"matchDepNames": [
Expand Down Expand Up @@ -2258,25 +2257,6 @@
"minimumReleaseAge": "14 days",
"enabled": true
},
{
"groupName": "json-stringify-pretty-compact",
"matchDepNames": [
"json-stringify-pretty-compact"
],
"reviewers": [
"team:kibana-visualizations"
],
"matchBaseBranches": [
"main"
],
"labels": [
"Feature:Vega",
"Team:Visualizations",
"release_note:skip"
],
"minimumReleaseAge": "14 days",
"enabled": true
},
{
"groupName": "@elastic/kibana-visualizations Color related modules",
"matchDepNames": [
Expand Down Expand Up @@ -4580,7 +4560,6 @@
"@types/stats-lite",
"@types/textarea-caret",
"email-addresses",
"json-stringify-safe",
"murmurhash",
"stats-lite",
"textarea-caret"
Expand Down Expand Up @@ -4926,4 +4905,4 @@
"datasourceTemplate": "docker"
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import path from 'node:path';
import { schema } from '@kbn/config-schema';
import stringify from 'json-stable-stringify';
import { stableStringify } from '@kbn/std';
import { createPromiseFromStreams, createMapStream, createConcatStream } from '@kbn/utils';

import type { KibanaRequest } from '@kbn/core-http-server';
Expand Down Expand Up @@ -264,7 +264,7 @@ NOTE: The \`savedObjects.maxImportExportSize\` configuration setting limits the
const docsToExport: string[] = await createPromiseFromStreams([
exportStream,
createMapStream((obj: unknown) => {
return stringify(obj);
return stableStringify(obj);
}),
createConcatStream([]),
]);
Expand Down
1 change: 1 addition & 0 deletions src/platform/packages/shared/kbn-es-archiver/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependsOn:
- '@kbn/repo-info'
- '@kbn/jest-serializers'
- '@kbn/apm-utils'
- '@kbn/std'
tags:
- test-helper
- package
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

import { createGzip, Z_BEST_COMPRESSION } from 'zlib';
import { PassThrough } from 'stream';
import stringify from 'json-stable-stringify';
import { stableStringify } from '@kbn/std';

import { createMapStream, createIntersperseStream } from '@kbn/utils';
import { RECORD_SEPARATOR } from './constants';

export function createFormatArchiveStreams({ gzip = false }: { gzip?: boolean } = {}) {
return [
createMapStream((record) => stringify(record, { space: ' ' })),
createMapStream((record) => stableStringify(record, { space: ' ' })),
createIntersperseStream(RECORD_SEPARATOR),
gzip ? createGzip({ level: Z_BEST_COMPRESSION }) : new PassThrough(),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@kbn/repo-info",
"@kbn/jest-serializers",
"@kbn/apm-utils",
"@kbn/std",
],
"exclude": [
"target/**/*",
Expand Down
5 changes: 5 additions & 0 deletions src/platform/packages/shared/kbn-std/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export { matchWildcardPattern } from './src/match_wildcard_pattern';

export { safeJsonParse } from './src/safe_json_parse';
export { safeJsonStringify } from './src/safe_json_stringify';
export { stableStringify, type StableStringifyOptions } from './src/stable_stringify';
export {
prettyCompactStringify,
type PrettyCompactStringifyOptions,
} from './src/pretty_compact_stringify';

export { bytePartition } from './src/byte_partition/byte_partition';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { prettyCompactStringify } from './pretty_compact_stringify';

describe('prettyCompactStringify', () => {
it('stringifies primitives correctly', () => {
expect(prettyCompactStringify(null)).toBe('null');
expect(prettyCompactStringify(undefined)).toBe('null');
expect(prettyCompactStringify(true)).toBe('true');
expect(prettyCompactStringify(false)).toBe('false');
expect(prettyCompactStringify(42)).toBe('42');
expect(prettyCompactStringify('hello')).toBe('"hello"');
});

it('keeps short arrays on a single line', () => {
expect(prettyCompactStringify([1, 2, 3])).toBe('[1, 2, 3]');
expect(prettyCompactStringify(['a', 'b'])).toBe('["a", "b"]');
});

it('keeps short objects on a single line', () => {
expect(prettyCompactStringify({ a: 1, b: 2 })).toBe('{"a": 1, "b": 2}');
});

it('expands long arrays to multiple lines', () => {
const longArray = [
{ x: 1, y: 2, description: 'point one' },
{ x: 2, y: 1, description: 'point two' },
];
const result = prettyCompactStringify(longArray, { maxLength: 40 });
expect(result).toContain('\n');
});

it('expands long objects to multiple lines', () => {
const longObject = {
name: 'test',
description: 'a very long description that exceeds the maximum line length',
};
const result = prettyCompactStringify(longObject, { maxLength: 40 });
expect(result).toContain('\n');
});

it('returns empty array notation for empty arrays', () => {
expect(prettyCompactStringify([])).toBe('[]');
});

it('returns empty object notation for empty objects', () => {
expect(prettyCompactStringify({})).toBe('{}');
});

it('respects maxLength option', () => {
const obj = { a: 1, b: 2, c: 3 };
// With a very small maxLength, should expand
const result = prettyCompactStringify(obj, { maxLength: 10 });
expect(result).toContain('\n');
});

it('respects indent option', () => {
const obj = { nested: { value: 1 } };
const result = prettyCompactStringify(obj, { maxLength: 10, indent: 4 });
// Should have 4-space indentation
expect(result).toContain(' ');
});

it('supports replacer function', () => {
const obj = { a: 1, b: 'secret' };
const replacer = (key: string, value: unknown) => {
if (key === 'b') return undefined;
return value;
};
expect(prettyCompactStringify(obj, { replacer })).toBe('{"a": 1}');
});

it('supports replacer array', () => {
const obj = { a: 1, b: 2, c: 3 };
expect(prettyCompactStringify(obj, { replacer: ['a', 'c'] })).toBe('{"a": 1, "c": 3}');
});

it('handles nested structures correctly', () => {
const obj = {
items: [1, 2],
meta: { count: 2 },
};
const result = prettyCompactStringify(obj);
// Short enough to fit on one line with default maxLength
expect(result).toBe('{"items": [1, 2], "meta": {"count": 2}}');
});

it('defaults maxLength to 80', () => {
// Create an object that's under 80 chars but would exceed if maxLength were smaller
const obj = { short: 'value', another: 'property' };
const result = prettyCompactStringify(obj);
// Should stay on one line since it's under 80 chars
expect(result).not.toContain('\n');
});
});
Loading
Loading