@jbrowse/text-indexing-core 5.0.0-beta.1 → 5.0.0-beta.10

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/esm/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { decodeURIComponentNoThrow, defaultAttributesToIndex, defaultFeatureTypesToExclude, indexableAdapters, isSupportedIndexingAdapter, } from './util.ts';
2
- export type { Gff3IndexerOptions, Gff3Adapter, Gff3TabixAdapter, GtfAdapter, IndexableFormat, IndexerOptions, LocalPathLocation, Track, UriLocation, VcfAdapter, VcfTabixAdapter, } from './util.ts';
3
- export { createReadlineInterface, generateMeta, getLocalOrRemoteStream, guessAdapterFromFileName, isURL, makeLocation, parseAttributes, } from './types/common.ts';
1
+ export { decodeURIComponentNoThrow, defaultAttributesToIndex, defaultFeatureTypesToExclude, defaultIndexingPolicy, indexableAdapters, isSupportedIndexingAdapter, trackIndexingPolicy, } from './util.ts';
2
+ export type { Gff3IndexerOptions, Gff3Adapter, Gff3TabixAdapter, GtfAdapter, IndexableFormat, IndexerOptions, IndexingPolicy, LocalPathLocation, Track, UriLocation, VcfAdapter, VcfTabixAdapter, } from './util.ts';
3
+ export { createReadlineInterface, getLocalOrRemoteStream, guessAdapterFromFileName, isURL, makeLocation, parseAttributes, } from './types/common.ts';
4
4
  export { TRIX_DIR, sanitizeForFilename, trixFileNames, trixFilePaths, trixFileUris, } from './trixPaths.ts';
5
5
  export { indexGff3 } from './types/gff3Adapter.ts';
6
6
  export { indexGtf } from './types/gtfAdapter.ts';
7
7
  export { indexVcf } from './types/vcfAdapter.ts';
8
- export { indexFiles } from './indexFiles.ts';
9
8
  export type { TrackIndexProgress } from './indexFiles.ts';
9
+ export { writeTrixIndex } from './writeTrixIndex.ts';
package/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
- export { decodeURIComponentNoThrow, defaultAttributesToIndex, defaultFeatureTypesToExclude, indexableAdapters, isSupportedIndexingAdapter, } from './util.js';
2
- export { createReadlineInterface, generateMeta, getLocalOrRemoteStream, guessAdapterFromFileName, isURL, makeLocation, parseAttributes, } from './types/common.js';
1
+ export { decodeURIComponentNoThrow, defaultAttributesToIndex, defaultFeatureTypesToExclude, defaultIndexingPolicy, indexableAdapters, isSupportedIndexingAdapter, trackIndexingPolicy, } from './util.js';
2
+ export { createReadlineInterface, getLocalOrRemoteStream, guessAdapterFromFileName, isURL, makeLocation, parseAttributes, } from './types/common.js';
3
3
  export { TRIX_DIR, sanitizeForFilename, trixFileNames, trixFilePaths, trixFileUris, } from './trixPaths.js';
4
4
  export { indexGff3 } from './types/gff3Adapter.js';
5
5
  export { indexGtf } from './types/gtfAdapter.js';
6
6
  export { indexVcf } from './types/vcfAdapter.js';
7
- export { indexFiles } from './indexFiles.js';
7
+ export { writeTrixIndex } from './writeTrixIndex.js';
@@ -1,15 +1,13 @@
1
- import type { Track } from './util.ts';
1
+ import type { IndexingPolicy, Track } from './util.ts';
2
2
  export interface TrackIndexProgress {
3
3
  onStart?: (totalBytes: number) => void;
4
4
  onUpdate?: (processedBytes: number) => void;
5
5
  onDone?: () => void;
6
6
  }
7
- export declare function indexFiles({ tracks, attributesToIndex, outDir, featureTypesToExclude, featureTypesToInclude, makeProgress, checkAbort, }: {
7
+ export declare function indexFiles({ tracks, policy, outDir, makeProgress, checkAbort, }: {
8
8
  tracks: Track[];
9
- attributesToIndex: string[];
9
+ policy: IndexingPolicy;
10
10
  outDir: string;
11
- featureTypesToExclude: string[];
12
- featureTypesToInclude?: string[];
13
11
  makeProgress?: (trackId: string) => TrackIndexProgress;
14
12
  checkAbort?: () => void;
15
13
  }): AsyncGenerator<string, void, unknown>;
package/esm/indexFiles.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { indexGff3 } from './types/gff3Adapter.js';
2
2
  import { indexGtf } from './types/gtfAdapter.js';
3
3
  import { indexVcf } from './types/vcfAdapter.js';
4
- import { indexableAdapters } from './util.js';
4
+ import { indexableAdapters, trackIndexingPolicy } from './util.js';
5
5
  function getIndexingLocation(track, locationKey) {
6
6
  const { adapter } = track;
7
7
  // adapter[locationKey] is the canonical location object, e.g. gffGzLocation;
@@ -11,23 +11,21 @@ function getIndexingLocation(track, locationKey) {
11
11
  return loc?.locationType === 'LocalPathLocation' ? loc.localPath : loc?.uri;
12
12
  }
13
13
  const noop = () => { };
14
- // shared generator that streams index records for a set of tracks. Dispatches
15
- // to the gff3/vcf indexers based on adapter type; per-track attribute and
16
- // exclude/include overrides from textSearching take precedence over the
17
- // defaults.
18
- export async function* indexFiles({ tracks, attributesToIndex, outDir, featureTypesToExclude, featureTypesToInclude, makeProgress, checkAbort, }) {
14
+ // Streams index records for a set of tracks, each read under its own policy
15
+ export async function* indexFiles({ tracks, policy, outDir, makeProgress, checkAbort, }) {
19
16
  for (const track of tracks) {
20
17
  checkAbort?.();
21
- const { adapter, textSearching, trackId } = track;
18
+ const { adapter, trackId } = track;
22
19
  const indexable = indexableAdapters[adapter?.type ?? ''];
23
20
  const inLocation = indexable
24
21
  ? getIndexingLocation(track, indexable.locationKey)
25
22
  : undefined;
26
23
  if (indexable && inLocation) {
27
24
  const progress = makeProgress?.(trackId);
25
+ const { attributes, exclude, include } = trackIndexingPolicy(track, policy);
28
26
  const common = {
29
27
  config: track,
30
- attributesToIndex: textSearching?.indexingAttributes ?? attributesToIndex,
28
+ attributesToIndex: attributes,
31
29
  inLocation,
32
30
  outDir,
33
31
  onStart: progress?.onStart ?? noop,
@@ -37,10 +35,8 @@ export async function* indexFiles({ tracks, attributesToIndex, outDir, featureTy
37
35
  if (indexable.format === 'gff3') {
38
36
  yield* indexGff3({
39
37
  ...common,
40
- featureTypesToExclude: textSearching?.indexingFeatureTypesToExclude ??
41
- featureTypesToExclude,
42
- featureTypesToInclude: textSearching?.indexingFeatureTypesToInclude ??
43
- featureTypesToInclude,
38
+ featureTypesToExclude: exclude,
39
+ featureTypesToInclude: include,
44
40
  });
45
41
  }
46
42
  else if (indexable.format === 'gtf') {
@@ -5,11 +5,8 @@ export declare function sanitizeForFilename(name: string): string;
5
5
  * The three files an index called `name` writes, sanitized once.
6
6
  *
7
7
  * Everything that writes a trix artifact or points a config at one derives its
8
- * paths from here. They used to sanitize independently and one of them didn't:
9
- * the CLI wrote `.ix`/`.ixx` under the raw name while `generateMeta` and
10
- * `createTrixAdapter` sanitized, so a trackId holding a `/` aimed the write at
11
- * a `trix/` subdirectory that does not exist, and the rest of the
12
- * Windows-invalid set wrote a file no search would look for.
8
+ * paths from here, so a trackId holding a `/` or another Windows-invalid
9
+ * character names the same file for the writer as for the search reading it.
13
10
  */
14
11
  export declare function trixFileNames(name: string): {
15
12
  ix: string;
package/esm/trixPaths.js CHANGED
@@ -17,11 +17,8 @@ export function sanitizeForFilename(name) {
17
17
  * The three files an index called `name` writes, sanitized once.
18
18
  *
19
19
  * Everything that writes a trix artifact or points a config at one derives its
20
- * paths from here. They used to sanitize independently and one of them didn't:
21
- * the CLI wrote `.ix`/`.ixx` under the raw name while `generateMeta` and
22
- * `createTrixAdapter` sanitized, so a trackId holding a `/` aimed the write at
23
- * a `trix/` subdirectory that does not exist, and the rest of the
24
- * Windows-invalid set wrote a file no search would look for.
20
+ * paths from here, so a trackId holding a `/` or another Windows-invalid
21
+ * character names the same file for the writer as for the search reading it.
25
22
  */
26
23
  export function trixFileNames(name) {
27
24
  const safeName = sanitizeForFilename(name);
@@ -12,12 +12,3 @@ export declare function createReadlineInterface(stream: Readable, inLocation: st
12
12
  export declare function parseAttributes(infoString: string, decodeFunc: (s: string) => string): Record<string, string | undefined>;
13
13
  export declare function makeLocation(location: string, protocol: string): UriLocation | LocalPathLocation;
14
14
  export declare function guessAdapterFromFileName(filePath: string): Track;
15
- export declare function generateMeta({ configs, attributesToIndex, outDir, name, featureTypesToExclude, featureTypesToInclude, assemblyNames, }: {
16
- configs: Track[];
17
- attributesToIndex: string[];
18
- outDir: string;
19
- name: string;
20
- featureTypesToExclude: string[];
21
- featureTypesToInclude?: string[];
22
- assemblyNames: string[];
23
- }): void;
@@ -5,7 +5,6 @@ import readline from 'node:readline';
5
5
  import { Readable } from 'node:stream';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { createGunzip } from 'node:zlib';
8
- import { trixFilePaths } from '../trixPaths.js';
9
8
  import { indexableAdapters } from '../util.js';
10
9
  export function isURL(fileName) {
11
10
  try {
@@ -175,21 +174,3 @@ export function guessAdapterFromFileName(filePath) {
175
174
  throw new Error(`Unsupported file type ${filePath}`);
176
175
  }
177
176
  }
178
- export function generateMeta({ configs, attributesToIndex, outDir, name, featureTypesToExclude, featureTypesToInclude, assemblyNames, }) {
179
- fs.writeFileSync(trixFilePaths(outDir, name).meta, JSON.stringify({
180
- dateCreated: new Date().toISOString(),
181
- tracks: configs.map(config => {
182
- const { trackId, textSearching, adapter } = config;
183
- return {
184
- trackId,
185
- attributesIndexed: textSearching?.indexingAttributes ?? attributesToIndex,
186
- excludedTypes: textSearching?.indexingFeatureTypesToExclude ??
187
- featureTypesToExclude,
188
- includedTypes: textSearching?.indexingFeatureTypesToInclude ??
189
- featureTypesToInclude,
190
- adapterConf: adapter,
191
- };
192
- }),
193
- assemblyNames,
194
- }, null, 2));
195
- }
@@ -1,5 +1,19 @@
1
- import { decodeURIComponentNoThrow } from '../util.js';
1
+ import { decodeURIComponentNoThrow, trixLine } from '../util.js';
2
2
  import { createReadlineInterface, getLocalOrRemoteStream, parseAttributes, } from './common.js';
3
+ // GENCODE writes no `Name`: a gene row carries `gene_name`, a transcript row
4
+ // `transcript_name`, and every row repeats its parents' names too. So `Name`
5
+ // falls back to the name whose id is this row's own ID, which leaves a UTR or
6
+ // codon row unnamed rather than answering a search for its transcript.
7
+ function ownName(attrs) {
8
+ const id = attrs.ID;
9
+ return id === undefined
10
+ ? undefined
11
+ : id === attrs.transcript_id
12
+ ? attrs.transcript_name
13
+ : id === attrs.gene_id
14
+ ? attrs.gene_name
15
+ : undefined;
16
+ }
3
17
  /**
4
18
  * Stream one GFF3 into trix records, one line per indexed feature.
5
19
  *
@@ -35,7 +49,6 @@ export async function* indexGff3({ config, attributesToIndex, inLocation, outDir
35
49
  const includeSet = featureTypesToInclude?.length
36
50
  ? new Set(featureTypesToInclude)
37
51
  : undefined;
38
- const encodedTrackId = encodeURIComponent(trackId);
39
52
  for await (const line of rl) {
40
53
  checkAbort?.();
41
54
  if (!line.trim() || line.startsWith('#')) {
@@ -59,14 +72,11 @@ export async function* indexGff3({ config, attributesToIndex, inLocation, outDir
59
72
  (includeSet === undefined || includeSet.has(type))) {
60
73
  const col9attrs = parseAttributes(col9, decodeURIComponentNoThrow);
61
74
  const attrs = attributesToIndex
62
- .map(attr => col9attrs[attr])
75
+ .map(attr => col9attrs[attr] ??
76
+ (attr === 'Name' ? ownName(col9attrs) : undefined))
63
77
  .filter((f) => !!f);
64
78
  if (attrs.length > 0) {
65
- const locStr = `${seq_id}:${start}..${end}`;
66
- const encodedAttrs = attrs.map(a => `"${encodeURIComponent(a)}"`);
67
- const record = `["${encodeURIComponent(locStr)}"|"${encodedTrackId}"|${encodedAttrs.join('|')}]`;
68
- const uniqueAttrs = [...new Set(attrs)];
69
- yield `${record} ${uniqueAttrs.join(' ')}\n`;
79
+ yield trixLine(`${seq_id}:${start}..${end}`, trackId, attrs);
70
80
  }
71
81
  }
72
82
  }
@@ -1,3 +1,4 @@
1
+ import { trixLine } from '../util.js';
1
2
  import { createReadlineInterface, getLocalOrRemoteStream } from './common.js';
2
3
  // GFF3-style attribute names have no GTF equivalent, so the shared defaults
3
4
  // (`Name`, `ID`, `symbol`) would index nothing. Each requested name also pulls
@@ -82,7 +83,6 @@ export async function* indexGtf({ config, attributesToIndex, inLocation, outDir,
82
83
  onUpdate,
83
84
  });
84
85
  const rl = createReadlineInterface(stream, inLocation);
85
- const encodedTrackId = encodeURIComponent(trackId);
86
86
  const attributeNames = expandAttributeNames(attributesToIndex);
87
87
  const groups = new Map();
88
88
  function accumulate(key, refName, start, end, values) {
@@ -141,9 +141,6 @@ export async function* indexGtf({ config, attributesToIndex, inLocation, outDir,
141
141
  }
142
142
  }
143
143
  for (const { refName, start, end, attrs } of groups.values()) {
144
- const locStr = `${refName}:${start}..${end}`;
145
- const encodedAttrs = [...attrs].map(a => `"${encodeURIComponent(a)}"`);
146
- const record = `["${encodeURIComponent(locStr)}"|"${encodedTrackId}"|${encodedAttrs.join('|')}]`;
147
- yield `${record} ${[...attrs].join(' ')}\n`;
144
+ yield trixLine(`${refName}:${start}..${end}`, trackId, [...attrs]);
148
145
  }
149
146
  }
@@ -1,4 +1,4 @@
1
- import { decodeURIComponentNoThrow } from '../util.js';
1
+ import { decodeURIComponentNoThrow, trixLine } from '../util.js';
2
2
  import { createReadlineInterface, getLocalOrRemoteStream, parseAttributes, } from './common.js';
3
3
  export async function* indexVcf({ config, attributesToIndex, inLocation, outDir, onStart, onUpdate, checkAbort, }) {
4
4
  const { trackId } = config;
@@ -9,7 +9,6 @@ export async function* indexVcf({ config, attributesToIndex, inLocation, outDir,
9
9
  onUpdate,
10
10
  });
11
11
  const rl = createReadlineInterface(stream, inLocation);
12
- const encodedTrackId = encodeURIComponent(trackId);
13
12
  for await (const line of rl) {
14
13
  checkAbort?.();
15
14
  if (!line.trim() || line.startsWith('#')) {
@@ -30,7 +29,6 @@ export async function* indexVcf({ config, attributesToIndex, inLocation, outDir,
30
29
  const endNum = Number(fields.END);
31
30
  const end = fields.END && Number.isFinite(endNum) ? endNum : Number(pos) + 1;
32
31
  const locStr = `${ref}:${pos}..${end}`;
33
- const encodedLocStr = encodeURIComponent(locStr);
34
32
  const infoAttrs = attributesToIndex
35
33
  .map(attr => fields[attr])
36
34
  .filter((f) => !!f);
@@ -45,11 +43,7 @@ export async function* indexVcf({ config, attributesToIndex, inLocation, outDir,
45
43
  : [infoAttrs];
46
44
  for (const attrs of attrGroups) {
47
45
  if (attrs.length > 0) {
48
- // attributes go in the record so the adapter can display them, and in
49
- // the trailing word list so trix indexes them as searchable terms
50
- const encodedAttrs = attrs.map(a => `"${encodeURIComponent(a)}"`);
51
- const record = `["${encodedLocStr}"|"${encodedTrackId}"|${encodedAttrs.join('|')}]`;
52
- yield `${record} ${[...new Set(attrs)].join(' ')}\n`;
46
+ yield trixLine(locStr, trackId, attrs);
53
47
  }
54
48
  }
55
49
  }
package/esm/util.d.ts CHANGED
@@ -51,6 +51,20 @@ export interface Track {
51
51
  }
52
52
  export declare const defaultAttributesToIndex: string[];
53
53
  export declare const defaultFeatureTypesToExclude: string[];
54
+ /** What an index reads out of a track's features. */
55
+ export interface IndexingPolicy {
56
+ attributes: string[];
57
+ /** feature types to skip (GFF3 only) */
58
+ exclude: string[];
59
+ /**
60
+ * the only feature types to index, narrowed by `exclude` (GFF3 only). Absent
61
+ * or empty means no allow list, never "index nothing". See indexGff3
62
+ */
63
+ include?: string[];
64
+ }
65
+ export declare const defaultIndexingPolicy: IndexingPolicy;
66
+ /** The policy `track` is indexed under: its own settings, else the run's. */
67
+ export declare function trackIndexingPolicy({ textSearching }: Track, run: IndexingPolicy): IndexingPolicy;
54
68
  export type IndexableFormat = 'gff3' | 'gtf' | 'vcf';
55
69
  export declare const indexableAdapters: Record<string, {
56
70
  locationKey: string;
@@ -72,5 +86,12 @@ export interface Gff3IndexerOptions extends IndexerOptions {
72
86
  featureTypesToExclude: string[];
73
87
  featureTypesToInclude?: string[];
74
88
  }
89
+ /**
90
+ * One line of ixIxx input: the record a search hit hands back —
91
+ * `["loc"|"trackId"|"attr"|...]`, fields URI-encoded so neither a comma nor a
92
+ * space can split it — then the words the hit is found by.
93
+ * TrixTextSearchAdapter reads the record back.
94
+ */
95
+ export declare function trixLine(loc: string, trackId: string, attrs: string[]): string;
75
96
  export declare function decodeURIComponentNoThrow(uri: string): string;
76
97
  export {};
package/esm/util.js CHANGED
@@ -1,5 +1,17 @@
1
1
  export const defaultAttributesToIndex = ['Name', 'ID', 'symbol'];
2
2
  export const defaultFeatureTypesToExclude = ['exon', 'CDS'];
3
+ export const defaultIndexingPolicy = {
4
+ attributes: defaultAttributesToIndex,
5
+ exclude: defaultFeatureTypesToExclude,
6
+ };
7
+ /** The policy `track` is indexed under: its own settings, else the run's. */
8
+ export function trackIndexingPolicy({ textSearching }, run) {
9
+ return {
10
+ attributes: textSearching?.indexingAttributes ?? run.attributes,
11
+ exclude: textSearching?.indexingFeatureTypesToExclude ?? run.exclude,
12
+ include: textSearching?.indexingFeatureTypesToInclude ?? run.include,
13
+ };
14
+ }
3
15
  // single source of truth for what `jbrowse text-index` can index: which adapter
4
16
  // types, where each keeps its file location, and which line parser to use.
5
17
  // Everything else (the adapter dispatch, the CLI's supported() gate, the
@@ -15,6 +27,16 @@ export const indexableAdapters = {
15
27
  export function isSupportedIndexingAdapter(type = '') {
16
28
  return type in indexableAdapters;
17
29
  }
30
+ /**
31
+ * One line of ixIxx input: the record a search hit hands back —
32
+ * `["loc"|"trackId"|"attr"|...]`, fields URI-encoded so neither a comma nor a
33
+ * space can split it — then the words the hit is found by.
34
+ * TrixTextSearchAdapter reads the record back.
35
+ */
36
+ export function trixLine(loc, trackId, attrs) {
37
+ const fields = [loc, trackId, ...attrs].map(f => `"${encodeURIComponent(f)}"`);
38
+ return `[${fields.join('|')}] ${[...new Set(attrs)].join(' ')}\n`;
39
+ }
18
40
  export function decodeURIComponentNoThrow(uri) {
19
41
  try {
20
42
  return decodeURIComponent(uri);
@@ -0,0 +1,19 @@
1
+ import type { TrackIndexProgress } from './indexFiles.ts';
2
+ import type { IndexingPolicy, Track } from './util.ts';
3
+ /**
4
+ * Index `tracks` into `<outDir>/trix/<name>.ix`, `.ixx` and `_meta.json`.
5
+ *
6
+ * `onSort` fires once every file has been read, when the sort that writes the
7
+ * index begins — minutes on a large index, with no byte count to report.
8
+ */
9
+ export declare function writeTrixIndex({ tracks, outDir, name, policy, assemblyNames, prefixSize, makeProgress, checkAbort, onSort, }: {
10
+ tracks: Track[];
11
+ outDir: string;
12
+ name: string;
13
+ policy: IndexingPolicy;
14
+ assemblyNames: string[];
15
+ prefixSize?: number;
16
+ makeProgress?: (trackId: string) => TrackIndexProgress;
17
+ checkAbort?: () => void;
18
+ onSort?: () => void;
19
+ }): Promise<void>;
@@ -0,0 +1,37 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { Readable } from 'node:stream';
4
+ import { ixIxxStream } from 'ixixx';
5
+ import { indexFiles } from './indexFiles.js';
6
+ import { TRIX_DIR, trixFilePaths } from './trixPaths.js';
7
+ import { trackIndexingPolicy } from './util.js';
8
+ /**
9
+ * Index `tracks` into `<outDir>/trix/<name>.ix`, `.ixx` and `_meta.json`.
10
+ *
11
+ * `onSort` fires once every file has been read, when the sort that writes the
12
+ * index begins — minutes on a large index, with no byte count to report.
13
+ */
14
+ export async function writeTrixIndex({ tracks, outDir, name, policy, assemblyNames, prefixSize, makeProgress, checkAbort, onSort, }) {
15
+ fs.mkdirSync(path.join(outDir, TRIX_DIR), { recursive: true });
16
+ const records = Readable.from(indexFiles({ tracks, policy, outDir, makeProgress, checkAbort }));
17
+ if (onSort) {
18
+ records.on('end', onSort);
19
+ }
20
+ const paths = trixFilePaths(outDir, name);
21
+ await ixIxxStream(records, paths.ix, paths.ixx, prefixSize);
22
+ checkAbort?.();
23
+ fs.writeFileSync(paths.meta, JSON.stringify({
24
+ dateCreated: new Date().toISOString(),
25
+ tracks: tracks.map(track => {
26
+ const { attributes, exclude, include } = trackIndexingPolicy(track, policy);
27
+ return {
28
+ trackId: track.trackId,
29
+ attributesIndexed: attributes,
30
+ excludedTypes: exclude,
31
+ includedTypes: include,
32
+ adapterConf: track.adapter,
33
+ };
34
+ }),
35
+ assemblyNames,
36
+ }, null, 2));
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/text-indexing-core",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0-beta.10",
4
4
  "type": "module",
5
5
  "description": "JBrowse 2 core text indexing routines for parsing GFF3 and VCF files",
6
6
  "keywords": [
@@ -27,7 +27,9 @@
27
27
  "files": [
28
28
  "esm"
29
29
  ],
30
- "dependencies": {},
30
+ "dependencies": {
31
+ "ixixx": "^3.0.13"
32
+ },
31
33
  "publishConfig": {
32
34
  "access": "public"
33
35
  },