Add support for numerical sorting
This commit is contained in:
parent
e661fe8cf2
commit
1a2e17ff17
|
@ -40,13 +40,11 @@
|
||||||
if (wasSelectedTable._id === table._id) {
|
if (wasSelectedTable._id === table._id) {
|
||||||
$goto("./table")
|
$goto("./table")
|
||||||
}
|
}
|
||||||
editorModal.hide()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
await tables.save(table)
|
await tables.save(table)
|
||||||
notifications.success("Table renamed successfully")
|
notifications.success("Table renamed successfully")
|
||||||
editorModal.hide()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkValid(evt) {
|
function checkValid(evt) {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import { store, currentAsset } from "builderStore"
|
import { store, currentAsset } from "builderStore"
|
||||||
import { getBindableProperties } from "builderStore/dataBinding"
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import DrawerBindableInput from "components/common/DrawerBindableInput.svelte"
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -159,35 +159,44 @@
|
||||||
bind:value={expression.field}
|
bind:value={expression.field}
|
||||||
options={fieldOptions}
|
options={fieldOptions}
|
||||||
on:change={e => onFieldChange(expression, e.detail)}
|
on:change={e => onFieldChange(expression, e.detail)}
|
||||||
placeholder="Column" />
|
placeholder="Column"
|
||||||
|
/>
|
||||||
<Select
|
<Select
|
||||||
disabled={!expression.field}
|
disabled={!expression.field}
|
||||||
options={getValidOperatorsForType(expression.type)}
|
options={getValidOperatorsForType(expression.type)}
|
||||||
bind:value={expression.operator}
|
bind:value={expression.operator}
|
||||||
on:change={e => onOperatorChange(expression, e.detail)}
|
on:change={e => onOperatorChange(expression, e.detail)}
|
||||||
placeholder={null} />
|
placeholder={null}
|
||||||
{#if ['string', 'longform', 'number'].includes(expression.type)}
|
/>
|
||||||
|
{#if ["string", "longform", "number"].includes(expression.type)}
|
||||||
<DrawerBindableInput
|
<DrawerBindableInput
|
||||||
disabled={expression.noValue}
|
disabled={expression.noValue}
|
||||||
title={`Value for "${expression.field}"`}
|
title={`Value for "${expression.field}"`}
|
||||||
value={expression.value}
|
value={expression.value}
|
||||||
placeholder="Value"
|
placeholder="Value"
|
||||||
bindings={bindableProperties}
|
bindings={bindableProperties}
|
||||||
on:change={event => (expression.value = event.detail)} />
|
on:change={event => (expression.value = event.detail)}
|
||||||
{:else if expression.type === 'options'}
|
/>
|
||||||
|
{:else if expression.type === "options"}
|
||||||
<Combobox
|
<Combobox
|
||||||
disabled={expression.noValue}
|
disabled={expression.noValue}
|
||||||
options={getFieldOptions(expression.field)}
|
options={getFieldOptions(expression.field)}
|
||||||
bind:value={expression.value} />
|
bind:value={expression.value}
|
||||||
{:else if expression.type === 'boolean'}
|
/>
|
||||||
|
{:else if expression.type === "boolean"}
|
||||||
<Combobox
|
<Combobox
|
||||||
disabled
|
disabled
|
||||||
options={[{ label: 'True', value: true }, { label: 'False', value: false }]}
|
options={[
|
||||||
bind:value={expression.value} />
|
{ label: "True", value: true },
|
||||||
{:else if expression.type === 'datetime'}
|
{ label: "False", value: false },
|
||||||
|
]}
|
||||||
|
bind:value={expression.value}
|
||||||
|
/>
|
||||||
|
{:else if expression.type === "datetime"}
|
||||||
<DatePicker
|
<DatePicker
|
||||||
disabled={expression.noValue}
|
disabled={expression.noValue}
|
||||||
bind:value={expression.value} />
|
bind:value={expression.value}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<DrawerBindableInput disabled />
|
<DrawerBindableInput disabled />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -196,7 +205,8 @@
|
||||||
size="S"
|
size="S"
|
||||||
quiet
|
quiet
|
||||||
icon="Close"
|
icon="Close"
|
||||||
on:click={() => removeField(expression.id)} />
|
on:click={() => removeField(expression.id)}
|
||||||
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
Layout,
|
Layout,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import {} from "@budibase/bbui"
|
|
||||||
import {
|
import {
|
||||||
getDatasourceForProvider,
|
getDatasourceForProvider,
|
||||||
getSchemaForDatasource,
|
getSchemaForDatasource,
|
||||||
|
|
|
@ -5,14 +5,14 @@ import { enrichRows } from "./rows"
|
||||||
* Fetches a table definition.
|
* Fetches a table definition.
|
||||||
* Since definitions cannot change at runtime, the result is cached.
|
* Since definitions cannot change at runtime, the result is cached.
|
||||||
*/
|
*/
|
||||||
export const fetchTableDefinition = async (tableId) => {
|
export const fetchTableDefinition = async tableId => {
|
||||||
return await API.get({ url: `/api/tables/${tableId}`, cache: true })
|
return await API.get({ url: `/api/tables/${tableId}`, cache: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all rows from a table.
|
* Fetches all rows from a table.
|
||||||
*/
|
*/
|
||||||
export const fetchTableData = async (tableId) => {
|
export const fetchTableData = async tableId => {
|
||||||
const rows = await API.get({ url: `/api/${tableId}/rows` })
|
const rows = await API.get({ url: `/api/${tableId}/rows` })
|
||||||
return await enrichRows(rows, tableId)
|
return await enrichRows(rows, tableId)
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ export const searchTable = async ({
|
||||||
limit,
|
limit,
|
||||||
sort,
|
sort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
sortType,
|
||||||
}) => {
|
}) => {
|
||||||
if (!tableId || (!query && !raw)) {
|
if (!tableId || (!query && !raw)) {
|
||||||
return
|
return
|
||||||
|
@ -59,6 +60,7 @@ export const searchTable = async ({
|
||||||
limit,
|
limit,
|
||||||
sort,
|
sort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
sortType,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
const { QueryBuilder, buildSearchUrl, search } = require("./utils")
|
const { QueryBuilder, buildSearchUrl, search } = require("./utils")
|
||||||
|
|
||||||
exports.rowSearch = async (ctx) => {
|
exports.rowSearch = async ctx => {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const { tableId } = ctx.params
|
const { tableId } = ctx.params
|
||||||
const { bookmark, query, raw, limit, sort, sortOrder } = ctx.request.body
|
const {
|
||||||
|
bookmark,
|
||||||
|
query,
|
||||||
|
raw,
|
||||||
|
limit,
|
||||||
|
sort,
|
||||||
|
sortOrder,
|
||||||
|
sortType,
|
||||||
|
} = ctx.request.body
|
||||||
let url
|
let url
|
||||||
if (query) {
|
if (query) {
|
||||||
url = new QueryBuilder(appId, query, bookmark, limit, sort, sortOrder)
|
url = new QueryBuilder(
|
||||||
|
appId,
|
||||||
|
query,
|
||||||
|
bookmark,
|
||||||
|
limit,
|
||||||
|
sort,
|
||||||
|
sortOrder,
|
||||||
|
sortType
|
||||||
|
)
|
||||||
.addTable(tableId)
|
.addTable(tableId)
|
||||||
.complete()
|
.complete()
|
||||||
} else if (raw) {
|
} else if (raw) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ const fetch = require("node-fetch")
|
||||||
* @param {number} limit The number of entries to return per query.
|
* @param {number} limit The number of entries to return per query.
|
||||||
* @param {string} sort The column to sort by.
|
* @param {string} sort The column to sort by.
|
||||||
* @param {string} sortOrder The order to sort by. "ascending" or "descending".
|
* @param {string} sortOrder The order to sort by. "ascending" or "descending".
|
||||||
|
* @param {string} sortType The type of sort to perform. "string" or "number".
|
||||||
* @param {boolean} excludeDocs By default full rows are returned, if required this can be disabled.
|
* @param {boolean} excludeDocs By default full rows are returned, if required this can be disabled.
|
||||||
* @return {string} The URL which a GET can be performed on to receive results.
|
* @return {string} The URL which a GET can be performed on to receive results.
|
||||||
*/
|
*/
|
||||||
|
@ -21,18 +22,19 @@ function buildSearchUrl({
|
||||||
bookmark,
|
bookmark,
|
||||||
sort,
|
sort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
sortType,
|
||||||
excludeDocs,
|
excludeDocs,
|
||||||
limit = 50,
|
limit = 50,
|
||||||
}) {
|
}) {
|
||||||
let url = `${env.COUCH_DB_URL}/${appId}/_design/database/_search`
|
let url = `${env.COUCH_DB_URL}/${appId}/_design/database/_search`
|
||||||
url += `/${SearchIndexes.ROWS}?q=${query}`
|
url += `/${SearchIndexes.ROWS}?q=${query}`
|
||||||
url += `&limit=${limit}`
|
url += `&limit=${Math.min(limit, 200)}`
|
||||||
if (!excludeDocs) {
|
if (!excludeDocs) {
|
||||||
url += "&include_docs=true"
|
url += "&include_docs=true"
|
||||||
}
|
}
|
||||||
if (sort) {
|
if (sort) {
|
||||||
const orderChar = sortOrder === "descending" ? "-" : ""
|
const orderChar = sortOrder === "descending" ? "-" : ""
|
||||||
url += `&sort="${orderChar}${sort.replace(/ /, "_")}<string>"`
|
url += `&sort="${orderChar}${sort.replace(/ /, "_")}<${sortType}>"`
|
||||||
}
|
}
|
||||||
if (bookmark) {
|
if (bookmark) {
|
||||||
url += `&bookmark=${bookmark}`
|
url += `&bookmark=${bookmark}`
|
||||||
|
@ -41,12 +43,12 @@ function buildSearchUrl({
|
||||||
return checkSlashesInUrl(url)
|
return checkSlashesInUrl(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
const luceneEscape = (value) => {
|
const luceneEscape = value => {
|
||||||
return `${value}`.replace(/[ #+\-&|!(){}\[\]^"~*?:\\]/g, "\\$&")
|
return `${value}`.replace(/[ #+\-&|!(){}\[\]^"~*?:\\]/g, "\\$&")
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryBuilder {
|
class QueryBuilder {
|
||||||
constructor(appId, base, bookmark, limit, sort, sortOrder) {
|
constructor(appId, base, bookmark, limit, sort, sortOrder, sortType) {
|
||||||
this.appId = appId
|
this.appId = appId
|
||||||
this.query = {
|
this.query = {
|
||||||
string: {},
|
string: {},
|
||||||
|
@ -62,6 +64,7 @@ class QueryBuilder {
|
||||||
this.limit = limit || 50
|
this.limit = limit || 50
|
||||||
this.sort = sort
|
this.sort = sort
|
||||||
this.sortOrder = sortOrder || "ascending"
|
this.sortOrder = sortOrder || "ascending"
|
||||||
|
this.sortType = sortType || "string"
|
||||||
}
|
}
|
||||||
|
|
||||||
setLimit(limit) {
|
setLimit(limit) {
|
||||||
|
@ -165,10 +168,10 @@ class QueryBuilder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.query.empty) {
|
if (this.query.empty) {
|
||||||
build(this.query.empty, (key) => `!${key}:["" TO *]`)
|
build(this.query.empty, key => `!${key}:["" TO *]`)
|
||||||
}
|
}
|
||||||
if (this.query.notEmpty) {
|
if (this.query.notEmpty) {
|
||||||
build(this.query.notEmpty, (key) => `${key}:["" TO *]`)
|
build(this.query.notEmpty, key => `${key}:["" TO *]`)
|
||||||
}
|
}
|
||||||
if (rawQuery) {
|
if (rawQuery) {
|
||||||
output = output.length === 0 ? rawQuery : `&${rawQuery}`
|
output = output.length === 0 ? rawQuery : `&${rawQuery}`
|
||||||
|
@ -180,11 +183,12 @@ class QueryBuilder {
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
sort: this.sort,
|
sort: this.sort,
|
||||||
sortOrder: this.sortOrder,
|
sortOrder: this.sortOrder,
|
||||||
|
sortType: this.sortType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.search = async (query) => {
|
exports.search = async query => {
|
||||||
const response = await fetch(query, {
|
const response = await fetch(query, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
})
|
})
|
||||||
|
@ -193,7 +197,7 @@ exports.search = async (query) => {
|
||||||
rows: [],
|
rows: [],
|
||||||
}
|
}
|
||||||
if (json.rows != null && json.rows.length > 0) {
|
if (json.rows != null && json.rows.length > 0) {
|
||||||
output.rows = json.rows.map((row) => row.doc)
|
output.rows = json.rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
if (json.bookmark) {
|
if (json.bookmark) {
|
||||||
output.bookmark = json.bookmark
|
output.bookmark = json.bookmark
|
||||||
|
|
|
@ -18,17 +18,26 @@
|
||||||
|
|
||||||
// Provider state
|
// Provider state
|
||||||
let rows = []
|
let rows = []
|
||||||
|
let allRows = []
|
||||||
let schema = {}
|
let schema = {}
|
||||||
let bookmarks = [null]
|
let bookmarks = [null]
|
||||||
let pageNumber = 0
|
let pageNumber = 0
|
||||||
|
|
||||||
|
$: internalTable = dataSource?.type === "table"
|
||||||
$: query = dataSource?.type === "table" ? buildLuceneQuery(filter) : null
|
$: query = dataSource?.type === "table" ? buildLuceneQuery(filter) : null
|
||||||
$: hasNextPage = bookmarks[pageNumber + 1] != null
|
$: hasNextPage = bookmarks[pageNumber + 1] != null
|
||||||
$: hasPrevPage = pageNumber > 0
|
$: hasPrevPage = pageNumber > 0
|
||||||
$: fetchData(dataSource, query, limit, sortColumn, sortOrder)
|
|
||||||
// $: sortedRows = sortRows(filteredRows, sortColumn, sortOrder)
|
|
||||||
// $: rows = limitRows(sortedRows, limit)
|
|
||||||
$: getSchema(dataSource)
|
$: getSchema(dataSource)
|
||||||
|
$: sortType = getSortType(schema, sortColumn)
|
||||||
|
$: fetchData(dataSource, query, limit, sortColumn, sortOrder, sortType)
|
||||||
|
$: {
|
||||||
|
if (internalTable) {
|
||||||
|
rows = allRows
|
||||||
|
} else {
|
||||||
|
rows = sortRows(allRows, sortColumn, sortOrder)
|
||||||
|
rows = limitRows(rows, limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
$: actions = [
|
$: actions = [
|
||||||
{
|
{
|
||||||
type: ActionTypes.RefreshDatasource,
|
type: ActionTypes.RefreshDatasource,
|
||||||
|
@ -55,7 +64,15 @@
|
||||||
hasPrevPage,
|
hasPrevPage,
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildLuceneQuery = (filter) => {
|
const getSortType = (schema, sortColumn) => {
|
||||||
|
if (!schema || !sortColumn || !schema[sortColumn]) {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
const type = schema?.[sortColumn]?.type
|
||||||
|
return type === "number" ? "number" : "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildLuceneQuery = filter => {
|
||||||
let query = {
|
let query = {
|
||||||
string: {},
|
string: {},
|
||||||
fuzzy: {},
|
fuzzy: {},
|
||||||
|
@ -66,7 +83,7 @@
|
||||||
notEmpty: {},
|
notEmpty: {},
|
||||||
}
|
}
|
||||||
if (Array.isArray(filter)) {
|
if (Array.isArray(filter)) {
|
||||||
filter.forEach((expression) => {
|
filter.forEach(expression => {
|
||||||
if (expression.operator.startsWith("range")) {
|
if (expression.operator.startsWith("range")) {
|
||||||
let range = {
|
let range = {
|
||||||
low: Number.MIN_SAFE_INTEGER,
|
low: Number.MIN_SAFE_INTEGER,
|
||||||
|
@ -86,7 +103,14 @@
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchData = async (dataSource, query, limit, sortColumn, sortOrder) => {
|
const fetchData = async (
|
||||||
|
dataSource,
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
sortType
|
||||||
|
) => {
|
||||||
loading = true
|
loading = true
|
||||||
if (dataSource?.type === "table") {
|
if (dataSource?.type === "table") {
|
||||||
const res = await API.searchTable({
|
const res = await API.searchTable({
|
||||||
|
@ -95,9 +119,10 @@
|
||||||
limit,
|
limit,
|
||||||
sort: sortColumn,
|
sort: sortColumn,
|
||||||
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
||||||
|
sortType,
|
||||||
})
|
})
|
||||||
pageNumber = 0
|
pageNumber = 0
|
||||||
rows = res.rows
|
allRows = res.rows
|
||||||
|
|
||||||
// Check we have next data
|
// Check we have next data
|
||||||
const next = await API.searchTable({
|
const next = await API.searchTable({
|
||||||
|
@ -107,6 +132,7 @@
|
||||||
bookmark: res.bookmark,
|
bookmark: res.bookmark,
|
||||||
sort: sortColumn,
|
sort: sortColumn,
|
||||||
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
||||||
|
sortType,
|
||||||
})
|
})
|
||||||
if (next.rows?.length) {
|
if (next.rows?.length) {
|
||||||
bookmarks = [null, res.bookmark]
|
bookmarks = [null, res.bookmark]
|
||||||
|
@ -115,7 +141,7 @@
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const rows = await API.fetchDatasource(dataSource)
|
const rows = await API.fetchDatasource(dataSource)
|
||||||
rows = inMemoryFilterRows(rows, filter)
|
allRows = inMemoryFilterRows(rows, filter)
|
||||||
}
|
}
|
||||||
loading = false
|
loading = false
|
||||||
loaded = true
|
loaded = true
|
||||||
|
@ -123,9 +149,9 @@
|
||||||
|
|
||||||
const inMemoryFilterRows = (rows, filter) => {
|
const inMemoryFilterRows = (rows, filter) => {
|
||||||
let filteredData = [...rows]
|
let filteredData = [...rows]
|
||||||
Object.entries(filter).forEach(([field, value]) => {
|
Object.entries(filter || {}).forEach(([field, value]) => {
|
||||||
if (value != null && value !== "") {
|
if (value != null && value !== "") {
|
||||||
filteredData = filteredData.filter((row) => {
|
filteredData = filteredData.filter(row => {
|
||||||
return row[field] === value
|
return row[field] === value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -156,7 +182,7 @@
|
||||||
return rows.slice(0, numLimit)
|
return rows.slice(0, numLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSchema = async (dataSource) => {
|
const getSchema = async dataSource => {
|
||||||
if (dataSource?.schema) {
|
if (dataSource?.schema) {
|
||||||
schema = dataSource.schema
|
schema = dataSource.schema
|
||||||
} else if (dataSource?.tableId) {
|
} else if (dataSource?.tableId) {
|
||||||
|
@ -196,9 +222,10 @@
|
||||||
limit,
|
limit,
|
||||||
sort: sortColumn,
|
sort: sortColumn,
|
||||||
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
||||||
|
sortType,
|
||||||
})
|
})
|
||||||
pageNumber++
|
pageNumber++
|
||||||
rows = res.rows
|
allRows = res.rows
|
||||||
|
|
||||||
// Check we have next data
|
// Check we have next data
|
||||||
const next = await API.searchTable({
|
const next = await API.searchTable({
|
||||||
|
@ -208,6 +235,7 @@
|
||||||
bookmark: res.bookmark,
|
bookmark: res.bookmark,
|
||||||
sort: sortColumn,
|
sort: sortColumn,
|
||||||
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
||||||
|
sortType,
|
||||||
})
|
})
|
||||||
if (next.rows?.length) {
|
if (next.rows?.length) {
|
||||||
bookmarks[pageNumber + 1] = res.bookmark
|
bookmarks[pageNumber + 1] = res.bookmark
|
||||||
|
@ -225,9 +253,10 @@
|
||||||
limit,
|
limit,
|
||||||
sort: sortColumn,
|
sort: sortColumn,
|
||||||
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
sortOrder: sortOrder?.toLowerCase() ?? "ascending",
|
||||||
|
sortType,
|
||||||
})
|
})
|
||||||
pageNumber--
|
pageNumber--
|
||||||
rows = res.rows
|
allRows = res.rows
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -4559,7 +4559,7 @@ supports-color@^7.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
svelte@^3.37.0:
|
svelte@^3.38.2:
|
||||||
version "3.38.2"
|
version "3.38.2"
|
||||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"
|
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"
|
||||||
integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==
|
integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==
|
||||||
|
|
Loading…
Reference in New Issue