eyeling 1.7.19 → 1.7.20

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
@@ -21,7 +21,7 @@ Try it here:
21
21
  - Load an N3 program from a URL (in the "Load N3 from URL" box or as ?url=...).
22
22
  - Share a link with the program encoded in the URL fragment (`#...`).
23
23
 
24
- - [Eyeling streaming playground](https://eyereasoner.github.io/eyeling/stream)
24
+ - Streaming demo
25
25
  - Browse a Wikidata entity, load its facts, and see Eyeling’s **deductive closure appear incrementally** as triples are derived.
26
26
  - Edit **N3 rules live** and re-run to watch how different inference rules change what gets derived.
27
27
  - Demo **CORS-safe dynamic fetching**: derived “fetch requests” can trigger extra facts (e.g., Wikiquote extracts) that are injected and re-reasoned.
@@ -0,0 +1,29 @@
1
+ @prefix ex: <http://example.org/> .
2
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
3
+ @prefix tfn: <https://w3id.org/time-fn#> .
4
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
5
+
6
+ ex:e1 a ex:Event ;
7
+ ex:when "2000-01-02T03:04:05"^^xsd:dateTime ; # floating (no TZ)
8
+ ex:day "2000-01-02"^^xsd:date ;
9
+ ex:tz "Z" .
10
+ ex:e2 a ex:Event ;
11
+ ex:when "2000-01-02T03:04:05-03:00"^^xsd:dateTime ;
12
+ ex:day "2000-01-02"^^xsd:date ;
13
+ ex:tz "-03:00" .
14
+
15
+ {
16
+ ?this ex:when ?whenRaw .
17
+ ?this ex:day ?day .
18
+ ?this ex:tz ?tz .
19
+ ?startInc math:notGreaterThan ?whenBound .
20
+ ?whenBound math:notGreaterThan ?endInc .
21
+ (?whenRaw ?tz) tfn:bindDefaultTimezone ?whenBound .
22
+ (?day) tfn:periodMinInclusive ?startInc .
23
+ (?day) tfn:periodMaxInclusive ?endInc .
24
+ (?day) tfn:periodMinExclusive ?startEx .
25
+ (?day) tfn:periodMaxExclusive ?endEx .
26
+ } => {
27
+ ?this ex:occursOn ?day .
28
+ ?this ex:whenBound ?whenBound .
29
+ } .
@@ -0,0 +1,36 @@
1
+ PREFIX ex: <http://example.org/>
2
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
3
+ PREFIX tfn: <https://w3id.org/time-fn#>
4
+
5
+ DATA {
6
+ ex:e1 a ex:Event ;
7
+ ex:when "2000-01-02T03:04:05"^^xsd:dateTime ; # floating (no TZ)
8
+ ex:day "2000-01-02"^^xsd:date ;
9
+ ex:tz "Z" .
10
+
11
+ ex:e2 a ex:Event ;
12
+ ex:when "2000-01-02T03:04:05-03:00"^^xsd:dateTime ;
13
+ ex:day "2000-01-02"^^xsd:date ;
14
+ ex:tz "-03:00" .
15
+ }
16
+
17
+ RULE {
18
+ $this ex:occursOn ?day .
19
+ $this ex:whenBound ?whenBound .
20
+ }
21
+ WHERE {
22
+ $this ex:when ?whenRaw ;
23
+ ex:day ?day ;
24
+ ex:tz ?tz .
25
+
26
+ BIND( tfn:bindDefaultTimezone(?whenRaw, ?tz) AS ?whenBound )
27
+
28
+ BIND( tfn:periodMinInclusive(?day) AS ?startInc )
29
+ BIND( tfn:periodMaxInclusive(?day) AS ?endInc )
30
+ BIND( tfn:periodMinExclusive(?day) AS ?startEx )
31
+ BIND( tfn:periodMaxExclusive(?day) AS ?endEx )
32
+
33
+ # Inclusive containment: start <= whenBound <= end
34
+ FILTER( ?startInc <= ?whenBound && ?whenBound <= ?endInc )
35
+ }
36
+
@@ -0,0 +1,7 @@
1
+ @prefix ex: <http://example.org/> .
2
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
3
+
4
+ ex:e1 ex:occursOn "2000-01-02"^^xsd:date .
5
+ ex:e1 ex:whenBound "2000-01-02T03:04:05Z"^^xsd:dateTime .
6
+ ex:e2 ex:occursOn "2000-01-02"^^xsd:date .
7
+ ex:e2 ex:whenBound "2000-01-02T03:04:05-03:00"^^xsd:dateTime .
@@ -8,6 +8,7 @@
8
8
  @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
9
9
  @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
10
10
  @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
11
+ @prefix tfn: <https://w3id.org/time-fn#> .
11
12
 
12
13
  # ------------------------------------------------------------------------------
13
14
  # Builtin catalog (eyeling.js)
@@ -336,3 +337,26 @@ string:format a ex:Builtin ; ex:kind ex:Function ;
336
337
 
337
338
  string:jsonPointer a ex:Builtin ; ex:kind ex:Function ;
338
339
  rdfs:comment "JSON Pointer lookup: (jsonText pointer) -> value term. Expects rdf:JSON-typed literal (or treated as rdf:JSON); caches parsed JSON and pointer lookups." .
340
+
341
+ # --- tfn: (Time Functions) -------------------------------------------
342
+ # Source semantics: Smessaert et al., “It’s About Time: Time Functions for Comparing Partial and Floating Time Literals in SPARQL”, §2.2.
343
+
344
+ tfn:periodMinInclusive a ex:Builtin ;
345
+ ex:kind ex:Function ;
346
+ rdfs:comment "Time Functions: inclusive lower bound of the period represented by a temporal literal (xsd:date, xsd:gYearMonth, xsd:gYear, xsd:dateTime, ...), returned as xsd:dateTime." .
347
+
348
+ tfn:periodMaxInclusive a ex:Builtin ;
349
+ ex:kind ex:Function ;
350
+ rdfs:comment "Time Functions: inclusive upper bound of the period represented by a temporal literal, returned as xsd:dateTime." .
351
+
352
+ tfn:periodMinExclusive a ex:Builtin ;
353
+ ex:kind ex:Function ;
354
+ rdfs:comment "Time Functions: exclusive lower bound of the period represented by a temporal literal, returned as xsd:dateTime." .
355
+
356
+ tfn:periodMaxExclusive a ex:Builtin ;
357
+ ex:kind ex:Function ;
358
+ rdfs:comment "Time Functions: exclusive upper bound of the period represented by a temporal literal, returned as xsd:dateTime." .
359
+
360
+ tfn:bindDefaultTimezone a ex:Builtin ;
361
+ ex:kind ex:Function ;
362
+ rdfs:comment "Time Functions: binds a default timezone to a floating time literal. Input is (timeLiteral timeZone). If the literal already has a timezone, it is returned unchanged; otherwise the specified timezone is applied." .
package/eyeling.js CHANGED
@@ -42,6 +42,7 @@ const XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
42
42
  const CRYPTO_NS = 'http://www.w3.org/2000/10/swap/crypto#';
43
43
  const MATH_NS = 'http://www.w3.org/2000/10/swap/math#';
44
44
  const TIME_NS = 'http://www.w3.org/2000/10/swap/time#';
45
+ const TFN_NS = 'https://w3id.org/time-fn#';
45
46
  const LIST_NS = 'http://www.w3.org/2000/10/swap/list#';
46
47
  const LOG_NS = 'http://www.w3.org/2000/10/swap/log#';
47
48
  const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
@@ -3625,6 +3626,248 @@ function parseXsdDateTimeLexParts(t) {
3625
3626
  return { yearStr, month, day, hour, minute, second, tz };
3626
3627
  }
3627
3628
 
3629
+ // -----------------------------------------------------------------------------
3630
+ // Time Functions (tfn:) helpers
3631
+ // -----------------------------------------------------------------------------
3632
+
3633
+ function __tfnParseTimezoneValue(t) {
3634
+ // Accept plain string literals and xsd:string. Return "Z" or "+hh:mm" or "-hh:mm".
3635
+ if (!(t instanceof Literal)) return null;
3636
+ const [lex, dt] = literalParts(t.value);
3637
+ const s = stripQuotes(lex).trim();
3638
+ if (!s) return null;
3639
+ if (s === 'Z') return 'Z';
3640
+ const m = /^([+-])(\d{2}):(\d{2})$/.exec(s);
3641
+ if (!m) return null;
3642
+ const hh = parseInt(m[2], 10);
3643
+ const mm = parseInt(m[3], 10);
3644
+ if (!(hh >= 0 && hh <= 14)) return null;
3645
+ if (!(mm >= 0 && mm <= 59)) return null;
3646
+ // disallow offsets beyond 14:00 (e.g., +14:30)
3647
+ if (hh === 14 && mm !== 0) return null;
3648
+ return `${m[1]}${m[2]}:${m[3]}`;
3649
+ }
3650
+
3651
+ function __tfnHasTimezoneSuffix(s) {
3652
+ return /(Z|[+-]\d{2}:\d{2})$/.test(s);
3653
+ }
3654
+
3655
+ function __tfnParseTemporalLiteralParts(t) {
3656
+ if (!(t instanceof Literal)) return null;
3657
+ const [lex, dt] = literalParts(t.value);
3658
+ if (!dt) return null;
3659
+ const val = stripQuotes(lex);
3660
+
3661
+ // xsd:dateTime
3662
+ if (dt === XSD_NS + 'dateTime') {
3663
+ const m = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})?$/.exec(val);
3664
+ if (!m) return null;
3665
+ const year = BigInt(m[1]);
3666
+ const month = parseInt(m[2], 10);
3667
+ const day = parseInt(m[3], 10);
3668
+ const hour = parseInt(m[4], 10);
3669
+ const minute = parseInt(m[5], 10);
3670
+ const second = parseInt(m[6], 10);
3671
+ const frac = m[7] || '';
3672
+ const tz = m[8] || null;
3673
+
3674
+ if (!(month >= 1 && month <= 12)) return null;
3675
+ if (!(day >= 1 && day <= 31)) return null;
3676
+ if (!(hour >= 0 && hour <= 23)) return null;
3677
+ if (!(minute >= 0 && minute <= 59)) return null;
3678
+ if (!(second >= 0 && second <= 59)) return null;
3679
+
3680
+ // Keep milliseconds precision (3 digits), truncating extra digits.
3681
+ const msStr = (frac + '000').slice(0, 3);
3682
+ const millis = parseInt(msStr, 10);
3683
+
3684
+ return { kind: 'dateTime', year, month, day, hour, minute, second, millis, tz, dt };
3685
+ }
3686
+
3687
+ // xsd:date
3688
+ if (dt === XSD_NS + 'date') {
3689
+ const m = /^(-?\d{4,})-(\d{2})-(\d{2})(Z|[+-]\d{2}:\d{2})?$/.exec(val);
3690
+ if (!m) return null;
3691
+ const year = BigInt(m[1]);
3692
+ const month = parseInt(m[2], 10);
3693
+ const day = parseInt(m[3], 10);
3694
+ const tz = m[4] || null;
3695
+ if (!(month >= 1 && month <= 12)) return null;
3696
+ if (!(day >= 1 && day <= 31)) return null;
3697
+ return { kind: 'date', year, month, day, tz, dt };
3698
+ }
3699
+
3700
+ // xsd:gYearMonth
3701
+ if (dt === XSD_NS + 'gYearMonth') {
3702
+ const m = /^(-?\d{4,})-(\d{2})(Z|[+-]\d{2}:\d{2})?$/.exec(val);
3703
+ if (!m) return null;
3704
+ const year = BigInt(m[1]);
3705
+ const month = parseInt(m[2], 10);
3706
+ const tz = m[3] || null;
3707
+ if (!(month >= 1 && month <= 12)) return null;
3708
+ return { kind: 'gYearMonth', year, month, tz, dt };
3709
+ }
3710
+
3711
+ // xsd:gYear
3712
+ if (dt === XSD_NS + 'gYear') {
3713
+ const m = /^(-?\d{4,})(Z|[+-]\d{2}:\d{2})?$/.exec(val);
3714
+ if (!m) return null;
3715
+ const year = BigInt(m[1]);
3716
+ const tz = m[2] || null;
3717
+ return { kind: 'gYear', year, tz, dt };
3718
+ }
3719
+
3720
+ return null;
3721
+ }
3722
+
3723
+ function __tfnMod(a, b) {
3724
+ let r = a % b;
3725
+ if (r < 0n) r += b;
3726
+ return r;
3727
+ }
3728
+
3729
+ function __tfnIsLeapYear(y) {
3730
+ // Proleptic Gregorian leap-year rule.
3731
+ // Works for negative years using normalized modulus.
3732
+ if (__tfnMod(y, 4n) !== 0n) return false;
3733
+ if (__tfnMod(y, 100n) !== 0n) return true;
3734
+ return __tfnMod(y, 400n) === 0n;
3735
+ }
3736
+
3737
+ function __tfnDaysInMonth(y, m) {
3738
+ if (m === 2) return __tfnIsLeapYear(y) ? 29 : 28;
3739
+ if (m === 4 || m === 6 || m === 9 || m === 11) return 30;
3740
+ return 31;
3741
+ }
3742
+
3743
+ function __tfnPad2(n) {
3744
+ return n < 10 ? '0' + String(n) : String(n);
3745
+ }
3746
+ function __tfnPad3(n) {
3747
+ const s = String(n);
3748
+ return s.length === 1 ? '00' + s : s.length === 2 ? '0' + s : s;
3749
+ }
3750
+ function __tfnFormatYear(y) {
3751
+ const neg = y < 0n;
3752
+ const abs = neg ? -y : y;
3753
+ let digits = abs.toString();
3754
+ if (digits.length < 4) digits = '0'.repeat(4 - digits.length) + digits;
3755
+ return neg ? '-' + digits : digits;
3756
+ }
3757
+
3758
+ function __tfnAdd1ms(c) {
3759
+ // Mutates and returns c; c: {year,month,day,hour,minute,second,millis}
3760
+ if (c.millis < 999) { c.millis++; return c; }
3761
+ c.millis = 0;
3762
+ if (c.second < 59) { c.second++; return c; }
3763
+ c.second = 0;
3764
+ if (c.minute < 59) { c.minute++; return c; }
3765
+ c.minute = 0;
3766
+ if (c.hour < 23) { c.hour++; return c; }
3767
+ c.hour = 0;
3768
+
3769
+ const dim = __tfnDaysInMonth(c.year, c.month);
3770
+ if (c.day < dim) { c.day++; return c; }
3771
+ c.day = 1;
3772
+ if (c.month < 12) { c.month++; return c; }
3773
+ c.month = 1;
3774
+ c.year = c.year + 1n;
3775
+ return c;
3776
+ }
3777
+
3778
+ function __tfnSub1ms(c) {
3779
+ // Mutates and returns c; c: {year,month,day,hour,minute,second,millis}
3780
+ if (c.millis > 0) { c.millis--; return c; }
3781
+ c.millis = 999;
3782
+ if (c.second > 0) { c.second--; return c; }
3783
+ c.second = 59;
3784
+ if (c.minute > 0) { c.minute--; return c; }
3785
+ c.minute = 59;
3786
+ if (c.hour > 0) { c.hour--; return c; }
3787
+ c.hour = 23;
3788
+
3789
+ if (c.day > 1) { c.day--; return c; }
3790
+ // move to previous month
3791
+ if (c.month > 1) {
3792
+ c.month--;
3793
+ } else {
3794
+ c.month = 12;
3795
+ c.year = c.year - 1n;
3796
+ }
3797
+ c.day = __tfnDaysInMonth(c.year, c.month);
3798
+ return c;
3799
+ }
3800
+
3801
+ function __tfnMakeDateTimeLiteral(c, tz) {
3802
+ const y = __tfnFormatYear(c.year);
3803
+ const lex = `${y}-${__tfnPad2(c.month)}-${__tfnPad2(c.day)}T${__tfnPad2(c.hour)}:${__tfnPad2(c.minute)}:${__tfnPad2(c.second)}.${__tfnPad3(c.millis)}${tz}`;
3804
+ return internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
3805
+ }
3806
+
3807
+ function __tfnComputePeriodBounds(parts) {
3808
+ // Returns { tzMin, tzMax, startC, endC } where startC/endC are component objects.
3809
+ const tzMin = parts.tz || '+14:00';
3810
+ const tzMax = parts.tz || '-14:00';
3811
+
3812
+ if (parts.kind === 'dateTime') {
3813
+ const base = {
3814
+ year: parts.year,
3815
+ month: parts.month,
3816
+ day: parts.day,
3817
+ hour: parts.hour,
3818
+ minute: parts.minute,
3819
+ second: parts.second,
3820
+ millis: parts.millis,
3821
+ };
3822
+ return { tzMin, tzMax, startC: { ...base }, endC: { ...base } };
3823
+ }
3824
+
3825
+ if (parts.kind === 'date') {
3826
+ const startC = { year: parts.year, month: parts.month, day: parts.day, hour: 0, minute: 0, second: 0, millis: 0 };
3827
+ const endC = { year: parts.year, month: parts.month, day: parts.day, hour: 23, minute: 59, second: 59, millis: 999 };
3828
+ return { tzMin, tzMax, startC, endC };
3829
+ }
3830
+
3831
+ if (parts.kind === 'gYearMonth') {
3832
+ const dim = __tfnDaysInMonth(parts.year, parts.month);
3833
+ const startC = { year: parts.year, month: parts.month, day: 1, hour: 0, minute: 0, second: 0, millis: 0 };
3834
+ const endC = { year: parts.year, month: parts.month, day: dim, hour: 23, minute: 59, second: 59, millis: 999 };
3835
+ return { tzMin, tzMax, startC, endC };
3836
+ }
3837
+
3838
+ if (parts.kind === 'gYear') {
3839
+ const startC = { year: parts.year, month: 1, day: 1, hour: 0, minute: 0, second: 0, millis: 0 };
3840
+ const endC = { year: parts.year, month: 12, day: 31, hour: 23, minute: 59, second: 59, millis: 999 };
3841
+ return { tzMin, tzMax, startC, endC };
3842
+ }
3843
+
3844
+ return null;
3845
+ }
3846
+
3847
+ function __tfnBindDefaultTimezone(timeLit, tzLit) {
3848
+ if (!(timeLit instanceof Literal) || !(tzLit instanceof Literal)) return null;
3849
+ const tz = __tfnParseTimezoneValue(tzLit);
3850
+ if (!tz) return null;
3851
+
3852
+ const [lex, dt] = literalParts(timeLit.value);
3853
+ if (!dt) return null;
3854
+ const v = stripQuotes(lex);
3855
+
3856
+ // If already has tz, return unchanged.
3857
+ if (__tfnHasTimezoneSuffix(v)) return timeLit;
3858
+
3859
+ // Only support the temporal types we parse.
3860
+ if (
3861
+ dt !== XSD_NS + 'dateTime' &&
3862
+ dt !== XSD_NS + 'date' &&
3863
+ dt !== XSD_NS + 'gYearMonth' &&
3864
+ dt !== XSD_NS + 'gYear'
3865
+ ) return null;
3866
+
3867
+ const outLex = `"${v}${tz}"^^<${dt}>`;
3868
+ return internLiteral(outLex);
3869
+ }
3870
+
3628
3871
  function parseDatetimeLike(t) {
3629
3872
  const d = parseXsdDateTerm(t);
3630
3873
  if (d !== null) return d;
@@ -4920,6 +5163,73 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4920
5163
  return [];
4921
5164
  }
4922
5165
 
5166
+ // -----------------------------------------------------------------
5167
+ // 4.3.1 tfn: Time Functions builtins
5168
+ // -----------------------------------------------------------------
5169
+
5170
+ // tfn:periodMinInclusive / periodMaxInclusive / periodMinExclusive / periodMaxExclusive
5171
+ // Schema: ( $s.1+ )+ tfn:* $o-
5172
+ const tfnPeriodKind =
5173
+ pv === TFN_NS + 'periodMinInclusive'
5174
+ ? 'minInc'
5175
+ : pv === TFN_NS + 'periodMaxInclusive'
5176
+ ? 'maxInc'
5177
+ : pv === TFN_NS + 'periodMinExclusive'
5178
+ ? 'minEx'
5179
+ : pv === TFN_NS + 'periodMaxExclusive'
5180
+ ? 'maxEx'
5181
+ : null;
5182
+ if (tfnPeriodKind) {
5183
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 1) return [];
5184
+ const arg = g.s.elems[0];
5185
+ const parts = __tfnParseTemporalLiteralParts(arg);
5186
+ if (!parts) return [];
5187
+ const bounds = __tfnComputePeriodBounds(parts);
5188
+ if (!bounds) return [];
5189
+
5190
+ let out;
5191
+ if (tfnPeriodKind === 'minInc') {
5192
+ out = __tfnMakeDateTimeLiteral({ ...bounds.startC }, bounds.tzMin);
5193
+ } else if (tfnPeriodKind === 'maxInc') {
5194
+ out = __tfnMakeDateTimeLiteral({ ...bounds.endC }, bounds.tzMax);
5195
+ } else if (tfnPeriodKind === 'minEx') {
5196
+ const c = __tfnSub1ms({ ...bounds.startC });
5197
+ out = __tfnMakeDateTimeLiteral(c, bounds.tzMin);
5198
+ } else { // maxEx
5199
+ const c = __tfnAdd1ms({ ...bounds.endC });
5200
+ out = __tfnMakeDateTimeLiteral(c, bounds.tzMax);
5201
+ }
5202
+
5203
+ if (g.o instanceof Var) {
5204
+ const s2 = { ...subst };
5205
+ s2[g.o.name] = out;
5206
+ return [s2];
5207
+ }
5208
+ if (g.o instanceof Blank) return [{ ...subst }];
5209
+
5210
+ const s2 = unifyTerm(g.o, out, subst);
5211
+ return s2 !== null ? [s2] : [];
5212
+ }
5213
+
5214
+ // tfn:bindDefaultTimezone
5215
+ // Schema: ( $s.1+ $s.2+ )+ tfn:bindDefaultTimezone $o-
5216
+ if (pv === TFN_NS + 'bindDefaultTimezone') {
5217
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
5218
+ const [timeLit, tzLit] = g.s.elems;
5219
+ const out = __tfnBindDefaultTimezone(timeLit, tzLit);
5220
+ if (!out) return [];
5221
+
5222
+ if (g.o instanceof Var) {
5223
+ const s2 = { ...subst };
5224
+ s2[g.o.name] = out;
5225
+ return [s2];
5226
+ }
5227
+ if (g.o instanceof Blank) return [{ ...subst }];
5228
+
5229
+ const s2 = unifyTerm(g.o, out, subst);
5230
+ return s2 !== null ? [s2] : [];
5231
+ }
5232
+
4923
5233
  // -----------------------------------------------------------------
4924
5234
  // 4.4 list: builtins
4925
5235
  // -----------------------------------------------------------------
@@ -6144,6 +6454,7 @@ function isBuiltinPred(p) {
6144
6454
  v.startsWith(LOG_NS) ||
6145
6455
  v.startsWith(STRING_NS) ||
6146
6456
  v.startsWith(TIME_NS) ||
6457
+ v.startsWith(TFN_NS) ||
6147
6458
  v.startsWith(LIST_NS)
6148
6459
  );
6149
6460
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.7.19",
3
+ "version": "1.7.20",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [