@osm-editor-kit/osm-way-chain 0.1.0-alpha.0 → 0.1.0-alpha.2

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,12 +1,16 @@
1
1
  # `@osm-editor-kit/osm-way-chain`
2
2
 
3
- **Status:** First npm **alpha** (`0.1.0-alpha.0`). Publish with `bun run packages:release -- --publish-only` after `npm login`.
3
+ > [!NOTE]
4
+ > This package is an **alpha** release and still under active development. APIs may change; install with the npm `alpha` dist-tag.
4
5
 
5
6
  ## What it does
6
7
 
7
- Bidirectional OSM way-chain traversal for lane editors: walk connected highway ways from a center segment, score neighbor candidates (same kind, name, ref), surface ambiguous junctions, and orient geometry plus directional tags when a way is reversed.
8
-
9
- Includes `mirrorTags` for flipping digitization direction (left/right, forward/backward, oneway, lane pipes, placement), road-like highway predicates (`public` vs `inclusive` inclusion style, Overpass selectors), and a session adapter over in-memory `ParsedOsmData` from `@osm-editor-kit/osm-data`.
8
+ - Walk connected highway ways left and right from a center segment
9
+ - Rank neighbor candidates (same road kind, name, ref)
10
+ - Flag junctions where more than one neighbor is equally good
11
+ - Flip geometry and directional tags when a way is digitized the “wrong” way
12
+ - Compile and match **app-owned** way-selection policies (Overpass + in-memory tags)
13
+ - Adapter for in-memory session data from `@osm-editor-kit/osm-data`
10
14
 
11
15
  ## Usage
12
16
 
@@ -14,11 +18,21 @@ Includes `mirrorTags` for flipping digitization direction (left/right, forward/b
14
18
  import type { ParsedOsmData } from '@osm-editor-kit/osm-data'
15
19
  import {
16
20
  buildChain,
21
+ buildWaysOverpassQuery,
17
22
  createSessionGraphAdapter,
18
- isEditableRoadLikeSegment,
19
- overpassRoadLikeSelector,
23
+ matchesOsmWaySelectionSegment,
24
+ tag,
25
+ type OsmWaySelectionPolicy,
20
26
  } from '@osm-editor-kit/osm-way-chain'
21
27
 
28
+ /** App-owned contract — which ways to download / chain. Not package defaults. */
29
+ const wayPolicy: OsmWaySelectionPolicy = {
30
+ include: [
31
+ { all: [tag.oneOf('highway', ['residential', 'cycleway', 'path', 'footway', 'steps'])] },
32
+ ],
33
+ globalAll: [tag.neq('access', 'private'), tag.neq('access', 'no')],
34
+ }
35
+
22
36
  const graph: ParsedOsmData = /* loaded viewport data */
23
37
  const adapter = createSessionGraphAdapter(graph)
24
38
 
@@ -26,14 +40,14 @@ const { chain, pendingJunctions } = await buildChain(adapter, {
26
40
  centerWayId: 123456,
27
41
  maxPerSide: 5,
28
42
  candidateFilter: (candidate, from) =>
29
- isEditableRoadLikeSegment(candidate) && isEditableRoadLikeSegment(from),
43
+ matchesOsmWaySelectionSegment(candidate, wayPolicy) &&
44
+ matchesOsmWaySelectionSegment(from, wayPolicy),
30
45
  })
31
46
 
32
- // chain.segments: predecessors, center, successors (ordered)
33
- // pendingJunctions: nodes where multiple equally good neighbors need a user pick
34
-
35
- // Overpass fragment for lane-mode downloads:
36
- overpassRoadLikeSelector('public')
47
+ // Overpass download for the same policy:
48
+ buildWaysOverpassQuery(wayPolicy, '52.5,13.4,52.51,13.41')
37
49
  ```
38
50
 
39
51
  Implement `OsmDataAdapter` (`getWay`, `getWaysForNode`) for other data sources. Use `extendChainAtJunction` and `recenterChain` to continue after resolving a junction choice.
52
+
53
+ Legacy `overpassRoadLikeSelector` / `HighwayInclusionStyle` helpers remain as deprecated shims for street-space-editor.
@@ -1,30 +1,36 @@
1
1
  import type { OsmTags } from '@osm-editor-kit/osm-data';
2
2
  import type { Segment } from './domain/types';
3
+ import { type OsmWaySelectionPolicy } from './way-selection-policy';
3
4
  /**
4
- * Base highway values used for lane-mode street chain traversal.
5
- * Link variants (`*_link`) and `highway=busway` are handled separately.
6
- */
7
- export declare const ROAD_LIKE_HIGHWAY_BASE_REGEX: RegExp;
8
- /**
9
- * How broadly the editor includes low-priority access roads in the map.
10
- *
11
- * - `public` (default): skip private access, driveways, emergency access, and parking aisles
12
- * - `inclusive`: include those ways (previous editor behavior)
5
+ * @deprecated App-owned concept. Prefer {@link OsmWaySelectionPolicy} defined in the app.
6
+ * Kept temporarily for street-space-editor call sites.
13
7
  */
14
8
  export type HighwayInclusionStyle = 'public' | 'inclusive';
9
+ /** @deprecated Prefer app-owned policies. */
15
10
  export declare const DEFAULT_HIGHWAY_INCLUSION_STYLE: HighwayInclusionStyle;
11
+ /**
12
+ * @deprecated Prefer app-owned {@link OsmWaySelectionPolicy}.
13
+ * Legacy lane-mode car/road selection (incl. `*_link` and `busway`).
14
+ */
15
+ export declare function createLegacyStreetRoadWayPolicy(style?: HighwayInclusionStyle): OsmWaySelectionPolicy;
16
+ /** @deprecated Prefer matching against an app policy. */
17
+ export declare const ROAD_LIKE_HIGHWAY_BASE_REGEX: RegExp;
18
+ /** @deprecated Prefer policy `globalAll`. */
16
19
  export declare const OVERPASS_PUBLIC_ROAD_FILTERS = "[service!=emergency_access][service!=parking_aisle][service!=driveway][access!=private]";
17
- /** Overpass selector for road-like ways under the given inclusion style. */
20
+ /**
21
+ * @deprecated Prefer {@link compileOverpassWaySelector} with an app policy.
22
+ * Returns the legacy fragment expected by `way[${tag}]` callers (no leading `[`).
23
+ */
18
24
  export declare function overpassRoadLikeSelector(style?: HighwayInclusionStyle): string;
19
- /** Ways that are usually noise in street-space editing (private driveways, etc.). */
25
+ /** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
20
26
  export declare function isClutterAccessWay(tags: OsmTags): boolean;
21
- /** Whether a way should be shown/edited under the chosen inclusion style. */
27
+ /** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
22
28
  export declare function matchesHighwayInclusionStyle(tags: OsmTags, style?: HighwayInclusionStyle): boolean;
23
- /** Whether OSM tags describe a road-like highway suitable for lane-mode chaining. */
29
+ /** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
24
30
  export declare function isRoadLikeHighway(tags: OsmTags): boolean;
25
- /** Road-like highways that pass the active inclusion style. */
31
+ /** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
26
32
  export declare function isEditableRoadLikeHighway(tags: OsmTags, style?: HighwayInclusionStyle): boolean;
27
- /** Segment-level predicate for `buildChain` / `filterNeighborCandidates`. */
33
+ /** @deprecated Prefer app policy + {@link matchesOsmWaySelectionSegment}. */
28
34
  export declare function isRoadLikeSegment(segment: Segment): boolean;
29
- /** Segment-level predicate that also applies the editor inclusion style. */
35
+ /** @deprecated Prefer app policy + {@link matchesOsmWaySelectionSegment}. */
30
36
  export declare function isEditableRoadLikeSegment(segment: Segment, style?: HighwayInclusionStyle): boolean;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export type { JunctionChoice, NeighborDirection, OsmTags, Segment, SegmentChain, } from './domain/types';
2
- export { DEFAULT_HIGHWAY_INCLUSION_STYLE, isClutterAccessWay, isEditableRoadLikeHighway, isEditableRoadLikeSegment, isRoadLikeHighway, isRoadLikeSegment, matchesHighwayInclusionStyle, overpassRoadLikeSelector, ROAD_LIKE_HIGHWAY_BASE_REGEX, OVERPASS_PUBLIC_ROAD_FILTERS, type HighwayInclusionStyle, } from './highway-filter';
2
+ export { createLegacyStreetRoadWayPolicy, DEFAULT_HIGHWAY_INCLUSION_STYLE, isClutterAccessWay, isEditableRoadLikeHighway, isEditableRoadLikeSegment, isRoadLikeHighway, isRoadLikeSegment, matchesHighwayInclusionStyle, overpassRoadLikeSelector, ROAD_LIKE_HIGHWAY_BASE_REGEX, OVERPASS_PUBLIC_ROAD_FILTERS, type HighwayInclusionStyle, } from './highway-filter';
3
+ export { buildWaysOverpassQuery, compileOverpassWaySelector, compileOverpassWaySelectors, matchesOsmWaySelection, matchesOsmWaySelectionSegment, tag, type OsmTagPredicate, type OsmWayFilterClause, type OsmWaySelectionPolicy, } from './way-selection-policy';
3
4
  export type { OsmDataAdapter } from './ports/OsmDataAdapter';
4
5
  export { createSessionGraphAdapter, osmWayToSegment } from './session-graph-adapter';
5
6
  export { buildChain, extendChainAtJunction, getOtherEndpointNodeId, getSharedNodeBetween, recenterChain, type BuildChainOptions, type BuildChainResult, } from './traversal/buildChain';
package/dist/index.js CHANGED
@@ -1,31 +1,147 @@
1
+ // src/way-selection-policy.ts
2
+ var tag = {
3
+ eq: (key, value) => ({ key, op: "eq", value }),
4
+ neq: (key, value) => ({ key, op: "neq", value }),
5
+ oneOf: (key, values) => ({
6
+ key,
7
+ op: "in",
8
+ values
9
+ }),
10
+ noneOf: (key, values) => ({
11
+ key,
12
+ op: "nin",
13
+ values
14
+ }),
15
+ regex: (key, pattern) => ({ key, op: "regex", pattern }),
16
+ nregex: (key, pattern) => ({ key, op: "nregex", pattern }),
17
+ present: (key) => ({ key, op: "present" }),
18
+ absent: (key) => ({ key, op: "absent" })
19
+ };
20
+ function escapeRegexAlternation(values) {
21
+ return values.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
22
+ }
23
+ function quoteOverpassValue(value) {
24
+ if (/^[A-Za-z0-9_:-]+$/.test(value)) return value;
25
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
26
+ }
27
+ function escapeOverpassRegex(pattern) {
28
+ return pattern.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
29
+ }
30
+ function compileTagPredicate(predicate) {
31
+ switch (predicate.op) {
32
+ case "eq":
33
+ return `[${predicate.key}=${quoteOverpassValue(predicate.value)}]`;
34
+ case "neq":
35
+ return `[${predicate.key}!=${quoteOverpassValue(predicate.value)}]`;
36
+ case "in":
37
+ return `[${predicate.key}~"^(${escapeRegexAlternation(predicate.values)})$"]`;
38
+ case "nin":
39
+ return `[${predicate.key}!~"^(${escapeRegexAlternation(predicate.values)})$"]`;
40
+ case "regex":
41
+ return `[${predicate.key}~"${escapeOverpassRegex(predicate.pattern)}"]`;
42
+ case "nregex":
43
+ return `[${predicate.key}!~"${escapeOverpassRegex(predicate.pattern)}"]`;
44
+ case "present":
45
+ return `[${predicate.key}]`;
46
+ case "absent":
47
+ return `[!${predicate.key}]`;
48
+ }
49
+ }
50
+ function clausePredicates(policy, clause) {
51
+ return [...clause.all, ...policy.globalAll ?? []];
52
+ }
53
+ function compileOverpassWaySelectors(policy) {
54
+ if (policy.include.length === 0) {
55
+ throw new Error("OsmWaySelectionPolicy.include must contain at least one clause");
56
+ }
57
+ return policy.include.map(
58
+ (clause) => clausePredicates(policy, clause).map(compileTagPredicate).join("")
59
+ );
60
+ }
61
+ function compileOverpassWaySelector(policy) {
62
+ const selectors = compileOverpassWaySelectors(policy);
63
+ if (selectors.length !== 1) {
64
+ throw new Error(
65
+ `compileOverpassWaySelector expects exactly one include clause, got ${selectors.length}`
66
+ );
67
+ }
68
+ return selectors[0];
69
+ }
70
+ function matchesTagPredicate(tags, predicate) {
71
+ const raw = tags[predicate.key];
72
+ switch (predicate.op) {
73
+ case "eq":
74
+ return raw === predicate.value;
75
+ case "neq":
76
+ return raw !== predicate.value;
77
+ case "in":
78
+ return raw != null && predicate.values.includes(raw);
79
+ case "nin":
80
+ return raw == null || !predicate.values.includes(raw);
81
+ case "regex":
82
+ return raw != null && new RegExp(predicate.pattern).test(raw);
83
+ case "nregex":
84
+ return raw == null || !new RegExp(predicate.pattern).test(raw);
85
+ case "present":
86
+ return raw != null && raw !== "";
87
+ case "absent":
88
+ return raw == null || raw === "";
89
+ }
90
+ }
91
+ function matchesClause(tags, predicates) {
92
+ return predicates.every((predicate) => matchesTagPredicate(tags, predicate));
93
+ }
94
+ function matchesOsmWaySelection(tags, policy) {
95
+ return policy.include.some((clause) => matchesClause(tags, clausePredicates(policy, clause)));
96
+ }
97
+ function matchesOsmWaySelectionSegment(segment, policy) {
98
+ return matchesOsmWaySelection(segment.tags, policy);
99
+ }
100
+ function buildWaysOverpassQuery(policy, bbox, options) {
101
+ const selectors = compileOverpassWaySelectors(policy);
102
+ const timeout = options?.timeoutSeconds ?? 60;
103
+ const out = options?.out ?? "meta";
104
+ const union = selectors.map((selector) => `way${selector}(${bbox});`).join("");
105
+ return `[out:xml][timeout:${timeout}];(${union});(._;>;);out ${out};`;
106
+ }
107
+
1
108
  // src/highway-filter.ts
2
- var ROAD_LIKE_HIGHWAY_BASE_REGEX = /^motorway|trunk|primary|secondary|tertiary|unclassified|residential|living_street|service/;
3
109
  var DEFAULT_HIGHWAY_INCLUSION_STYLE = "public";
4
- var CLUTTER_SERVICE_VALUES = /* @__PURE__ */ new Set(["driveway", "emergency_access", "parking_aisle"]);
110
+ function createLegacyStreetRoadWayPolicy(style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
111
+ const highway = tag.regex(
112
+ "highway",
113
+ "^(motorway|trunk|primary|secondary|tertiary|unclassified|residential|living_street|service)(_link)?$|^busway$"
114
+ );
115
+ if (style === "inclusive") {
116
+ return { include: [{ all: [highway] }] };
117
+ }
118
+ return {
119
+ include: [{ all: [highway] }],
120
+ globalAll: [
121
+ tag.neq("access", "private"),
122
+ tag.noneOf("service", ["emergency_access", "parking_aisle", "driveway"])
123
+ ]
124
+ };
125
+ }
126
+ var ROAD_LIKE_HIGHWAY_BASE_REGEX = /^motorway|trunk|primary|secondary|tertiary|unclassified|residential|living_street|service/;
5
127
  var OVERPASS_PUBLIC_ROAD_FILTERS = "[service!=emergency_access][service!=parking_aisle][service!=driveway][access!=private]";
6
128
  function overpassRoadLikeSelector(style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
7
- const base = 'highway~"^motorway|trunk|primary|secondary|tertiary|unclassified|residential|service|living_street"';
8
- if (style === "inclusive") return base;
9
- return base + OVERPASS_PUBLIC_ROAD_FILTERS;
129
+ return compileOverpassWaySelector(createLegacyStreetRoadWayPolicy(style)).slice(1);
10
130
  }
11
131
  function isClutterAccessWay(tags) {
12
132
  if (tags.access === "private") return true;
13
133
  const service = tags.service;
14
- return service != null && CLUTTER_SERVICE_VALUES.has(service);
134
+ return service === "driveway" || service === "emergency_access" || service === "parking_aisle";
15
135
  }
16
136
  function matchesHighwayInclusionStyle(tags, style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
17
137
  if (style === "inclusive") return true;
18
138
  return !isClutterAccessWay(tags);
19
139
  }
20
140
  function isRoadLikeHighway(tags) {
21
- const highway = tags.highway;
22
- if (!highway) return false;
23
- if (highway === "busway") return true;
24
- const base = highway.endsWith("_link") ? highway.slice(0, -"_link".length) : highway;
25
- return ROAD_LIKE_HIGHWAY_BASE_REGEX.test(base);
141
+ return matchesOsmWaySelection(tags, createLegacyStreetRoadWayPolicy("inclusive"));
26
142
  }
27
143
  function isEditableRoadLikeHighway(tags, style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
28
- return isRoadLikeHighway(tags) && matchesHighwayInclusionStyle(tags, style);
144
+ return matchesOsmWaySelection(tags, createLegacyStreetRoadWayPolicy(style));
29
145
  }
30
146
  function isRoadLikeSegment(segment) {
31
147
  return isRoadLikeHighway(segment.tags);
@@ -396,4 +512,4 @@ function getSharedNodeBetween(a, b) {
396
512
  return void 0;
397
513
  }
398
514
 
399
- export { DEFAULT_HIGHWAY_INCLUSION_STYLE, OVERPASS_PUBLIC_ROAD_FILTERS, ROAD_LIKE_HIGHWAY_BASE_REGEX, buildChain, createSessionGraphAdapter, extendChainAtJunction, filterNeighborCandidates, getEndpointNodeId, getKindKey, getOtherEndpointNodeId, getSharedNodeBetween, isClutterAccessWay, isEditableRoadLikeHighway, isEditableRoadLikeSegment, isReversedAtNode, isRoadLikeHighway, isRoadLikeSegment, matchesHighwayInclusionStyle, mirrorTags, normalizeTagsForDirection, orientNeighbor, osmWayToSegment, overpassRoadLikeSelector, pickBestNeighbor, recenterChain, sameKind, scoreNeighborCandidate, swapLeftRightKey };
515
+ export { DEFAULT_HIGHWAY_INCLUSION_STYLE, OVERPASS_PUBLIC_ROAD_FILTERS, ROAD_LIKE_HIGHWAY_BASE_REGEX, buildChain, buildWaysOverpassQuery, compileOverpassWaySelector, compileOverpassWaySelectors, createLegacyStreetRoadWayPolicy, createSessionGraphAdapter, extendChainAtJunction, filterNeighborCandidates, getEndpointNodeId, getKindKey, getOtherEndpointNodeId, getSharedNodeBetween, isClutterAccessWay, isEditableRoadLikeHighway, isEditableRoadLikeSegment, isReversedAtNode, isRoadLikeHighway, isRoadLikeSegment, matchesHighwayInclusionStyle, matchesOsmWaySelection, matchesOsmWaySelectionSegment, mirrorTags, normalizeTagsForDirection, orientNeighbor, osmWayToSegment, overpassRoadLikeSelector, pickBestNeighbor, recenterChain, sameKind, scoreNeighborCandidate, swapLeftRightKey, tag };
@@ -0,0 +1,64 @@
1
+ import type { OsmTags } from '@osm-editor-kit/osm-data';
2
+ import type { Segment } from './domain/types';
3
+ /**
4
+ * Atomic tag predicate. Compiles 1:1 to Overpass filter syntax and the same
5
+ * semantics when matching in-memory `OsmTags` (missing tag matches `neq` / `nin`
6
+ * / `absent`, same as Overpass).
7
+ */
8
+ export type OsmTagPredicate = {
9
+ readonly key: string;
10
+ readonly op: 'eq' | 'neq';
11
+ readonly value: string;
12
+ } | {
13
+ readonly key: string;
14
+ readonly op: 'in' | 'nin';
15
+ readonly values: readonly string[];
16
+ } | {
17
+ readonly key: string;
18
+ readonly op: 'regex' | 'nregex';
19
+ readonly pattern: string;
20
+ } | {
21
+ readonly key: string;
22
+ readonly op: 'present' | 'absent';
23
+ };
24
+ /** One AND-group of tag predicates — one `way[...]` branch in Overpass. */
25
+ export type OsmWayFilterClause = {
26
+ readonly all: readonly OsmTagPredicate[];
27
+ };
28
+ /**
29
+ * Declarative way-selection policy (app-owned contract).
30
+ *
31
+ * - `include`: OR — union of Overpass `way[...](bbox)` statements
32
+ * - `globalAll`: AND — appended to every include clause (e.g. access exclusions)
33
+ */
34
+ export type OsmWaySelectionPolicy = {
35
+ readonly include: readonly OsmWayFilterClause[];
36
+ readonly globalAll?: readonly OsmTagPredicate[];
37
+ };
38
+ /** Ergonomic constructors for {@link OsmTagPredicate}. */
39
+ export declare const tag: {
40
+ readonly eq: (key: string, value: string) => OsmTagPredicate;
41
+ readonly neq: (key: string, value: string) => OsmTagPredicate;
42
+ readonly oneOf: (key: string, values: readonly string[]) => OsmTagPredicate;
43
+ readonly noneOf: (key: string, values: readonly string[]) => OsmTagPredicate;
44
+ readonly regex: (key: string, pattern: string) => OsmTagPredicate;
45
+ readonly nregex: (key: string, pattern: string) => OsmTagPredicate;
46
+ readonly present: (key: string) => OsmTagPredicate;
47
+ readonly absent: (key: string) => OsmTagPredicate;
48
+ };
49
+ /**
50
+ * Compiles each include clause to an Overpass way-filter suffix, e.g.
51
+ * `[highway=cycleway][access!=private]`.
52
+ * Callers wrap these in `way…(bbox)` union statements.
53
+ */
54
+ export declare function compileOverpassWaySelectors(policy: OsmWaySelectionPolicy): string[];
55
+ /** Single selector when the policy has exactly one include clause; otherwise throws. */
56
+ export declare function compileOverpassWaySelector(policy: OsmWaySelectionPolicy): string;
57
+ /** Whether OSM tags match the policy (OR of include clauses, each with globalAll). */
58
+ export declare function matchesOsmWaySelection(tags: OsmTags, policy: OsmWaySelectionPolicy): boolean;
59
+ export declare function matchesOsmWaySelectionSegment(segment: Segment, policy: OsmWaySelectionPolicy): boolean;
60
+ /** Builds Overpass QL that downloads matching ways (+ their nodes) for a bbox string. */
61
+ export declare function buildWaysOverpassQuery(policy: OsmWaySelectionPolicy, bbox: string, options?: {
62
+ readonly timeoutSeconds?: number;
63
+ readonly out?: 'meta' | 'body';
64
+ }): string;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@osm-editor-kit/osm-way-chain",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Traverse and orient connected OSM highway ways for multi-segment editing sessions.",
5
+ "homepage": "https://github.com/osmberlin/street-space-editor/tree/main/packages/osm-way-chain",
5
6
  "license": "MIT",
6
7
  "author": "Tobias Jordans",
7
8
  "repository": {
@@ -9,11 +10,11 @@
9
10
  "url": "git+https://github.com/osmberlin/street-space-editor.git",
10
11
  "directory": "packages/osm-way-chain"
11
12
  },
12
- "type": "module",
13
- "sideEffects": false,
14
13
  "files": [
15
14
  "dist"
16
15
  ],
16
+ "type": "module",
17
+ "sideEffects": false,
17
18
  "exports": {
18
19
  ".": {
19
20
  "types": "./dist/index.d.ts",