ochre-sdk 1.1.3 → 1.1.4
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/dist/fetchers/gallery.mjs +2 -2
- package/dist/fetchers/item-children.mjs +2 -2
- package/dist/fetchers/item-links.mjs +2 -2
- package/dist/fetchers/item-ocr-data.mjs +4 -6
- package/dist/fetchers/item.mjs +2 -2
- package/dist/fetchers/request.d.mts +1 -1
- package/dist/fetchers/request.mjs +2 -1
- package/dist/fetchers/website-metadata.mjs +2 -1
- package/dist/fetchers/website.mjs +5 -3
- package/dist/query.mjs +15 -19
- package/dist/xquery.d.mts +11 -0
- package/dist/xquery.mjs +12 -1
- package/package.json +9 -9
|
@@ -4,14 +4,14 @@ import { gallerySchema } from "../schemas.mjs";
|
|
|
4
4
|
import { parseRequestedLanguages, resolveContentLanguages } from "../parsers/languages.mjs";
|
|
5
5
|
import { parseGallery } from "../parsers/index.mjs";
|
|
6
6
|
import { XMLGalleryData } from "../xml/schemas.mjs";
|
|
7
|
-
import { compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
7
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
8
8
|
import * as v from "valibot";
|
|
9
9
|
//#region src/fetchers/gallery.ts
|
|
10
10
|
function buildXQuery(parameters) {
|
|
11
11
|
const { uuid, filter, page, perPage } = parameters;
|
|
12
12
|
const start = (page - 1) * perPage + 1;
|
|
13
13
|
const filterLiteral = stringLiteral(filter?.trim() ?? "");
|
|
14
|
-
return compileOchreQuery({ body: ({ omitSupplemental }) => `<ochre
|
|
14
|
+
return compileOchreQuery({ body: ({ omitSupplemental }) => `<ochre>${OCHRE_RESPONSE_PADDING}{
|
|
15
15
|
for $q in doc()/ochre[@uuid=${stringLiteral(uuid)}]
|
|
16
16
|
let $filter := ${filterLiteral}
|
|
17
17
|
let $resources := $q//items/resource
|
|
@@ -5,7 +5,7 @@ import { uuidSchema } from "../schemas.mjs";
|
|
|
5
5
|
import { parseRequestedLanguages, resolveContentLanguages } from "../parsers/languages.mjs";
|
|
6
6
|
import { parseLinkedItems } from "../parsers/index.mjs";
|
|
7
7
|
import { XMLItemLinksData } from "../xml/schemas.mjs";
|
|
8
|
-
import { compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
8
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
9
9
|
import * as v from "valibot";
|
|
10
10
|
//#region src/fetchers/item-children.ts
|
|
11
11
|
function buildXQuery(uuid, category) {
|
|
@@ -49,7 +49,7 @@ let $children :=
|
|
|
49
49
|
else if ($category = "resource") then $item/resource
|
|
50
50
|
else ()
|
|
51
51
|
return
|
|
52
|
-
<ochre
|
|
52
|
+
<ochre>${OCHRE_RESPONSE_PADDING}
|
|
53
53
|
<items>{${omitSupplemental("$children")}}</items>
|
|
54
54
|
</ochre>`
|
|
55
55
|
});
|
|
@@ -5,7 +5,7 @@ import { uuidSchema } from "../schemas.mjs";
|
|
|
5
5
|
import { parseRequestedLanguages, resolveContentLanguages } from "../parsers/languages.mjs";
|
|
6
6
|
import { parseLinkedItems } from "../parsers/index.mjs";
|
|
7
7
|
import { XMLItemLinksData } from "../xml/schemas.mjs";
|
|
8
|
-
import { compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
8
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
9
9
|
import * as v from "valibot";
|
|
10
10
|
//#region src/fetchers/item-links.ts
|
|
11
11
|
/**
|
|
@@ -24,7 +24,7 @@ function buildXQuery(uuid) {
|
|
|
24
24
|
return `if (${ITEM_CATEGORY_ALIASES[category].map((alias) => `$category = ${stringLiteral(alias)}`).join(" or ")}) then fn:collection("ochre/${category}")/ochre/${category}[@uuid = $uuid]`;
|
|
25
25
|
}).join("\n else ")}
|
|
26
26
|
else ()`;
|
|
27
|
-
return compileOchreQuery({ body: ({ omitSupplemental }) => `<ochre
|
|
27
|
+
return compileOchreQuery({ body: ({ omitSupplemental }) => `<ochre>${OCHRE_RESPONSE_PADDING}{let $item-uuid := ${stringLiteral(uuid)}
|
|
28
28
|
|
|
29
29
|
let $source-items := (
|
|
30
30
|
${OCHRE_COLLECTION_CATEGORIES.map((category) => ` fn:collection("ochre/${category}")/ochre[@uuid = $item-uuid]/${category}`).join(",\n")}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getErrorOutput } from "../errors.mjs";
|
|
2
2
|
import { requestOchre } from "./request.mjs";
|
|
3
3
|
import { itemOcrDataParametersSchema } from "../schemas.mjs";
|
|
4
|
-
import { stringLiteral } from "../xquery.mjs";
|
|
4
|
+
import { OCHRE_RESPONSE_PADDING, stringLiteral } from "../xquery.mjs";
|
|
5
5
|
import { buildOcrWordPath } from "../ocr.mjs";
|
|
6
6
|
import * as v from "valibot";
|
|
7
7
|
//#region src/fetchers/item-ocr-data.ts
|
|
@@ -62,10 +62,8 @@ function parseOcrStringVertices(value) {
|
|
|
62
62
|
* these elements and may serve them in a namespace. A plain `//ocr//string`
|
|
63
63
|
* name test silently matches nothing in either of those cases.
|
|
64
64
|
*
|
|
65
|
-
* The matches are wrapped in an `<ocrStrings>` element
|
|
66
|
-
*
|
|
67
|
-
* element children down to a bare `<ochre/>`, which would drop the `found`
|
|
68
|
-
* flag and make "no such item" indistinguishable from "no matches".
|
|
65
|
+
* The matches are wrapped in an `<ocrStrings>` element carrying a `found`
|
|
66
|
+
* flag, which tells "no such item" apart from "no matches".
|
|
69
67
|
* @param parameters - The parameters for the fetch
|
|
70
68
|
* @param parameters.uuid - The UUID of the OCHRE item to read the OCR layer of
|
|
71
69
|
* @param parameters.terms - The whitespace-separated search terms to match against, already lowercased for case-insensitive matching
|
|
@@ -95,7 +93,7 @@ let $ocrStrings :=
|
|
|
95
93
|
height="{string($string/@HEIGHT)}"
|
|
96
94
|
vertices="{string($string/@VERTICES)}"/>
|
|
97
95
|
|
|
98
|
-
return <ochre
|
|
96
|
+
return <ochre>${OCHRE_RESPONSE_PADDING}<ocrStrings found="{exists($ochre)}">{$ocrStrings}</ocrStrings></ochre>`;
|
|
99
97
|
}
|
|
100
98
|
/**
|
|
101
99
|
* Fetches and parses the OCR strings of an OCHRE item that match a search value
|
package/dist/fetchers/item.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { uuidSchema } from "../schemas.mjs";
|
|
|
5
5
|
import { parseRequestedLanguages } from "../parsers/languages.mjs";
|
|
6
6
|
import { parseItem } from "../parsers/index.mjs";
|
|
7
7
|
import { XMLData } from "../xml/schemas.mjs";
|
|
8
|
-
import { compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
8
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
9
9
|
import { parseWebpageView } from "../parsers/website/index.mjs";
|
|
10
10
|
import * as v from "valibot";
|
|
11
11
|
//#region src/fetchers/item.ts
|
|
@@ -48,7 +48,7 @@ ${ITEM_CATEGORIES_WITH_EMBEDDED_ITEMS.map((category) => ` $ochre/${category}`).
|
|
|
48
48
|
}
|
|
49
49
|
return compileOchreQuery({ body: ({ omitSupplemental }) => `${letClauses.join("\n")}
|
|
50
50
|
return
|
|
51
|
-
if (empty($ochre)) then
|
|
51
|
+
if (empty($ochre)) then <ochre>${OCHRE_RESPONSE_PADDING}</ochre>
|
|
52
52
|
else element ochre {
|
|
53
53
|
$ochre/@*,
|
|
54
54
|
${omitSupplemental(itemNodesExpression)}
|
|
@@ -21,7 +21,7 @@ export type OchreRequestOptions = {
|
|
|
21
21
|
* @param parameters.label - What is being decoded, used in failure messages
|
|
22
22
|
* @param parameters.checkRawData - Guard run against the parsed XML before validation
|
|
23
23
|
* @returns The validated response, with XML source metadata restored
|
|
24
|
-
* @throws When the guard rejects or validation fails
|
|
24
|
+
* @throws When the query failed on the server, the guard rejects, or validation fails
|
|
25
25
|
* @internal
|
|
26
26
|
*/
|
|
27
27
|
export declare function decodeOchreResponse<TOutput>(parameters: {
|
|
@@ -27,11 +27,12 @@ function resolveSignal(options) {
|
|
|
27
27
|
* @param parameters.label - What is being decoded, used in failure messages
|
|
28
28
|
* @param parameters.checkRawData - Guard run against the parsed XML before validation
|
|
29
29
|
* @returns The validated response, with XML source metadata restored
|
|
30
|
-
* @throws When the guard rejects or validation fails
|
|
30
|
+
* @throws When the query failed on the server, the guard rejects, or validation fails
|
|
31
31
|
* @internal
|
|
32
32
|
*/
|
|
33
33
|
function decodeOchreResponse(parameters) {
|
|
34
34
|
const { xml, schema, label, checkRawData } = parameters;
|
|
35
|
+
if (xml.trim() === "<result><ochre/></result>") throw new Error(`Failed to fetch ${label}: the OCHRE API could not run the query`);
|
|
35
36
|
const data = xmlParser.parse(xml);
|
|
36
37
|
checkRawData?.(data);
|
|
37
38
|
const { success, issues, output } = v.safeParse(schema, data);
|
|
@@ -4,7 +4,7 @@ import { parseStringLike } from "../parsers/helpers.mjs";
|
|
|
4
4
|
import { parseMetadataLanguages, parseRequestedLanguages, resolveLanguages } from "../parsers/languages.mjs";
|
|
5
5
|
import { parseIdentification, parseSimplifiedProperties } from "../parsers/index.mjs";
|
|
6
6
|
import { XMLWebsiteData } from "../xml/schemas.mjs";
|
|
7
|
-
import { compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
7
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
8
8
|
import { websitePresentationReader } from "../parsers/website/reader.mjs";
|
|
9
9
|
import { WEBSITE_WALK_DECLARATIONS } from "../parsers/website/walk.mjs";
|
|
10
10
|
//#region src/fetchers/website-metadata.ts
|
|
@@ -118,6 +118,7 @@ return
|
|
|
118
118
|
$website/@belongsTo,
|
|
119
119
|
$website/@publicationDateTime,
|
|
120
120
|
$website/@languages,
|
|
121
|
+
${OCHRE_RESPONSE_PADDING},
|
|
121
122
|
${omitSupplemental(`(
|
|
122
123
|
$website/metadata,
|
|
123
124
|
local:metadata-tree($website/tree[1], $target-slug, "")
|
|
@@ -2,7 +2,7 @@ import { getErrorOutput } from "../errors.mjs";
|
|
|
2
2
|
import { isOchreJsonAccepted, requestOchre } from "./request.mjs";
|
|
3
3
|
import { parseLanguages } from "../parsers/languages.mjs";
|
|
4
4
|
import { XMLWebsiteData } from "../xml/schemas.mjs";
|
|
5
|
-
import { compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
5
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "../xquery.mjs";
|
|
6
6
|
import { parseWebsite } from "../parsers/website/index.mjs";
|
|
7
7
|
//#region src/fetchers/website.ts
|
|
8
8
|
async function areWebsiteCredentialsValid(uuid, credentials, options) {
|
|
@@ -26,8 +26,10 @@ async function areWebsiteCredentialsValid(uuid, credentials, options) {
|
|
|
26
26
|
* @returns An XQuery string
|
|
27
27
|
*/
|
|
28
28
|
function buildXQuery(abbreviation) {
|
|
29
|
-
return compileOchreQuery({ body: ({ omitSupplemental }) => `
|
|
30
|
-
return
|
|
29
|
+
return compileOchreQuery({ body: ({ omitSupplemental }) => `let $ochre := collection("ochre/tree")/ochre[tree/identification/abbreviation/content/string = ${stringLiteral(abbreviation)}][1]
|
|
30
|
+
return
|
|
31
|
+
if (empty($ochre)) then <ochre>${OCHRE_RESPONSE_PADDING}</ochre>
|
|
32
|
+
else element ochre { $ochre/@*, ${omitSupplemental("$ochre/node()")} }` });
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Fetches and parses a website configuration from the OCHRE API.
|
package/dist/query.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BELONGS_TO_COLLECTION_UUID } from "./constants.mjs";
|
|
2
|
-
import { compileOchreQuery, stringLiteral } from "./xquery.mjs";
|
|
2
|
+
import { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral } from "./xquery.mjs";
|
|
3
3
|
import { OCR_WORD_CONTENT_ATTRIBUTE, OCR_WORD_QNAMES, buildOcrWordPath } from "./ocr.mjs";
|
|
4
4
|
//#region src/query.ts
|
|
5
5
|
const CTS_INCLUDES_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -517,13 +517,15 @@ function buildOcrValueQueryExpression(parameters) {
|
|
|
517
517
|
})})`;
|
|
518
518
|
}
|
|
519
519
|
/**
|
|
520
|
-
* Compile an OCR text search into a query over the `<ocr>`
|
|
521
|
-
* document
|
|
520
|
+
* Compile an OCR text search into a query over the `<ocr>` layers of a
|
|
521
|
+
* Resource document
|
|
522
522
|
*
|
|
523
|
-
* Each word node in
|
|
523
|
+
* Each word node in a layer holds a single OCR word in its `CONTENT`
|
|
524
524
|
* attribute, so `includes` matches every search term as a word inside that
|
|
525
|
-
* attribute
|
|
526
|
-
*
|
|
525
|
+
* attribute, and `exact` requires every term to equal a whole `CONTENT` value.
|
|
526
|
+
* A Resource carries one layer per page, so each term is its own element query
|
|
527
|
+
* and the terms may fall on different pages, which is also all the indexes can
|
|
528
|
+
* tell apart.
|
|
527
529
|
*
|
|
528
530
|
* The conjunction is only an index narrowing for `exact`. Attribute values
|
|
529
531
|
* carry no word positions, so `cts:near-query` over them silently degenerates
|
|
@@ -533,20 +535,13 @@ function buildOcrValueQueryExpression(parameters) {
|
|
|
533
535
|
*/
|
|
534
536
|
function buildOcrQueryExpression(query) {
|
|
535
537
|
const { value, matchMode, isCaseSensitive } = query;
|
|
536
|
-
|
|
537
|
-
const terms = tokenizeOcrExactValue(value);
|
|
538
|
-
if (terms.length === 0) return "cts:false-query()";
|
|
539
|
-
return buildNestedElementQuery(["ocr"], buildAndCtsQueryExpressionInternal(Array.from(terms, (term) => buildOcrValueQueryExpression({
|
|
540
|
-
value: searchValue(term),
|
|
541
|
-
isCaseSensitive
|
|
542
|
-
}))));
|
|
543
|
-
}
|
|
544
|
-
const terms = tokenizeIncludesSearchValue({
|
|
538
|
+
const terms = matchMode === "exact" ? tokenizeOcrExactValue(value) : tokenizeIncludesSearchValue({
|
|
545
539
|
value,
|
|
546
540
|
isCaseSensitive
|
|
547
541
|
});
|
|
548
542
|
if (terms.length === 0) return "cts:false-query()";
|
|
549
|
-
|
|
543
|
+
const buildTermQueryExpression = matchMode === "exact" ? buildOcrValueQueryExpression : buildOcrWordQueryExpression;
|
|
544
|
+
return buildAndCtsQueryExpressionInternal(Array.from(terms, (term) => buildNestedElementQuery(["ocr"], buildTermQueryExpression({
|
|
550
545
|
value: searchValue(term),
|
|
551
546
|
isCaseSensitive
|
|
552
547
|
}))));
|
|
@@ -592,12 +587,13 @@ function registerOcrBinding(context, query) {
|
|
|
592
587
|
const queryExpression = buildOcrQueryExpression(query);
|
|
593
588
|
const phraseTerms = query.matchMode === "exact" ? tokenizeOcrExactValue(query.value) : [];
|
|
594
589
|
context.ocrBindingNamesByKey.set(key, name);
|
|
595
|
-
const
|
|
590
|
+
const scopedQueryExpression = buildAndCtsQueryExpressionInternal([queryExpression, `cts:document-query(${context.baseItemsExpression}/@uuid/string())`]);
|
|
596
591
|
const phraseHelperName = phraseTerms.length > 1 ? registerOcrPhraseHelper(context) : null;
|
|
597
592
|
context.ocrBindings.push({
|
|
598
593
|
name,
|
|
599
|
-
expression: queryExpression === "cts:false-query()" ? "()" : phraseHelperName
|
|
594
|
+
expression: queryExpression === "cts:false-query()" ? "()" : phraseHelperName != null ? `for $ocrResource in cts:search(/ochre/resource, ${scopedQueryExpression}, "unfiltered")
|
|
600
595
|
where ${phraseHelperName}($ocrResource, (${phraseTerms.map((term) => stringLiteral(term)).join(", ")}), ${query.isCaseSensitive ? "true()" : "false()"})
|
|
596
|
+
return string($ocrResource/@uuid)` : query.matchMode === "includes" && !query.isCaseSensitive ? `cts:uris((), (), ${scopedQueryExpression})` : `for $ocrResource in cts:search(/ochre/resource, ${scopedQueryExpression})
|
|
601
597
|
return string($ocrResource/@uuid)`
|
|
602
598
|
});
|
|
603
599
|
return name;
|
|
@@ -1226,7 +1222,7 @@ function compileContainerItemsQuery(parameters) {
|
|
|
1226
1222
|
`declare variable ${scopeVariable} := (${Array.from(scopeUuids, (uuid) => stringLiteral(uuid)).join(", ")});`,
|
|
1227
1223
|
...plan.prolog === "" ? [] : [plan.prolog]
|
|
1228
1224
|
],
|
|
1229
|
-
body: (context) => `<ochre
|
|
1225
|
+
body: (context) => `<ochre>${OCHRE_RESPONSE_PADDING}{
|
|
1230
1226
|
${plan.itemsClause}
|
|
1231
1227
|
${body({
|
|
1232
1228
|
...context,
|
package/dist/xquery.d.mts
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
8
|
export declare function stringLiteral(value: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* A comment every query puts inside the `<ochre>` element it returns
|
|
11
|
+
*
|
|
12
|
+
* The API answers a failed query with a bare `<result><ochre/></result>`, and
|
|
13
|
+
* sends the same response whenever the returned `<ochre>` element holds fewer
|
|
14
|
+
* than 25 characters. Padding keeps an empty or not-found result above that
|
|
15
|
+
* cutoff, so the bare response only ever means failure. The XML parser drops
|
|
16
|
+
* comments, so no schema sees it.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare const OCHRE_RESPONSE_PADDING = "<!--ochre-sdk: keeps an empty result distinct from a failed query-->";
|
|
9
20
|
/**
|
|
10
21
|
* What a query body can ask the surrounding document for
|
|
11
22
|
*
|
package/dist/xquery.mjs
CHANGED
|
@@ -9,6 +9,17 @@ function stringLiteral(value) {
|
|
|
9
9
|
return `"${value.replaceAll("\"", "\"\"")}"`;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
+
* A comment every query puts inside the `<ochre>` element it returns
|
|
13
|
+
*
|
|
14
|
+
* The API answers a failed query with a bare `<result><ochre/></result>`, and
|
|
15
|
+
* sends the same response whenever the returned `<ochre>` element holds fewer
|
|
16
|
+
* than 25 characters. Padding keeps an empty or not-found result above that
|
|
17
|
+
* cutoff, so the bare response only ever means failure. The XML parser drops
|
|
18
|
+
* comments, so no schema sees it.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
const OCHRE_RESPONSE_PADDING = "<!--ochre-sdk: keeps an empty result distinct from a failed query-->";
|
|
22
|
+
/**
|
|
12
23
|
* XQuery prolog declaring `local:omit-supplemental`, which drops every element
|
|
13
24
|
* carrying `supplemental="true"` from a node sequence, at any depth.
|
|
14
25
|
*
|
|
@@ -63,4 +74,4 @@ ${body({
|
|
|
63
74
|
})}`;
|
|
64
75
|
}
|
|
65
76
|
//#endregion
|
|
66
|
-
export { compileOchreQuery, stringLiteral };
|
|
77
|
+
export { OCHRE_RESPONSE_PADDING, compileOchreQuery, stringLiteral };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ochre-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Node.js library for working with OCHRE (Online Cultural and Historical Research Environment) data",
|
|
@@ -45,23 +45,23 @@
|
|
|
45
45
|
"dist"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"fast-equals": "^6.0.
|
|
48
|
+
"fast-equals": "^6.0.4",
|
|
49
49
|
"fast-xml-parser": "^5.11.1",
|
|
50
50
|
"valibot": "^1.5.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@antfu/eslint-config": "^9.5.1",
|
|
54
|
-
"@types/node": "^24.13.
|
|
54
|
+
"@types/node": "^24.13.6",
|
|
55
55
|
"bumpp": "^12.3.0",
|
|
56
|
-
"eslint": "^10.
|
|
57
|
-
"eslint-plugin-erasable-syntax-only": "^0.7.
|
|
56
|
+
"eslint": "^10.11.0",
|
|
57
|
+
"eslint-plugin-erasable-syntax-only": "^0.7.2",
|
|
58
58
|
"eslint-plugin-slop": "^0.1.3",
|
|
59
|
-
"eslint-plugin-sonarjs": "^4.2.
|
|
60
|
-
"knip": "^6.
|
|
61
|
-
"oxfmt": "^0.
|
|
59
|
+
"eslint-plugin-sonarjs": "^4.2.1",
|
|
60
|
+
"knip": "^6.38.0",
|
|
61
|
+
"oxfmt": "^0.70.0",
|
|
62
62
|
"tsdown": "^0.23.0",
|
|
63
63
|
"typescript": "^6.0.3",
|
|
64
|
-
"vitest": "^5.0.
|
|
64
|
+
"vitest": "^5.0.1"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"dev": "tsdown src/index.ts --watch",
|