@x12i/api-hub-text 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,11 @@
1
- # Temporary Holding Version
1
+ # @x12i/api-hub-text
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Term and entity helpers for the x12i API hub
4
+
5
+ ESM package for Node.js 24 or newer. Import the public name:
6
+
7
+ ```js
8
+ import * as text from '@x12i/api-hub-text';
9
+ ```
10
+
11
+ The whole set is also available from [`@x12i/api-hub`](https://www.npmjs.com/package/@x12i/api-hub).
@@ -0,0 +1,27 @@
1
+ import { ENGLISH_STOPWORDS } from './stopwords.js';
2
+ export { ENGLISH_STOPWORDS };
3
+ export interface ScoredKeyword {
4
+ term: string;
5
+ score: number;
6
+ }
7
+ export interface KeywordOptions {
8
+ limit?: number;
9
+ /** Extra terms to drop, case-insensitive. */
10
+ ignore?: string[];
11
+ minLength?: number;
12
+ }
13
+ /** Most salient terms of one text (`@x12i/distinctive-terms`, English stopwords removed). */
14
+ export declare function keywords(text: string, opts?: KeywordOptions): ScoredKeyword[];
15
+ /** For each document, the terms that set it apart from the rest of the collection. */
16
+ export declare function distinctiveTerms(documents: string[], opts?: KeywordOptions): ScoredKeyword[][];
17
+ export type IdentifierType = 'email' | 'url' | 'ip' | 'cve' | 'domain';
18
+ export interface IdentifierMention {
19
+ type: IdentifierType;
20
+ value: string;
21
+ /** Code-unit offsets into the input text. */
22
+ start: number;
23
+ end: number;
24
+ }
25
+ export declare const MAX_IDENTIFIER_TEXT = 1000000;
26
+ /** Validated identifiers with their offsets (`@x12i/text-entities`). Nothing from the input is retained. */
27
+ export declare function findIdentifiers(text: string, types?: IdentifierType[], maxMentions?: number): IdentifierMention[];
package/dist/index.js ADDED
@@ -0,0 +1,60 @@
1
+ import { extractTerms } from '@x12i/distinctive-terms';
2
+ import { createEntityExtractor } from '@x12i/text-entities';
3
+ import { ENGLISH_STOPWORDS } from "./stopwords.js";
4
+ export { ENGLISH_STOPWORDS };
5
+ function merge(rows, limit) {
6
+ const byTerm = new Map();
7
+ for (const row of rows) {
8
+ const key = row.term.toLowerCase();
9
+ const prev = byTerm.get(key);
10
+ if (prev)
11
+ prev.score += row.score;
12
+ else
13
+ byTerm.set(key, { term: key, score: row.score });
14
+ }
15
+ return [...byTerm.values()]
16
+ .map((k) => ({ term: k.term, score: Math.round(k.score * 1000) / 1000 }))
17
+ .sort((a, b) => b.score - a.score || a.term.localeCompare(b.term))
18
+ .slice(0, limit);
19
+ }
20
+ function options(opts, topK) {
21
+ return {
22
+ topK,
23
+ stopwords: ENGLISH_STOPWORDS,
24
+ ignore: opts.ignore ?? [],
25
+ minTokenLength: opts.minLength ?? 3,
26
+ minDf: 1,
27
+ };
28
+ }
29
+ /** Most salient terms of one text (`@x12i/distinctive-terms`, English stopwords removed). */
30
+ export function keywords(text, opts = {}) {
31
+ const limit = opts.limit ?? 10;
32
+ const [rows = []] = extractTerms([text], options(opts, limit * 3));
33
+ return merge(rows, limit);
34
+ }
35
+ /** For each document, the terms that set it apart from the rest of the collection. */
36
+ export function distinctiveTerms(documents, opts = {}) {
37
+ const limit = opts.limit ?? 5;
38
+ return extractTerms(documents, options(opts, limit * 3)).map((rows) => merge(rows, limit));
39
+ }
40
+ const TO_LIB = { email: 'EMAIL', url: 'URL', ip: 'IP_ADDRESS', cve: 'CVE_ID', domain: 'DOMAIN' };
41
+ const FROM_LIB = Object.fromEntries(Object.entries(TO_LIB).map(([k, v]) => [v, k]));
42
+ export const MAX_IDENTIFIER_TEXT = 1_000_000;
43
+ /** Validated identifiers with their offsets (`@x12i/text-entities`). Nothing from the input is retained. */
44
+ export function findIdentifiers(text, types = ['email', 'url', 'ip', 'cve'], maxMentions = 1_000) {
45
+ const extractor = createEntityExtractor({
46
+ profileId: 'validated-ascii-v1',
47
+ types: [...new Set(types)].map((t) => TO_LIB[t]),
48
+ limits: { maxInputCodeUnits: MAX_IDENTIFIER_TEXT, maxMentions },
49
+ });
50
+ const result = extractor.extract({
51
+ documentId: 'input',
52
+ revision: '1',
53
+ text,
54
+ segments: [{ id: 'body', zone: 'content', path: '/body', text, language: 'und', kind: 'prose', source: { start: 0, end: text.length } }],
55
+ });
56
+ return result.items
57
+ .filter((m) => FROM_LIB[m.type])
58
+ .map((m) => ({ type: FROM_LIB[m.type], value: m.normalized, start: m.evidence.start, end: m.evidence.end }));
59
+ }
60
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/text/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAc7B,SAAS,KAAK,CAAC,IAAuC,EAAE,KAAa;IACnE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,IAAI;YAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;;YAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;SACxB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;SACxE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SACjE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,OAAO,CAAC,IAAoB,EAAE,IAAY;IACjD,OAAO;QACL,IAAI;QACJ,SAAS,EAAE,iBAAiB;QAC5B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;QACzB,cAAc,EAAE,IAAI,CAAC,SAAS,IAAI,CAAC;QACnC,KAAK,EAAE,CAAC;KACT,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,OAAuB,EAAE;IAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,SAAmB,EAAE,OAAuB,EAAE;IAC7E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;IAC9B,OAAO,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7F,CAAC;AAID,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAW,CAAC;AAC1G,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAmC,CAAC;AAUtH,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAE7C,4GAA4G;AAC5G,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAA0B,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,KAAK;IACxH,MAAM,SAAS,GAAG,qBAAqB,CAAC;QACtC,SAAS,EAAE,oBAAoB;QAC/B,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,EAAE,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,WAAW,EAAE;KAChE,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC;QAC/B,UAAU,EAAE,OAAO;QACnB,QAAQ,EAAE,GAAG;QACb,IAAI;QACJ,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;KAChI,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,KAAK;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAE,EAAE,KAAK,EAAE,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAClH,CAAC","sourcesContent":["import { extractTerms } from '@x12i/distinctive-terms';\nimport { createEntityExtractor } from '@x12i/text-entities';\nimport { ENGLISH_STOPWORDS } from './stopwords.ts';\n\nexport { ENGLISH_STOPWORDS };\n\nexport interface ScoredKeyword {\n term: string;\n score: number;\n}\n\nexport interface KeywordOptions {\n limit?: number;\n /** Extra terms to drop, case-insensitive. */\n ignore?: string[];\n minLength?: number;\n}\n\nfunction merge(rows: { term: string; score: number }[], limit: number): ScoredKeyword[] {\n const byTerm = new Map<string, ScoredKeyword>();\n for (const row of rows) {\n const key = row.term.toLowerCase();\n const prev = byTerm.get(key);\n if (prev) prev.score += row.score;\n else byTerm.set(key, { term: key, score: row.score });\n }\n return [...byTerm.values()]\n .map((k) => ({ term: k.term, score: Math.round(k.score * 1000) / 1000 }))\n .sort((a, b) => b.score - a.score || a.term.localeCompare(b.term))\n .slice(0, limit);\n}\n\nfunction options(opts: KeywordOptions, topK: number) {\n return {\n topK,\n stopwords: ENGLISH_STOPWORDS,\n ignore: opts.ignore ?? [],\n minTokenLength: opts.minLength ?? 3,\n minDf: 1,\n };\n}\n\n/** Most salient terms of one text (`@x12i/distinctive-terms`, English stopwords removed). */\nexport function keywords(text: string, opts: KeywordOptions = {}): ScoredKeyword[] {\n const limit = opts.limit ?? 10;\n const [rows = []] = extractTerms([text], options(opts, limit * 3));\n return merge(rows, limit);\n}\n\n/** For each document, the terms that set it apart from the rest of the collection. */\nexport function distinctiveTerms(documents: string[], opts: KeywordOptions = {}): ScoredKeyword[][] {\n const limit = opts.limit ?? 5;\n return extractTerms(documents, options(opts, limit * 3)).map((rows) => merge(rows, limit));\n}\n\nexport type IdentifierType = 'email' | 'url' | 'ip' | 'cve' | 'domain';\n\nconst TO_LIB = { email: 'EMAIL', url: 'URL', ip: 'IP_ADDRESS', cve: 'CVE_ID', domain: 'DOMAIN' } as const;\nconst FROM_LIB = Object.fromEntries(Object.entries(TO_LIB).map(([k, v]) => [v, k])) as Record<string, IdentifierType>;\n\nexport interface IdentifierMention {\n type: IdentifierType;\n value: string;\n /** Code-unit offsets into the input text. */\n start: number;\n end: number;\n}\n\nexport const MAX_IDENTIFIER_TEXT = 1_000_000;\n\n/** Validated identifiers with their offsets (`@x12i/text-entities`). Nothing from the input is retained. */\nexport function findIdentifiers(text: string, types: IdentifierType[] = ['email', 'url', 'ip', 'cve'], maxMentions = 1_000): IdentifierMention[] {\n const extractor = createEntityExtractor({\n profileId: 'validated-ascii-v1',\n types: [...new Set(types)].map((t) => TO_LIB[t]),\n limits: { maxInputCodeUnits: MAX_IDENTIFIER_TEXT, maxMentions },\n });\n const result = extractor.extract({\n documentId: 'input',\n revision: '1',\n text,\n segments: [{ id: 'body', zone: 'content', path: '/body', text, language: 'und', kind: 'prose', source: { start: 0, end: text.length } }],\n } as never);\n return result.items\n .filter((m) => FROM_LIB[m.type])\n .map((m) => ({ type: FROM_LIB[m.type]!, value: m.normalized, start: m.evidence.start, end: m.evidence.end }));\n}\n"]}
@@ -0,0 +1,2 @@
1
+ /** Function words that never make useful keywords. English only for now. */
2
+ export declare const ENGLISH_STOPWORDS: string[];
@@ -0,0 +1,17 @@
1
+ /** Function words that never make useful keywords. English only for now. */
2
+ export const ENGLISH_STOPWORDS = `
3
+ a about above after again against all almost also although always am among an and another any anyone anything are
4
+ around as at be became because become been before being below between both but by can cannot could did do does
5
+ doing done down during each either else enough etc even ever every few for from further get gets got had has have
6
+ having he her here hers herself him himself his how however i if in into is it its itself just least less let like
7
+ made make many may me might more most much must my myself near need neither never no nor not now of off often on
8
+ once one only onto or other others otherwise our ours ourselves out over own per perhaps please put rather really
9
+ same see seem seemed seems several shall she should since so some something sometimes still such than that the
10
+ their theirs them themselves then there therefore these they this those though through thus to together too
11
+ toward towards under until up upon us use used uses using very via was we well were what whatever when whenever
12
+ where whereas wherever whether which while who whoever whom whose why will with within without would yet you your
13
+ yours yourself yourselves
14
+ `
15
+ .split(/\s+/)
16
+ .filter(Boolean);
17
+ //# sourceMappingURL=stopwords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stopwords.js","sourceRoot":"","sources":["../../../../../packages/text/src/stopwords.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;CAYhC;KACE,KAAK,CAAC,KAAK,CAAC;KACZ,MAAM,CAAC,OAAO,CAAC,CAAC","sourcesContent":["/** Function words that never make useful keywords. English only for now. */\nexport const ENGLISH_STOPWORDS = `\na about above after again against all almost also although always am among an and another any anyone anything are\naround as at be became because become been before being below between both but by can cannot could did do does\ndoing done down during each either else enough etc even ever every few for from further get gets got had has have\nhaving he her here hers herself him himself his how however i if in into is it its itself just least less let like\nmade make many may me might more most much must my myself near need neither never no nor not now of off often on\nonce one only onto or other others otherwise our ours ourselves out over own per perhaps please put rather really\nsame see seem seemed seems several shall she should since so some something sometimes still such than that the\ntheir theirs them themselves then there therefore these they this those though through thus to together too\ntoward towards under until up upon us use used uses using very via was we well were what whatever when whenever\nwhere whereas wherever whether which while who whoever whom whose why will with within without would yet you your\nyours yourself yourselves\n`\n .split(/\\s+/)\n .filter(Boolean);\n"]}
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@x12i/api-hub-text",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Term and entity helpers for the x12i API hub",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/x12i/apis-hub.git",
24
+ "directory": "packages/text"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/x12i/apis-hub/issues"
28
+ },
29
+ "homepage": "https://github.com/x12i/apis-hub",
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "@x12i/distinctive-terms": "1.1.0",
35
+ "@x12i/text-entities": "1.0.0"
36
+ }
37
+ }