@objectstack/plugin-pinyin-search 16.1.0 → 17.0.0-rc.1
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/CHANGELOG.md +252 -0
- package/dist/index.js +39 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +37 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -6
- package/.turbo/turbo-build.log +0 -22
- package/src/companion-projection.test.ts +0 -168
- package/src/companion-projection.ts +0 -214
- package/src/index.ts +0 -15
- package/src/pinyin-search-plugin.ts +0 -105
- package/src/pinyin.test.ts +0 -39
- package/src/pinyin.ts +0 -63
- package/tsconfig.json +0 -10
package/src/pinyin.test.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect } from 'vitest';
|
|
4
|
-
import { computeSearchCompanionValue } from './pinyin.js';
|
|
5
|
-
|
|
6
|
-
describe('computeSearchCompanionValue (#2486)', () => {
|
|
7
|
-
it('stores full pinyin + initials in one blob ("张伟" → "zhangwei zw")', async () => {
|
|
8
|
-
expect(await computeSearchCompanionValue(['张伟'])).toBe('zhangwei zw');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('recalls every documented input shape as a substring of the blob', async () => {
|
|
12
|
-
const blob = (await computeSearchCompanionValue(['张伟']))!;
|
|
13
|
-
for (const typed of ['zhang', 'wei', 'zhangwei', 'zw']) {
|
|
14
|
-
expect(blob.includes(typed)).toBe(true);
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('handles multi-word and mixed CJK/latin values', async () => {
|
|
19
|
-
const blob = (await computeSearchCompanionValue(['上海分公司']))!;
|
|
20
|
-
expect(blob).toBe('shanghaifengongsi shfgs');
|
|
21
|
-
|
|
22
|
-
const mixed = (await computeSearchCompanionValue(['张伟2号']))!;
|
|
23
|
-
expect(mixed.includes('zhangwei2hao')).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('returns null for non-CJK / empty / non-string values (companion cleared)', async () => {
|
|
27
|
-
expect(await computeSearchCompanionValue(['Zhang Wei'])).toBe(null);
|
|
28
|
-
expect(await computeSearchCompanionValue([''])).toBe(null);
|
|
29
|
-
expect(await computeSearchCompanionValue([null, undefined, 42])).toBe(null);
|
|
30
|
-
expect(await computeSearchCompanionValue([])).toBe(null);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('deduplicates when initials equal the full form (single-char name)', async () => {
|
|
34
|
-
const blob = (await computeSearchCompanionValue(['张']))!;
|
|
35
|
-
expect(blob).toBe('zhang z');
|
|
36
|
-
// no duplicated tokens
|
|
37
|
-
expect(new Set(blob.split(' ')).size).toBe(blob.split(' ').length);
|
|
38
|
-
});
|
|
39
|
-
});
|
package/src/pinyin.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Pinyin normalization for the `__search` companion column (#2486).
|
|
5
|
-
*
|
|
6
|
-
* One normalized blob per record — full pinyin AND initials in the same
|
|
7
|
-
* column — so a single `$contains` recalls every latin input shape:
|
|
8
|
-
*
|
|
9
|
-
* "张伟" → "zhangwei zw"
|
|
10
|
-
* `zhang` / `wei` / `zhangwei` → substring of the full form
|
|
11
|
-
* `zw` → substring of the initials form
|
|
12
|
-
*
|
|
13
|
-
* `pinyin-pro` is loaded lazily on first use: non-Chinese deployments (flag
|
|
14
|
-
* off → hooks never bound) never import it and pay zero cost.
|
|
15
|
-
*
|
|
16
|
-
* Polyphones: pinyin-pro's default heuristics are accepted (issue #2486
|
|
17
|
-
* "待定" — surname polyphone dictionaries are a P2 follow-up).
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { containsCJK } from '@objectstack/objectql';
|
|
21
|
-
|
|
22
|
-
type PinyinFn = (text: string, options?: Record<string, unknown>) => string | string[];
|
|
23
|
-
|
|
24
|
-
let _pinyin: Promise<PinyinFn> | null = null;
|
|
25
|
-
|
|
26
|
-
/** Lazy-load `pinyin-pro` (cached module-wide). */
|
|
27
|
-
function loadPinyin(): Promise<PinyinFn> {
|
|
28
|
-
_pinyin ??= import('pinyin-pro').then((m: any) => (m.pinyin ?? m.default?.pinyin) as PinyinFn);
|
|
29
|
-
return _pinyin;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Lowercase and strip everything that is not a latin letter or digit. */
|
|
33
|
-
function squash(syllables: string | string[]): string {
|
|
34
|
-
const joined = Array.isArray(syllables) ? syllables.join('') : String(syllables ?? '');
|
|
35
|
-
return joined.toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Compute the companion value for the given source-field values.
|
|
40
|
-
*
|
|
41
|
-
* Returns the normalized blob (`"<full-pinyin> <initials>"`, deduplicated)
|
|
42
|
-
* when at least one value contains CJK characters, else `null` — a `null`
|
|
43
|
-
* companion means "nothing pinyin-searchable here" and clears any stale blob
|
|
44
|
-
* when a name is edited away from CJK. Non-CJK values need no companion:
|
|
45
|
-
* their source column already matches latin input directly.
|
|
46
|
-
*/
|
|
47
|
-
export async function computeSearchCompanionValue(values: ReadonlyArray<unknown>): Promise<string | null> {
|
|
48
|
-
const cjkValues = values.filter((v): v is string => containsCJK(v));
|
|
49
|
-
if (cjkValues.length === 0) return null;
|
|
50
|
-
|
|
51
|
-
const pinyin = await loadPinyin();
|
|
52
|
-
const parts: string[] = [];
|
|
53
|
-
for (const value of cjkValues) {
|
|
54
|
-
// `nonZh: 'consecutive'` keeps latin/digit runs intact inside mixed
|
|
55
|
-
// values ("张伟2号" → "zhangwei2hao"), so mixed names stay one token.
|
|
56
|
-
const full = squash(pinyin(value, { toneType: 'none', type: 'array', nonZh: 'consecutive' }));
|
|
57
|
-
const initials = squash(pinyin(value, { pattern: 'first', toneType: 'none', type: 'array', nonZh: 'consecutive' }));
|
|
58
|
-
if (full) parts.push(full);
|
|
59
|
-
if (initials && initials !== full) parts.push(initials);
|
|
60
|
-
}
|
|
61
|
-
if (parts.length === 0) return null;
|
|
62
|
-
return [...new Set(parts)].join(' ');
|
|
63
|
-
}
|