@dxos/util 0.9.1-main.c7dcc2e112 → 0.10.0
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/dist/lib/browser/index.mjs +31 -21
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +31 -21
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/Position.d.ts +17 -0
- package/dist/types/src/Position.d.ts.map +1 -0
- package/dist/types/src/Position.test.d.ts +2 -0
- package/dist/types/src/Position.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +7 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/Position.test.ts +106 -0
- package/src/Position.ts +29 -0
- package/src/index.ts +1 -1
- package/src/types.ts +8 -0
- package/dist/types/src/position.d.ts +0 -14
- package/dist/types/src/position.d.ts.map +0 -1
- package/dist/types/src/position.test.d.ts +0 -2
- package/dist/types/src/position.test.d.ts.map +0 -1
- package/src/position.test.ts +0 -92
- package/src/position.ts +0 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/util",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Temporary bucket for misc functions, which should graduate into separate packages.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@hazae41/symbol-dispose-polyfill": "^1.0.2",
|
|
33
|
-
"@dxos/debug": "0.
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/node-std": "0.
|
|
33
|
+
"@dxos/debug": "0.10.0",
|
|
34
|
+
"@dxos/keys": "0.10.0",
|
|
35
|
+
"@dxos/invariant": "0.10.0",
|
|
36
|
+
"@dxos/node-std": "0.10.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@dxos/crypto": "0.
|
|
39
|
+
"@dxos/crypto": "0.10.0"
|
|
40
40
|
},
|
|
41
41
|
"publishConfig": {
|
|
42
42
|
"access": "public"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import * as Position from './Position';
|
|
8
|
+
|
|
9
|
+
type TestItem = {
|
|
10
|
+
id: number;
|
|
11
|
+
position?: Position.Position;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe('Position.compare', () => {
|
|
15
|
+
test('should keep items with same position in their original order', ({ expect }) => {
|
|
16
|
+
const items: TestItem[] = [
|
|
17
|
+
{ id: 1 },
|
|
18
|
+
{ id: 2 },
|
|
19
|
+
{ id: 3, position: Position.first },
|
|
20
|
+
{ id: 4, position: Position.first },
|
|
21
|
+
{ id: 5, position: Position.last },
|
|
22
|
+
{ id: 6, position: Position.last },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const sorted = [...items].sort(Position.compare);
|
|
26
|
+
|
|
27
|
+
expect(sorted.findIndex((item) => item.id === 3)).toBeLessThan(sorted.findIndex((item) => item.id === 4));
|
|
28
|
+
expect(sorted.findIndex((item) => item.id === 1)).toBeLessThan(sorted.findIndex((item) => item.id === 2));
|
|
29
|
+
expect(sorted.findIndex((item) => item.id === 5)).toBeLessThan(sorted.findIndex((item) => item.id === 6));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should place Position.first items before items in natural order', ({ expect }) => {
|
|
33
|
+
const items: TestItem[] = [{ id: 1 }, { id: 2, position: Position.first }];
|
|
34
|
+
|
|
35
|
+
const sorted = [...items].sort(Position.compare);
|
|
36
|
+
expect(sorted[0].position).toBe(Position.first);
|
|
37
|
+
expect(sorted[1].position).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should place Position.last items after items in natural order', ({ expect }) => {
|
|
41
|
+
const items: TestItem[] = [{ id: 1, position: Position.last }, { id: 2 }];
|
|
42
|
+
|
|
43
|
+
const sorted = [...items].sort(Position.compare);
|
|
44
|
+
expect(sorted[0].position).toBeUndefined();
|
|
45
|
+
expect(sorted[1].position).toBe(Position.last);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('should treat items without position as natural order', ({ expect }) => {
|
|
49
|
+
const items: TestItem[] = [{ id: 1 }, { id: 2, position: Position.first }, { id: 3, position: Position.last }];
|
|
50
|
+
|
|
51
|
+
const sorted = [...items].sort(Position.compare);
|
|
52
|
+
expect(sorted[0].position).toBe(Position.first);
|
|
53
|
+
expect(sorted[1].position).toBeUndefined();
|
|
54
|
+
expect(sorted[2].position).toBe(Position.last);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('should correctly sort mixed positions', ({ expect }) => {
|
|
58
|
+
const items: TestItem[] = [
|
|
59
|
+
{ id: 1, position: Position.last },
|
|
60
|
+
{ id: 2 },
|
|
61
|
+
{ id: 3, position: Position.first },
|
|
62
|
+
{ id: 4 },
|
|
63
|
+
{ id: 5, position: Position.first },
|
|
64
|
+
{ id: 6, position: Position.last },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const sorted = [...items].sort(Position.compare);
|
|
68
|
+
|
|
69
|
+
expect(sorted[0].position).toBe(Position.first);
|
|
70
|
+
expect(sorted[1].position).toBe(Position.first);
|
|
71
|
+
|
|
72
|
+
expect(sorted[2].position).toBeUndefined();
|
|
73
|
+
expect(sorted[3].position).toBeUndefined();
|
|
74
|
+
|
|
75
|
+
expect(sorted[4].position).toBe(Position.last);
|
|
76
|
+
expect(sorted[5].position).toBe(Position.last);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('should sort numeric tiers between first and last', ({ expect }) => {
|
|
80
|
+
const items: TestItem[] = [
|
|
81
|
+
{ id: 1, position: Position.last },
|
|
82
|
+
{ id: 2, position: 1 },
|
|
83
|
+
{ id: 3, position: Position.first },
|
|
84
|
+
{ id: 4 },
|
|
85
|
+
{ id: 5, position: -1 },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const sorted = [...items].sort(Position.compare);
|
|
89
|
+
|
|
90
|
+
expect(sorted[0].id).toBe(3); // Position.first = -Infinity
|
|
91
|
+
expect(sorted[1].id).toBe(5); // -1
|
|
92
|
+
expect(sorted[2].id).toBe(4); // undefined = 0
|
|
93
|
+
expect(sorted[3].id).toBe(2); // 1
|
|
94
|
+
expect(sorted[4].id).toBe(1); // Position.last = Infinity
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should handle empty arrays', ({ expect }) => {
|
|
98
|
+
const items: TestItem[] = [];
|
|
99
|
+
expect(() => [...items].sort(Position.compare)).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should handle single item arrays', ({ expect }) => {
|
|
103
|
+
const items: TestItem[] = [{ id: 1 }];
|
|
104
|
+
expect(() => [...items].sort(Position.compare)).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
});
|
package/src/Position.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @import-as-namespace
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Numeric priority order — lower values sort earlier.
|
|
9
|
+
* - `undefined` - Treated as `0`; sorts between `first` and `last`.
|
|
10
|
+
* - `first` (-Infinity) - Sorts before all other items.
|
|
11
|
+
* - `last` (Infinity) - Sorts after all other items.
|
|
12
|
+
* - Any integer between `first` and `last` allows custom tiers.
|
|
13
|
+
*/
|
|
14
|
+
export type Position = number;
|
|
15
|
+
|
|
16
|
+
export const first: Position = -Infinity;
|
|
17
|
+
export const last: Position = Infinity;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Comparator for sorting by position.
|
|
21
|
+
*/
|
|
22
|
+
export const compare = <T extends { position?: Position }>({ position: a }: T, { position: b }: T): -1 | 0 | 1 => {
|
|
23
|
+
const aVal = a ?? 0;
|
|
24
|
+
const bVal = b ?? 0;
|
|
25
|
+
if (aVal === bVal) {
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
return aVal < bVal ? -1 : 1;
|
|
29
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -37,7 +37,7 @@ export * from './order-keys';
|
|
|
37
37
|
export * from './order';
|
|
38
38
|
export * from './pick';
|
|
39
39
|
export * from './platform';
|
|
40
|
-
export * from './
|
|
40
|
+
export * as Position from './Position';
|
|
41
41
|
export * from './random';
|
|
42
42
|
export * from './range';
|
|
43
43
|
export * from './reducers';
|
package/src/types.ts
CHANGED
|
@@ -12,6 +12,14 @@ export type MaybePromise<T> = T | Promise<T>;
|
|
|
12
12
|
|
|
13
13
|
export type GuardedType<T> = T extends ((value: any) => value is infer R) ? R : never;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Flat intersection of up to five types. Behaves like `A & B & C & ...` but
|
|
17
|
+
* formats as a comma-separated tuple, which fits multi-line layouts more
|
|
18
|
+
* cleanly than chained `&` operators. Unused slots default to `unknown`,
|
|
19
|
+
* which is inert under intersection (`T & unknown = T`).
|
|
20
|
+
*/
|
|
21
|
+
export type Merge<A, B = unknown, C = unknown, D = unknown, E = unknown> = A & B & C & D & E;
|
|
22
|
+
|
|
15
23
|
/**
|
|
16
24
|
* Removes readonly modifiers from top-level properties of T.
|
|
17
25
|
* Also converts readonly arrays at the top level to mutable arrays.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Determines priority order:
|
|
3
|
-
* - `undefined` - Remain in natural order.
|
|
4
|
-
* - `first` - Placed before items in natural order.
|
|
5
|
-
* - `last` - Placed after items in natural order.
|
|
6
|
-
*/
|
|
7
|
-
export type Position = 'first' | 'last';
|
|
8
|
-
/**
|
|
9
|
-
* Sorting function for sorting by position.
|
|
10
|
-
*/
|
|
11
|
-
export declare const byPosition: <T extends {
|
|
12
|
-
position?: Position;
|
|
13
|
-
}>({ position: a }: T, { position: b }: T) => -1 | 0 | 1;
|
|
14
|
-
//# sourceMappingURL=position.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../src/position.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAE,mBAAmB,CAAC,mBAAmB,CAAC,eAUnG,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"position.test.d.ts","sourceRoot":"","sources":["../../../src/position.test.ts"],"names":[],"mappings":""}
|
package/src/position.test.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { describe, test } from 'vitest';
|
|
6
|
-
|
|
7
|
-
import { type Position, byPosition } from './position';
|
|
8
|
-
|
|
9
|
-
type TestItem = {
|
|
10
|
-
id: number;
|
|
11
|
-
position?: Position;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
describe('byPosition', () => {
|
|
15
|
-
test('should keep items with same position in their original order', ({ expect }) => {
|
|
16
|
-
const items: TestItem[] = [
|
|
17
|
-
{ id: 1 },
|
|
18
|
-
{ id: 2 },
|
|
19
|
-
{ id: 3, position: 'first' },
|
|
20
|
-
{ id: 4, position: 'first' },
|
|
21
|
-
{ id: 5, position: 'last' },
|
|
22
|
-
{ id: 6, position: 'last' },
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const sorted = [...items].sort(byPosition);
|
|
26
|
-
|
|
27
|
-
// Check that items with the same position maintain relative order
|
|
28
|
-
expect(sorted.findIndex((item) => item.id === 3)).toBeLessThan(sorted.findIndex((item) => item.id === 4));
|
|
29
|
-
expect(sorted.findIndex((item) => item.id === 1)).toBeLessThan(sorted.findIndex((item) => item.id === 2));
|
|
30
|
-
expect(sorted.findIndex((item) => item.id === 5)).toBeLessThan(sorted.findIndex((item) => item.id === 6));
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('should place "first" items before items in natural order', ({ expect }) => {
|
|
34
|
-
const items: TestItem[] = [{ id: 1 }, { id: 2, position: 'first' }];
|
|
35
|
-
|
|
36
|
-
const sorted = [...items].sort(byPosition);
|
|
37
|
-
expect(sorted[0].position).toBe('first');
|
|
38
|
-
expect(sorted[1].position).toBeUndefined();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('should place "last" items after items in natural order', ({ expect }) => {
|
|
42
|
-
const items: TestItem[] = [{ id: 1, position: 'last' }, { id: 2 }];
|
|
43
|
-
|
|
44
|
-
const sorted = [...items].sort(byPosition);
|
|
45
|
-
expect(sorted[0].position).toBeUndefined();
|
|
46
|
-
expect(sorted[1].position).toBe('last');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('should treat items without position as natural order', ({ expect }) => {
|
|
50
|
-
const items: TestItem[] = [{ id: 1 }, { id: 2, position: 'first' }, { id: 3, position: 'last' }];
|
|
51
|
-
|
|
52
|
-
const sorted = [...items].sort(byPosition);
|
|
53
|
-
expect(sorted[0].position).toBe('first');
|
|
54
|
-
expect(sorted[1].position).toBeUndefined();
|
|
55
|
-
expect(sorted[2].position).toBe('last');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('should correctly sort mixed positions', ({ expect }) => {
|
|
59
|
-
const items: TestItem[] = [
|
|
60
|
-
{ id: 1, position: 'last' },
|
|
61
|
-
{ id: 2 },
|
|
62
|
-
{ id: 3, position: 'first' },
|
|
63
|
-
{ id: 4 },
|
|
64
|
-
{ id: 5, position: 'first' },
|
|
65
|
-
{ id: 6, position: 'last' },
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
const sorted = [...items].sort(byPosition);
|
|
69
|
-
|
|
70
|
-
// All "first" items should come first
|
|
71
|
-
expect(sorted[0].position).toBe('first');
|
|
72
|
-
expect(sorted[1].position).toBe('first');
|
|
73
|
-
|
|
74
|
-
// Natural-order items (undefined) should be in the middle
|
|
75
|
-
expect(sorted[2].position).toBeUndefined();
|
|
76
|
-
expect(sorted[3].position).toBeUndefined();
|
|
77
|
-
|
|
78
|
-
// "last" items should be at the end
|
|
79
|
-
expect(sorted[4].position).toBe('last');
|
|
80
|
-
expect(sorted[5].position).toBe('last');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test('should handle empty arrays', ({ expect }) => {
|
|
84
|
-
const items: TestItem[] = [];
|
|
85
|
-
expect(() => [...items].sort(byPosition)).not.toThrow();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('should handle single item arrays', ({ expect }) => {
|
|
89
|
-
const items: TestItem[] = [{ id: 1 }];
|
|
90
|
-
expect(() => [...items].sort(byPosition)).not.toThrow();
|
|
91
|
-
});
|
|
92
|
-
});
|
package/src/position.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// TODO(burdon): This shouldn't be in this low-level util? Move to app-framework?
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Determines priority order:
|
|
9
|
-
* - `undefined` - Remain in natural order.
|
|
10
|
-
* - `first` - Placed before items in natural order.
|
|
11
|
-
* - `last` - Placed after items in natural order.
|
|
12
|
-
*/
|
|
13
|
-
export type Position = 'first' | 'last';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Sorting function for sorting by position.
|
|
17
|
-
*/
|
|
18
|
-
export const byPosition = <T extends { position?: Position }>({ position: a }: T, { position: b }: T) => {
|
|
19
|
-
if (a === b) {
|
|
20
|
-
return 0;
|
|
21
|
-
} else if (a === 'first' || b === 'last') {
|
|
22
|
-
return -1;
|
|
23
|
-
} else if (b === 'first' || a === 'last') {
|
|
24
|
-
return 1;
|
|
25
|
-
} else {
|
|
26
|
-
return 0;
|
|
27
|
-
}
|
|
28
|
-
};
|