@wener/common 1.0.1 → 1.0.3
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/lib/cn/DivisionCode.js +311 -0
- package/lib/cn/DivisionCode.js.map +1 -0
- package/lib/cn/Mod11Checksum.js +42 -0
- package/lib/cn/Mod11Checksum.js.map +1 -0
- package/lib/cn/Mod31Checksum.js +48 -0
- package/lib/cn/Mod31Checksum.js.map +1 -0
- package/lib/cn/ResidentIdentityCardNumber.js +50 -0
- package/lib/cn/ResidentIdentityCardNumber.js.map +1 -0
- package/lib/cn/UnifiedSocialCreditCode.js +118 -0
- package/lib/cn/UnifiedSocialCreditCode.js.map +1 -0
- package/lib/cn/formatDate.js +15 -0
- package/lib/cn/formatDate.js.map +1 -0
- package/lib/cn/index.js +4 -0
- package/lib/cn/index.js.map +1 -0
- package/lib/cn/parseSex.js +22 -0
- package/lib/cn/parseSex.js.map +1 -0
- package/lib/cn/types.d.js +8 -0
- package/lib/cn/types.d.js.map +1 -0
- package/lib/meta/defineFileType.js +44 -0
- package/lib/meta/defineFileType.js.map +1 -0
- package/lib/meta/defineMetadata.js +14 -3
- package/lib/meta/defineMetadata.js.map +1 -1
- package/lib/meta/index.js +1 -0
- package/lib/meta/index.js.map +1 -1
- package/lib/parseSort.js +15 -0
- package/lib/parseSort.js.map +1 -1
- package/lib/search/AdvanceSearch.js +10 -0
- package/lib/search/AdvanceSearch.js.map +1 -0
- package/lib/search/formatAdvanceSearch.js +64 -0
- package/lib/search/formatAdvanceSearch.js.map +1 -0
- package/lib/search/index.js +2 -0
- package/lib/search/index.js.map +1 -0
- package/lib/search/optimizeAdvanceSearch.js +89 -0
- package/lib/search/optimizeAdvanceSearch.js.map +1 -0
- package/lib/search/parseAdvanceSearch.js +20 -0
- package/lib/search/parseAdvanceSearch.js.map +1 -0
- package/lib/search/parser.d.js +3 -0
- package/lib/search/parser.d.js.map +1 -0
- package/lib/search/parser.js +3065 -0
- package/lib/search/parser.js.map +1 -0
- package/lib/search/types.d.js +3 -0
- package/lib/search/types.d.js.map +1 -0
- package/package.json +12 -4
- package/src/cn/DivisionCode.test.ts +50 -0
- package/src/cn/DivisionCode.ts +253 -0
- package/src/cn/Mod11Checksum.ts +24 -0
- package/src/cn/Mod31Checksum.ts +36 -0
- package/src/cn/ResidentIdentityCardNumber.test.ts +21 -0
- package/src/cn/ResidentIdentityCardNumber.ts +96 -0
- package/src/cn/UnifiedSocialCreditCode.test.ts +16 -0
- package/src/cn/UnifiedSocialCreditCode.ts +148 -0
- package/src/cn/__snapshots__/ResidentIdentityCardNumber.test.ts.snap +15 -0
- package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +41 -0
- package/src/cn/formatDate.ts +12 -0
- package/src/cn/index.ts +3 -0
- package/src/cn/parseSex.ts +27 -0
- package/src/cn/types.d.ts +51 -0
- package/src/meta/defineFileType.tsx +68 -0
- package/src/meta/defineMetadata.ts +16 -3
- package/src/meta/index.ts +2 -0
- package/src/parseSort.test.ts +1 -0
- package/src/parseSort.ts +18 -0
- package/src/search/AdvanceSearch.test.ts +156 -0
- package/src/search/AdvanceSearch.ts +14 -0
- package/src/search/Makefile +2 -0
- package/src/search/__snapshots__/AdvanceSearch.test.ts.snap +675 -0
- package/src/search/formatAdvanceSearch.ts +61 -0
- package/src/search/index.ts +1 -0
- package/src/search/optimizeAdvanceSearch.ts +90 -0
- package/src/search/parseAdvanceSearch.ts +26 -0
- package/src/search/parser.d.ts +8 -0
- package/src/search/parser.js +2794 -0
- package/src/search/parser.peggy +237 -0
- package/src/search/types.d.ts +71 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 居民身份证
|
|
3
|
+
*
|
|
4
|
+
* @see https://en.wikipedia.org/wiki/Resident_Identity_Card
|
|
5
|
+
* @see https://en.wikipedia.org/wiki/Foreign_Permanent_Resident_ID_Card
|
|
6
|
+
*/
|
|
7
|
+
export interface ResidentIdentityCardInfo {
|
|
8
|
+
/**
|
|
9
|
+
* @title 姓名
|
|
10
|
+
*/
|
|
11
|
+
name: string;
|
|
12
|
+
/**
|
|
13
|
+
* @title 性别
|
|
14
|
+
*/
|
|
15
|
+
sex: 'Male' | 'Female';
|
|
16
|
+
/**
|
|
17
|
+
* @title 民族
|
|
18
|
+
* 例如 '汉'/'满'/'回'
|
|
19
|
+
*/
|
|
20
|
+
ethnicity: string;
|
|
21
|
+
/**
|
|
22
|
+
* @title 出生日期
|
|
23
|
+
* @format date
|
|
24
|
+
*/
|
|
25
|
+
birthDate: string;
|
|
26
|
+
/**
|
|
27
|
+
* @title 地址
|
|
28
|
+
*
|
|
29
|
+
* - 通常为 domicile/户籍地
|
|
30
|
+
*/
|
|
31
|
+
address: string;
|
|
32
|
+
/**
|
|
33
|
+
* @title 身份证号
|
|
34
|
+
*/
|
|
35
|
+
identityCardNumber: string;
|
|
36
|
+
/**
|
|
37
|
+
* @title 签发机关
|
|
38
|
+
*/
|
|
39
|
+
issuer: string;
|
|
40
|
+
/**
|
|
41
|
+
* @title 有效期开始日期
|
|
42
|
+
* @format date
|
|
43
|
+
*/
|
|
44
|
+
validStartDate: string;
|
|
45
|
+
/**
|
|
46
|
+
* @title 有效期结束日期
|
|
47
|
+
* @format date
|
|
48
|
+
* @description 如长期有效则为 空
|
|
49
|
+
*/
|
|
50
|
+
validEndDate?: string;
|
|
51
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type DefineFileTypeOptions = {
|
|
2
|
+
name: string;
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
types?: string[];
|
|
7
|
+
extension?: string;
|
|
8
|
+
extensions?: string[];
|
|
9
|
+
tags?: string[];
|
|
10
|
+
metadata?: Record<string, any>;
|
|
11
|
+
};
|
|
12
|
+
export type FileTypeDef = {
|
|
13
|
+
name: string;
|
|
14
|
+
title: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
type: string; // primary type
|
|
17
|
+
extension: string; // primary extension
|
|
18
|
+
types: string[];
|
|
19
|
+
extensions: string[];
|
|
20
|
+
tags: string[];
|
|
21
|
+
metadata: Record<string, any>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let _all: FileTypeDef[] = [];
|
|
25
|
+
|
|
26
|
+
export function defineFileType({
|
|
27
|
+
type = '',
|
|
28
|
+
types = [],
|
|
29
|
+
extension = '',
|
|
30
|
+
extensions = [],
|
|
31
|
+
...opts
|
|
32
|
+
}: DefineFileTypeOptions): FileTypeDef {
|
|
33
|
+
if (!type) {
|
|
34
|
+
type = types[0] || '';
|
|
35
|
+
}
|
|
36
|
+
if (!extension) {
|
|
37
|
+
extension = extensions[0] || '';
|
|
38
|
+
}
|
|
39
|
+
if (!types.includes(type)) {
|
|
40
|
+
types = [type, ...types];
|
|
41
|
+
}
|
|
42
|
+
if (!extensions.includes(extension)) {
|
|
43
|
+
extensions = [extension, ...extensions];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const def: FileTypeDef = {
|
|
47
|
+
title: opts.title || opts.name,
|
|
48
|
+
type,
|
|
49
|
+
extension,
|
|
50
|
+
types,
|
|
51
|
+
extensions,
|
|
52
|
+
tags: [],
|
|
53
|
+
metadata: {},
|
|
54
|
+
...opts,
|
|
55
|
+
};
|
|
56
|
+
let idx = _all.findIndex((v) => v.name === def.name);
|
|
57
|
+
if (idx >= 0) {
|
|
58
|
+
_all[idx] = def;
|
|
59
|
+
console.warn(`File type ${def.name} is redefined`);
|
|
60
|
+
} else {
|
|
61
|
+
_all.push(def);
|
|
62
|
+
}
|
|
63
|
+
return def;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getFileType(): FileTypeDef[] {
|
|
67
|
+
return _all;
|
|
68
|
+
}
|
|
@@ -46,9 +46,22 @@ export function createMetadataKey<T = never>(a: any, b?: any): MetadataKey<T> {
|
|
|
46
46
|
return k;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export function defineMetadata<T>(res: HasMetadata, key: MetadataKey<T>, opts: T)
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
export function defineMetadata<T>(res: HasMetadata, key: MetadataKey<T>, opts: T): void;
|
|
50
|
+
export function defineMetadata<T>(key: MetadataKey<T>, items: Array<[HasMetadata, T]>): void;
|
|
51
|
+
export function defineMetadata<T>(a: any, b: any, c?: any) {
|
|
52
|
+
if (Array.isArray(b)) {
|
|
53
|
+
const key = a;
|
|
54
|
+
const items = b;
|
|
55
|
+
for (const [res, opts] of items) {
|
|
56
|
+
defineMetadata(res, key, opts);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
const res = a;
|
|
60
|
+
const key = b;
|
|
61
|
+
const opts = c;
|
|
62
|
+
res.metadata = res.metadata || {};
|
|
63
|
+
set(res.metadata, key.key, opts);
|
|
64
|
+
}
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
export function getMetadata<T>(res: HasMetadata | undefined | null, key: MetadataKey<T>): T | undefined {
|
package/src/meta/index.ts
CHANGED
package/src/parseSort.test.ts
CHANGED
|
@@ -35,6 +35,7 @@ test('parseSort', () => {
|
|
|
35
35
|
['a nulls first', [{ field: 'a', order: 'asc', nulls: 'first' }]],
|
|
36
36
|
['-a nulls first', [{ field: 'a', order: 'desc', nulls: 'first' }]],
|
|
37
37
|
['a.b', [{ field: 'a.b', order: 'asc' }]],
|
|
38
|
+
['-a asc', [{ field: 'a', order: 'asc' }]], // asc 优先级高
|
|
38
39
|
]) {
|
|
39
40
|
assert.deepEqual(parseSort(o as any), e as any, `parseOrder: ${JSON.stringify(o)}`);
|
|
40
41
|
}
|
package/src/parseSort.ts
CHANGED
|
@@ -113,3 +113,21 @@ function _parse(v: string) {
|
|
|
113
113
|
nulls,
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
export function formatSort(s: SortRule[]): string[] {
|
|
118
|
+
return s
|
|
119
|
+
.map(({ field, order, nulls }) => {
|
|
120
|
+
if (field) {
|
|
121
|
+
let r = field;
|
|
122
|
+
if (order) {
|
|
123
|
+
r += ` ${order}`;
|
|
124
|
+
}
|
|
125
|
+
if (nulls) {
|
|
126
|
+
r += ` nulls ${nulls}`;
|
|
127
|
+
}
|
|
128
|
+
return r;
|
|
129
|
+
}
|
|
130
|
+
return '';
|
|
131
|
+
})
|
|
132
|
+
.filter(Boolean);
|
|
133
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { AdvanceSearch } from './AdvanceSearch';
|
|
3
|
+
|
|
4
|
+
describe('AdvanceSearch', () => {
|
|
5
|
+
it('should parse', () => {
|
|
6
|
+
for (const input of [
|
|
7
|
+
//
|
|
8
|
+
'-a',
|
|
9
|
+
'a',
|
|
10
|
+
'a b',
|
|
11
|
+
'a -b',
|
|
12
|
+
'a-b',
|
|
13
|
+
'a"b',
|
|
14
|
+
'a&b',
|
|
15
|
+
'NOT a',
|
|
16
|
+
'NOT -a',
|
|
17
|
+
'HELLO -WORLD',
|
|
18
|
+
'(a)',
|
|
19
|
+
'( a OR B )',
|
|
20
|
+
'A OR B',
|
|
21
|
+
'a:[1,2]',
|
|
22
|
+
'a:*..1',
|
|
23
|
+
'a:ok a:=1 a:>1 a:<1 a:>=1 a:<=1 a:!=1 a:1..2 a:*..1 a:1..* a:[1,2] a:(1,2) a:[1,2) a:(1,2]',
|
|
24
|
+
'NOT (A B) AND (a:ok AND size:>1)',
|
|
25
|
+
'NOT -a',
|
|
26
|
+
'NOT (NOT -a)',
|
|
27
|
+
'owner:@me owner:=@me owner:!=@me',
|
|
28
|
+
'@AI:"Where is my car"',
|
|
29
|
+
'/**/ a',
|
|
30
|
+
'/* Hint */ a',
|
|
31
|
+
'/* a */ a /* b */',
|
|
32
|
+
]) {
|
|
33
|
+
let out = AdvanceSearch.parse(input);
|
|
34
|
+
let formated = AdvanceSearch.format(out);
|
|
35
|
+
expect(formated, `reformat`).toMatchSnapshot();
|
|
36
|
+
let optimized = AdvanceSearch.format(AdvanceSearch.optimize(out));
|
|
37
|
+
expect(optimized, `optimized`).toMatchSnapshot();
|
|
38
|
+
expect(out, `parsed`).toMatchSnapshot();
|
|
39
|
+
expect(AdvanceSearch.parse(formated), `reformat match original`).toMatchObject(out);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
it('should parse as expected', () => {
|
|
43
|
+
type Case = [string | null | undefined, AdvanceSearch.Expr[]];
|
|
44
|
+
|
|
45
|
+
const cases: Case[] = [
|
|
46
|
+
[null, []],
|
|
47
|
+
[undefined, []],
|
|
48
|
+
['', []],
|
|
49
|
+
// fast path
|
|
50
|
+
['a', [{ type: 'keyword', value: 'a' }]],
|
|
51
|
+
['a-b', [{ type: 'keyword', value: 'a-b' }]],
|
|
52
|
+
['a"b', [{ type: 'keyword', value: 'a"b' }]],
|
|
53
|
+
[
|
|
54
|
+
'hello world',
|
|
55
|
+
[
|
|
56
|
+
{ type: 'keyword', value: 'hello' },
|
|
57
|
+
{ type: 'keyword', value: 'world' },
|
|
58
|
+
],
|
|
59
|
+
],
|
|
60
|
+
// advance
|
|
61
|
+
['-a', [{ type: 'keyword', value: 'a', negative: true }]],
|
|
62
|
+
[
|
|
63
|
+
'/*Hi*/ hello -world',
|
|
64
|
+
[
|
|
65
|
+
{ type: 'comment', value: 'Hi' },
|
|
66
|
+
{ type: 'keyword', value: 'hello', negative: false },
|
|
67
|
+
{
|
|
68
|
+
type: 'keyword',
|
|
69
|
+
value: 'world',
|
|
70
|
+
negative: true,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
],
|
|
74
|
+
['"Hello"', [{ type: 'keyword', value: 'Hello', exact: true }]],
|
|
75
|
+
['-"Hello"', [{ type: 'keyword', value: 'Hello', exact: true, negative: true }]],
|
|
76
|
+
['is:ok', [{ type: 'compare', field: 'is', operator: 'match', value: { value: 'ok' } }]],
|
|
77
|
+
['-is:ok', [{ type: 'compare', field: 'is', negative: true, operator: 'match', value: { value: 'ok' } }]],
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
for (const [input, expected] of cases) {
|
|
81
|
+
let out = AdvanceSearch.parse(input);
|
|
82
|
+
expect(out).toMatchObject(expected);
|
|
83
|
+
expect(AdvanceSearch.parse(AdvanceSearch.format(out)), 'reformat should match').toMatchObject(expected);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should optimize simple', () => {
|
|
88
|
+
type Case = {
|
|
89
|
+
input: AdvanceSearch.Expr[];
|
|
90
|
+
expected: AdvanceSearch.Expr[];
|
|
91
|
+
};
|
|
92
|
+
const cases: Case[] = [
|
|
93
|
+
// rm empty comment
|
|
94
|
+
{
|
|
95
|
+
input: [
|
|
96
|
+
{ type: 'comment', value: '' },
|
|
97
|
+
{ type: 'keyword', value: 'a' },
|
|
98
|
+
],
|
|
99
|
+
expected: [{ type: 'keyword', value: 'a' }],
|
|
100
|
+
},
|
|
101
|
+
// unwrap parentheses
|
|
102
|
+
{
|
|
103
|
+
input: [
|
|
104
|
+
{ type: 'parentheses', value: [{ type: 'keyword', value: 'a' }] },
|
|
105
|
+
{ type: 'keyword', value: 'b' },
|
|
106
|
+
],
|
|
107
|
+
expected: [
|
|
108
|
+
{ type: 'keyword', value: 'a' },
|
|
109
|
+
{ type: 'keyword', value: 'b' },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
// not not
|
|
113
|
+
{
|
|
114
|
+
input: [{ type: 'not', value: { type: 'not', value: { type: 'keyword', value: 'a' } } }],
|
|
115
|
+
expected: [{ type: 'keyword', value: 'a', negative: false }],
|
|
116
|
+
},
|
|
117
|
+
// not to negative
|
|
118
|
+
{
|
|
119
|
+
input: [{ type: 'not', value: { type: 'keyword', value: 'a' } }],
|
|
120
|
+
expected: [{ type: 'keyword', value: 'a', negative: true }],
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < cases.length; i++) {
|
|
125
|
+
const { input, expected } = cases[i];
|
|
126
|
+
let out = AdvanceSearch.optimize(input);
|
|
127
|
+
expect(out, `case #${i}`).toEqual(expected);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should optimize by formated', () => {
|
|
132
|
+
type Case = [string, string];
|
|
133
|
+
|
|
134
|
+
const cases: Case[] = [
|
|
135
|
+
['( a )', 'a'],
|
|
136
|
+
['NOT a', '-a'],
|
|
137
|
+
['NOT -a', 'a'],
|
|
138
|
+
['NOT is:ok', '-is:ok'],
|
|
139
|
+
['NOT -is:ok', 'is:ok'],
|
|
140
|
+
['NOT -is:=ok', 'is:=ok'],
|
|
141
|
+
['NOT is:=ok', 'is:!=ok'],
|
|
142
|
+
['NOT (NOT -a)', '-a'],
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
for (const [input, expected] of cases) {
|
|
146
|
+
let out = AdvanceSearch.optimize(AdvanceSearch.parse(input));
|
|
147
|
+
expect(AdvanceSearch.format(out), `${input} -> ${expected}: ${JSON.stringify(out)}`).toEqual(expected);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it.fails('should parse parentheses', () => {
|
|
152
|
+
let out = AdvanceSearch.parse('(a)');
|
|
153
|
+
console.log(out);
|
|
154
|
+
expect(out).toEqual([{ type: 'parentheses', value: [{ type: 'keyword', value: 'a' }] }]);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { formatAdvanceSearch } from './formatAdvanceSearch';
|
|
2
|
+
import { optimizeAdvanceSearch } from './optimizeAdvanceSearch';
|
|
3
|
+
import { parseAdvanceSearch } from './parseAdvanceSearch';
|
|
4
|
+
import type * as types from './types';
|
|
5
|
+
|
|
6
|
+
export namespace AdvanceSearch {
|
|
7
|
+
export type Exprs = types.Exprs;
|
|
8
|
+
export type Expr = types.Expr;
|
|
9
|
+
export type Value = types.Value;
|
|
10
|
+
|
|
11
|
+
export const parse = parseAdvanceSearch;
|
|
12
|
+
export const format = formatAdvanceSearch;
|
|
13
|
+
export const optimize = optimizeAdvanceSearch;
|
|
14
|
+
}
|