1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-02-16 09:21:45 +02:00

import scaffoldings

This commit is contained in:
Gani Georgiev 2022-08-05 06:00:38 +03:00
parent 95f9d685dc
commit f459dd8812
25 changed files with 1362 additions and 261 deletions

View File

@ -21,6 +21,7 @@ func BindCollectionApi(app core.App, rg *echo.Group) {
subGroup.GET("/:collection", api.view)
subGroup.PATCH("/:collection", api.update)
subGroup.DELETE("/:collection", api.delete)
subGroup.POST("/import", api.bulkImport)
}
type collectionApi struct {
@ -167,3 +168,19 @@ func (api *collectionApi) delete(c echo.Context) error {
return handlerErr
}
func (api *collectionApi) bulkImport(c echo.Context) error {
form := forms.NewCollectionsImport(api.app)
// load request
if err := c.Bind(form); err != nil {
return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
}
submitErr := form.Submit()
if submitErr != nil {
return rest.NewBadRequestError("Failed to import the submitted collections.", submitErr)
}
return nil
}

View File

@ -128,7 +128,7 @@ func (dao *Dao) Delete(m models.Model) error {
// Save upserts (update or create if primary key is not set) the provided model.
func (dao *Dao) Save(m models.Model) error {
if m.HasId() {
if !m.IsNew() {
return dao.update(m)
}
@ -140,6 +140,10 @@ func (dao *Dao) update(m models.Model) error {
return errors.New("ID is not set")
}
if m.GetCreated().IsZero() {
m.RefreshCreated()
}
m.RefreshUpdated()
if dao.BeforeUpdateFunc != nil {
@ -195,6 +199,9 @@ func (dao *Dao) create(m models.Model) error {
if v, ok := any(m).(models.ColumnValueMapper); ok {
dataMap := v.ColumnValueMap()
if _, ok := dataMap["id"]; !ok {
dataMap["id"] = m.GetId()
}
_, err := dao.db.Insert(m.TableName(), dataMap).Execute()
if err != nil {
@ -206,6 +213,9 @@ func (dao *Dao) create(m models.Model) error {
}
}
// clears the internal isNewFlag
m.UnmarkAsNew()
if dao.AfterCreateFunc != nil {
dao.AfterCreateFunc(dao, m)
}

View File

@ -141,7 +141,7 @@ func (dao *Dao) DeleteCollection(collection *models.Collection) error {
func (dao *Dao) SaveCollection(collection *models.Collection) error {
var oldCollection *models.Collection
if collection.HasId() {
if !collection.IsNew() {
// get the existing collection state to compare with the new one
// note: the select is outside of the transaction to prevent SQLITE_LOCKED error when mixing read&write in a single transaction
var findErr error

View File

@ -147,6 +147,40 @@ func (dao *Dao) IsRecordValueUnique(
return err == nil && !exists
}
// IsRecordValueUnique checks if the provided key-value pair is a unique Record value.
//
// NB! Array values (eg. from multiple select fields) are matched
// as a serialized json strings (eg. `["a","b"]`), so the value uniqueness
// depends on the elements order. Or in other words the following values
// are considered different: `[]string{"a","b"}` and `[]string{"b","a"}`
func (dao *Dao) IsRecordUnique(
collection *models.Collection,
key string,
value any,
excludeId string,
) bool {
var exists bool
var normalizedVal any
switch val := value.(type) {
case []string:
normalizedVal = append(types.JsonArray{}, list.ToInterfaceSlice(val)...)
case []any:
normalizedVal = append(types.JsonArray{}, val...)
default:
normalizedVal = val
}
err := dao.RecordQuery(collection).
Select("count(*)").
AndWhere(dbx.Not(dbx.HashExp{"id": excludeId})).
AndWhere(dbx.HashExp{key: normalizedVal}).
Limit(1).
Row(&exists)
return err == nil && !exists
}
// FindUserRelatedRecords returns all records that has a reference
// to the provided User model (via the user shema field).
func (dao *Dao) FindUserRelatedRecords(user *models.User) ([]*models.Record, error) {

View File

@ -36,7 +36,7 @@ func NewCollectionUpsert(app core.App, collection *models.Collection) *Collectio
form := &CollectionUpsert{
app: app,
collection: collection,
isCreate: !collection.HasId(),
isCreate: collection.IsNew(),
}
// load defaults

151
forms/collections_import.go Normal file
View File

@ -0,0 +1,151 @@
package forms
import (
"fmt"
"log"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/daos"
"github.com/pocketbase/pocketbase/models"
)
// CollectionsImport defines a bulk collections import form.
type CollectionsImport struct {
app core.App
Collections []*models.Collection `form:"collections" json:"collections"`
DeleteOthers bool `form:"deleteOthers" json:"deleteOthers"`
}
// NewCollectionsImport bulk imports (create, replace and delete)
// a user provided list with collections data.
func NewCollectionsImport(app core.App) *CollectionsImport {
form := &CollectionsImport{
app: app,
}
return form
}
// Validate makes the form validatable by implementing [validation.Validatable] interface.
func (form *CollectionsImport) Validate() error {
return validation.ValidateStruct(form,
validation.Field(&form.Collections, validation.Required),
)
}
// Submit applies the import, aka.:
// - imports the form collections (create or replace)
// - sync the collection changes with their related records table
// - ensures the integrity of the imported structure (aka. run validations for each collection)
// - if [form.DeleteOthers] is set, deletes all local collections that are not found in the imports list
//
// All operations are wrapped in a single transaction that are
// rollbacked on the first encountered error.
func (form *CollectionsImport) Submit() error {
if err := form.Validate(); err != nil {
return err
}
// @todo validate id length in the form
return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
oldCollections := []*models.Collection{}
if err := txDao.CollectionQuery().All(&oldCollections); err != nil {
return err
}
mappedOldCollections := make(map[string]*models.Collection, len(oldCollections))
for _, old := range oldCollections {
mappedOldCollections[old.GetId()] = old
}
// raw insert/replace (aka. without any validations)
// (required to make sure that all linked collections exists before running the validations)
mappedFormCollections := make(map[string]*models.Collection, len(form.Collections))
for _, collection := range form.Collections {
if mappedOldCollections[collection.GetId()] == nil {
collection.MarkAsNew()
}
if err := txDao.Save(collection); err != nil {
if form.app.IsDebug() {
log.Println("[CollectionsImport] Save failure", collection.Name, err)
}
return validation.Errors{"collections": validation.NewError(
"collections_import_save_failure",
fmt.Sprintf("Failed to save the imported collection %q (id: %s).", collection.Name, collection.Id),
)}
}
mappedFormCollections[collection.GetId()] = collection
}
// delete all other collections not sent with the import
if form.DeleteOthers {
for _, old := range oldCollections {
if mappedFormCollections[old.GetId()] == nil {
// delete the collection
if err := txDao.DeleteCollection(old); err != nil {
if form.app.IsDebug() {
log.Println("[CollectionsImport] DeleteOthers failure", old.Name, err)
}
return validation.Errors{"deleteOthers": validation.NewError(
"collections_import_collection_delete_failure",
fmt.Sprintf("Failed to delete collection %q. Make sure that the collection is not system or referenced by other collections.", old.Name),
)}
}
}
}
}
// refresh the actual persisted collections list
refreshedCollections := []*models.Collection{}
if err := txDao.CollectionQuery().All(&refreshedCollections); err != nil {
return err
}
// trigger the validator for each existing collection to
// ensure that the app is not left in a broken state
for _, collection := range refreshedCollections {
upsertModel := mappedOldCollections[collection.GetId()]
if upsertModel == nil {
upsertModel = &models.Collection{}
}
upsertForm := NewCollectionUpsert(form.app, upsertModel)
// load form fields with the refreshed collection state
upsertForm.Name = collection.Name
upsertForm.System = collection.System
upsertForm.ListRule = collection.ListRule
upsertForm.ViewRule = collection.ViewRule
upsertForm.CreateRule = collection.CreateRule
upsertForm.UpdateRule = collection.UpdateRule
upsertForm.DeleteRule = collection.DeleteRule
upsertForm.Schema = collection.Schema
if err := upsertForm.Validate(); err != nil {
if form.app.IsDebug() {
log.Println("[CollectionsImport] Validate failure", collection.Name, err)
}
return validation.Errors{"collections": validation.NewError(
"collections_import_validate_failure",
fmt.Sprintf("Integrity check failed - collection %q has invalid data.", collection.Name),
)}
}
}
// sync the records table for each updated collection
for _, collection := range form.Collections {
oldCollection := mappedOldCollections[collection.GetId()]
if err := txDao.SyncRecordTableSchema(collection, oldCollection); err != nil {
if form.app.IsDebug() {
log.Println("[CollectionsImport] Records table sync failure", collection.Name, err)
}
return validation.Errors{"collections": validation.NewError(
"collections_import_records_table_sync_failure",
fmt.Sprintf("Failed to sync the records table changes for collection %q.", collection.Name),
)}
}
}
return nil
})
}

View File

@ -24,6 +24,10 @@ type FilesManager interface {
// Model defines an interface with common methods that all db models should have.
type Model interface {
TableName() string
IsNew() bool
MarkAsNew()
UnmarkAsNew()
SetId(id string)
HasId() bool
GetId() string
GetCreated() types.DateTime
@ -39,6 +43,9 @@ type Model interface {
// BaseModel defines common fields and methods used by all other models.
type BaseModel struct {
insertId string
isNewFlag bool
Id string `db:"id" json:"id"`
Created types.DateTime `db:"created" json:"created"`
Updated types.DateTime `db:"updated" json:"updated"`
@ -54,6 +61,27 @@ func (m *BaseModel) GetId() string {
return m.Id
}
// SetId sets the model's id to the provided one.
func (m *BaseModel) SetId(id string) {
m.Id = id
}
// UnmarkAsNew sets the model's isNewFlag enforcing [m.IsNew()] to be true.
func (m *BaseModel) MarkAsNew() {
m.isNewFlag = true
}
// UnmarkAsNew clears the enforced model's isNewFlag.
func (m *BaseModel) UnmarkAsNew() {
m.isNewFlag = false
}
// IsNew indicates what type of db query (insert or update)
// should be used with the model instance.
func (m *BaseModel) IsNew() bool {
return m.isNewFlag || !m.HasId()
}
// GetCreated returns the model's Created datetime.
func (m *BaseModel) GetCreated() types.DateTime {
return m.Created

View File

@ -24,6 +24,7 @@
window.Prism = window.Prism || {};
window.Prism.manual = true;
</script>
<script src="./libs/diff/diff.js"></script>
</head>
<body>
<div id="app"></div>

455
ui/package-lock.json generated
View File

@ -91,15 +91,15 @@
}
},
"node_modules/@codemirror/state": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.0.tgz",
"integrity": "sha512-qbUr94DZTe6/V1VS7LDLz11rM/1t/nJxR1El4I6UaxDEdc0aZZvq6JCLJWiRmUf95NRAnDH6fhXn+PWp9wGCIg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.1.tgz",
"integrity": "sha512-2s+aXsxmAwnR3Rd+JDHPG/1lw0YsA9PEwl7Re88gHJHGfxyfEzKBmsN4rr53RyPIR4lzbbhJX0DCq0WlqlBIRw==",
"dev": true
},
"node_modules/@codemirror/view": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.1.2.tgz",
"integrity": "sha512-puUydfKwfmOo+ixtuB+uN/ZpcteEYSnpjHmMaow1sOQhNICsKtGBup3i9ybVqyzDagARRYzSHTWjbdeHqmn31w==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.1.4.tgz",
"integrity": "sha512-pekgUX+0hL4ri2JV7/bu7jhhwOgOhU1eRc1/ZyAQYCWcCI4TPB1qLrPE3cD/qW9yjBcYiN9MN0XI1tjK7Yw05Q==",
"dev": true,
"dependencies": {
"@codemirror/state": "^6.0.0",
@ -107,6 +107,22 @@
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.53.tgz",
"integrity": "sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@lezer/common": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.0.tgz",
@ -123,9 +139,9 @@
}
},
"node_modules/@lezer/lr": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.0.tgz",
"integrity": "sha512-TgEpfm9br2SX8JwtwKT8HsQZKuFkLRg6g+IRxObk9nVKQLKnkP3oMh+QGcTBL9GQsfQ2ADtKPbj2iGSMf3ytiA==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.1.tgz",
"integrity": "sha512-RpHRs+Q+5tPsXtobSfSeRFRAnTRD0e4bApDvo74O+JiaWq9812x5S8WgftNX67owdaTQXCB5E8XZGALo4Wt77A==",
"dev": true,
"dependencies": {
"@lezer/common": "^1.0.0"
@ -206,19 +222,19 @@
}
},
"node_modules/chart.js": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.8.2.tgz",
"integrity": "sha512-7rqSlHWMUKFyBDOJvmFGW2lxULtcwaPLegDjX/Nu5j6QybY+GCiQkEY+6cqHw62S5tcwXMD8Y+H5OBGoR7d+ZQ==",
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
"dev": true
},
"node_modules/chartjs-adapter-luxon": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.1.0.tgz",
"integrity": "sha512-CS+xBWEyXYVLBZ3dSY/MwlSXhz8er4JjkApazY84ft/++oOLsmkt6TaXBCsUFudum7QdoYmpxiL/gSp20+emkw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.2.0.tgz",
"integrity": "sha512-h1lEns7+8cUN/Dmk24dhrT9hpAimKImQxzHpILqXn2kocdzj9b/fDlBa8v8/OMq5rq0uZEx/NV1WpByH4l2/Rw==",
"dev": true,
"peerDependencies": {
"chart.js": "^3.0.0",
"luxon": "^1.0.0 || ^2.0.0"
"luxon": ">=1.0.0"
}
},
"node_modules/chokidar": {
@ -281,9 +297,9 @@
}
},
"node_modules/esbuild": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz",
"integrity": "sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.53.tgz",
"integrity": "sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==",
"dev": true,
"hasInstallScript": true,
"bin": {
@ -293,32 +309,33 @@
"node": ">=12"
},
"optionalDependencies": {
"esbuild-android-64": "0.14.51",
"esbuild-android-arm64": "0.14.51",
"esbuild-darwin-64": "0.14.51",
"esbuild-darwin-arm64": "0.14.51",
"esbuild-freebsd-64": "0.14.51",
"esbuild-freebsd-arm64": "0.14.51",
"esbuild-linux-32": "0.14.51",
"esbuild-linux-64": "0.14.51",
"esbuild-linux-arm": "0.14.51",
"esbuild-linux-arm64": "0.14.51",
"esbuild-linux-mips64le": "0.14.51",
"esbuild-linux-ppc64le": "0.14.51",
"esbuild-linux-riscv64": "0.14.51",
"esbuild-linux-s390x": "0.14.51",
"esbuild-netbsd-64": "0.14.51",
"esbuild-openbsd-64": "0.14.51",
"esbuild-sunos-64": "0.14.51",
"esbuild-windows-32": "0.14.51",
"esbuild-windows-64": "0.14.51",
"esbuild-windows-arm64": "0.14.51"
"@esbuild/linux-loong64": "0.14.53",
"esbuild-android-64": "0.14.53",
"esbuild-android-arm64": "0.14.53",
"esbuild-darwin-64": "0.14.53",
"esbuild-darwin-arm64": "0.14.53",
"esbuild-freebsd-64": "0.14.53",
"esbuild-freebsd-arm64": "0.14.53",
"esbuild-linux-32": "0.14.53",
"esbuild-linux-64": "0.14.53",
"esbuild-linux-arm": "0.14.53",
"esbuild-linux-arm64": "0.14.53",
"esbuild-linux-mips64le": "0.14.53",
"esbuild-linux-ppc64le": "0.14.53",
"esbuild-linux-riscv64": "0.14.53",
"esbuild-linux-s390x": "0.14.53",
"esbuild-netbsd-64": "0.14.53",
"esbuild-openbsd-64": "0.14.53",
"esbuild-sunos-64": "0.14.53",
"esbuild-windows-32": "0.14.53",
"esbuild-windows-64": "0.14.53",
"esbuild-windows-arm64": "0.14.53"
}
},
"node_modules/esbuild-android-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz",
"integrity": "sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.53.tgz",
"integrity": "sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==",
"cpu": [
"x64"
],
@ -332,9 +349,9 @@
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz",
"integrity": "sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.53.tgz",
"integrity": "sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==",
"cpu": [
"arm64"
],
@ -348,9 +365,9 @@
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz",
"integrity": "sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.53.tgz",
"integrity": "sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==",
"cpu": [
"x64"
],
@ -364,9 +381,9 @@
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz",
"integrity": "sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.53.tgz",
"integrity": "sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==",
"cpu": [
"arm64"
],
@ -380,9 +397,9 @@
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz",
"integrity": "sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.53.tgz",
"integrity": "sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==",
"cpu": [
"x64"
],
@ -396,9 +413,9 @@
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz",
"integrity": "sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.53.tgz",
"integrity": "sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==",
"cpu": [
"arm64"
],
@ -412,9 +429,9 @@
}
},
"node_modules/esbuild-linux-32": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz",
"integrity": "sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.53.tgz",
"integrity": "sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==",
"cpu": [
"ia32"
],
@ -428,9 +445,9 @@
}
},
"node_modules/esbuild-linux-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz",
"integrity": "sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.53.tgz",
"integrity": "sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==",
"cpu": [
"x64"
],
@ -444,9 +461,9 @@
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz",
"integrity": "sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.53.tgz",
"integrity": "sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==",
"cpu": [
"arm"
],
@ -460,9 +477,9 @@
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz",
"integrity": "sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.53.tgz",
"integrity": "sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==",
"cpu": [
"arm64"
],
@ -476,9 +493,9 @@
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz",
"integrity": "sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.53.tgz",
"integrity": "sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==",
"cpu": [
"mips64el"
],
@ -492,9 +509,9 @@
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz",
"integrity": "sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.53.tgz",
"integrity": "sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==",
"cpu": [
"ppc64"
],
@ -508,9 +525,9 @@
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz",
"integrity": "sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.53.tgz",
"integrity": "sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==",
"cpu": [
"riscv64"
],
@ -524,9 +541,9 @@
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz",
"integrity": "sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.53.tgz",
"integrity": "sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==",
"cpu": [
"s390x"
],
@ -540,9 +557,9 @@
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz",
"integrity": "sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.53.tgz",
"integrity": "sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==",
"cpu": [
"x64"
],
@ -556,9 +573,9 @@
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz",
"integrity": "sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.53.tgz",
"integrity": "sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==",
"cpu": [
"x64"
],
@ -572,9 +589,9 @@
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz",
"integrity": "sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.53.tgz",
"integrity": "sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==",
"cpu": [
"x64"
],
@ -588,9 +605,9 @@
}
},
"node_modules/esbuild-windows-32": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz",
"integrity": "sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.53.tgz",
"integrity": "sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==",
"cpu": [
"ia32"
],
@ -604,9 +621,9 @@
}
},
"node_modules/esbuild-windows-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz",
"integrity": "sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.53.tgz",
"integrity": "sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==",
"cpu": [
"x64"
],
@ -620,9 +637,9 @@
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz",
"integrity": "sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.53.tgz",
"integrity": "sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==",
"cpu": [
"arm64"
],
@ -722,9 +739,9 @@
}
},
"node_modules/is-core-module": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
"integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
@ -937,9 +954,9 @@
}
},
"node_modules/sass": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz",
"integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.2.tgz",
"integrity": "sha512-wbVV26sejsCIbBScZZtNkvnrB/bVCQ8hSlZ01D9nzsVh9zLqCkWrlpvTb3YEb6xsuNi9cx75hncqwikHFSz7tw==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
@ -1085,9 +1102,9 @@
}
},
"node_modules/w3c-keyname": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.5.tgz",
"integrity": "sha512-WJrK7i6w+ULuZsGscCezbCH4Aev5U3xY87vnSimzzEgPQhb0Sa0a1rE3c2jtEwrFtSfi61Jefw3jI5/DD/3jbQ==",
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
"integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==",
"dev": true
}
},
@ -1151,15 +1168,15 @@
}
},
"@codemirror/state": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.0.tgz",
"integrity": "sha512-qbUr94DZTe6/V1VS7LDLz11rM/1t/nJxR1El4I6UaxDEdc0aZZvq6JCLJWiRmUf95NRAnDH6fhXn+PWp9wGCIg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.1.tgz",
"integrity": "sha512-2s+aXsxmAwnR3Rd+JDHPG/1lw0YsA9PEwl7Re88gHJHGfxyfEzKBmsN4rr53RyPIR4lzbbhJX0DCq0WlqlBIRw==",
"dev": true
},
"@codemirror/view": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.1.2.tgz",
"integrity": "sha512-puUydfKwfmOo+ixtuB+uN/ZpcteEYSnpjHmMaow1sOQhNICsKtGBup3i9ybVqyzDagARRYzSHTWjbdeHqmn31w==",
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.1.4.tgz",
"integrity": "sha512-pekgUX+0hL4ri2JV7/bu7jhhwOgOhU1eRc1/ZyAQYCWcCI4TPB1qLrPE3cD/qW9yjBcYiN9MN0XI1tjK7Yw05Q==",
"dev": true,
"requires": {
"@codemirror/state": "^6.0.0",
@ -1167,6 +1184,13 @@
"w3c-keyname": "^2.2.4"
}
},
"@esbuild/linux-loong64": {
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.53.tgz",
"integrity": "sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==",
"dev": true,
"optional": true
},
"@lezer/common": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.0.tgz",
@ -1183,9 +1207,9 @@
}
},
"@lezer/lr": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.0.tgz",
"integrity": "sha512-TgEpfm9br2SX8JwtwKT8HsQZKuFkLRg6g+IRxObk9nVKQLKnkP3oMh+QGcTBL9GQsfQ2ADtKPbj2iGSMf3ytiA==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.1.tgz",
"integrity": "sha512-RpHRs+Q+5tPsXtobSfSeRFRAnTRD0e4bApDvo74O+JiaWq9812x5S8WgftNX67owdaTQXCB5E8XZGALo4Wt77A==",
"dev": true,
"requires": {
"@lezer/common": "^1.0.0"
@ -1241,15 +1265,15 @@
}
},
"chart.js": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.8.2.tgz",
"integrity": "sha512-7rqSlHWMUKFyBDOJvmFGW2lxULtcwaPLegDjX/Nu5j6QybY+GCiQkEY+6cqHw62S5tcwXMD8Y+H5OBGoR7d+ZQ==",
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
"dev": true
},
"chartjs-adapter-luxon": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.1.0.tgz",
"integrity": "sha512-CS+xBWEyXYVLBZ3dSY/MwlSXhz8er4JjkApazY84ft/++oOLsmkt6TaXBCsUFudum7QdoYmpxiL/gSp20+emkw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.2.0.tgz",
"integrity": "sha512-h1lEns7+8cUN/Dmk24dhrT9hpAimKImQxzHpILqXn2kocdzj9b/fDlBa8v8/OMq5rq0uZEx/NV1WpByH4l2/Rw==",
"dev": true,
"requires": {}
},
@ -1291,170 +1315,171 @@
"dev": true
},
"esbuild": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz",
"integrity": "sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.53.tgz",
"integrity": "sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==",
"dev": true,
"requires": {
"esbuild-android-64": "0.14.51",
"esbuild-android-arm64": "0.14.51",
"esbuild-darwin-64": "0.14.51",
"esbuild-darwin-arm64": "0.14.51",
"esbuild-freebsd-64": "0.14.51",
"esbuild-freebsd-arm64": "0.14.51",
"esbuild-linux-32": "0.14.51",
"esbuild-linux-64": "0.14.51",
"esbuild-linux-arm": "0.14.51",
"esbuild-linux-arm64": "0.14.51",
"esbuild-linux-mips64le": "0.14.51",
"esbuild-linux-ppc64le": "0.14.51",
"esbuild-linux-riscv64": "0.14.51",
"esbuild-linux-s390x": "0.14.51",
"esbuild-netbsd-64": "0.14.51",
"esbuild-openbsd-64": "0.14.51",
"esbuild-sunos-64": "0.14.51",
"esbuild-windows-32": "0.14.51",
"esbuild-windows-64": "0.14.51",
"esbuild-windows-arm64": "0.14.51"
"@esbuild/linux-loong64": "0.14.53",
"esbuild-android-64": "0.14.53",
"esbuild-android-arm64": "0.14.53",
"esbuild-darwin-64": "0.14.53",
"esbuild-darwin-arm64": "0.14.53",
"esbuild-freebsd-64": "0.14.53",
"esbuild-freebsd-arm64": "0.14.53",
"esbuild-linux-32": "0.14.53",
"esbuild-linux-64": "0.14.53",
"esbuild-linux-arm": "0.14.53",
"esbuild-linux-arm64": "0.14.53",
"esbuild-linux-mips64le": "0.14.53",
"esbuild-linux-ppc64le": "0.14.53",
"esbuild-linux-riscv64": "0.14.53",
"esbuild-linux-s390x": "0.14.53",
"esbuild-netbsd-64": "0.14.53",
"esbuild-openbsd-64": "0.14.53",
"esbuild-sunos-64": "0.14.53",
"esbuild-windows-32": "0.14.53",
"esbuild-windows-64": "0.14.53",
"esbuild-windows-arm64": "0.14.53"
}
},
"esbuild-android-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz",
"integrity": "sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.53.tgz",
"integrity": "sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz",
"integrity": "sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.53.tgz",
"integrity": "sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz",
"integrity": "sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.53.tgz",
"integrity": "sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz",
"integrity": "sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.53.tgz",
"integrity": "sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz",
"integrity": "sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.53.tgz",
"integrity": "sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz",
"integrity": "sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.53.tgz",
"integrity": "sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz",
"integrity": "sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.53.tgz",
"integrity": "sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz",
"integrity": "sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.53.tgz",
"integrity": "sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz",
"integrity": "sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.53.tgz",
"integrity": "sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz",
"integrity": "sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.53.tgz",
"integrity": "sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz",
"integrity": "sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.53.tgz",
"integrity": "sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz",
"integrity": "sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.53.tgz",
"integrity": "sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz",
"integrity": "sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.53.tgz",
"integrity": "sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz",
"integrity": "sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.53.tgz",
"integrity": "sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz",
"integrity": "sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.53.tgz",
"integrity": "sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz",
"integrity": "sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.53.tgz",
"integrity": "sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz",
"integrity": "sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.53.tgz",
"integrity": "sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz",
"integrity": "sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.53.tgz",
"integrity": "sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz",
"integrity": "sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.53.tgz",
"integrity": "sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.14.51",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz",
"integrity": "sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==",
"version": "0.14.53",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.53.tgz",
"integrity": "sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==",
"dev": true,
"optional": true
},
@ -1526,9 +1551,9 @@
}
},
"is-core-module": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
"integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
"dev": true,
"requires": {
"has": "^1.0.3"
@ -1671,9 +1696,9 @@
}
},
"sass": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz",
"integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.2.tgz",
"integrity": "sha512-wbVV26sejsCIbBScZZtNkvnrB/bVCQ8hSlZ01D9nzsVh9zLqCkWrlpvTb3YEb6xsuNi9cx75hncqwikHFSz7tw==",
"dev": true,
"requires": {
"chokidar": ">=3.0.0 <4.0.0",
@ -1759,9 +1784,9 @@
}
},
"w3c-keyname": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.5.tgz",
"integrity": "sha512-WJrK7i6w+ULuZsGscCezbCH4Aev5U3xY87vnSimzzEgPQhb0Sa0a1rE3c2jtEwrFtSfi61Jefw3jI5/DD/3jbQ==",
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
"integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==",
"dev": true
}
}

View File

@ -0,0 +1,56 @@
// https://github.com/google/diff-match-patch
var diff_match_patch=function(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=.5;this.Patch_Margin=4;this.Match_MaxBits=32},DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;diff_match_patch.Diff=function(a,b){this[0]=a;this[1]=b};diff_match_patch.Diff.prototype.length=2;diff_match_patch.Diff.prototype.toString=function(){return this[0]+","+this[1]};
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[new diff_match_patch.Diff(DIFF_EQUAL,a)]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);f=this.diff_commonSuffix(a,b);var g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,
b.length-f);a=this.diff_compute_(a,b,e,d);c&&a.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));g&&a.push(new diff_match_patch.Diff(DIFF_EQUAL,g));this.diff_cleanupMerge(a);return a};
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[new diff_match_patch.Diff(DIFF_INSERT,b)];if(!b)return[new diff_match_patch.Diff(DIFF_DELETE,a)];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[new diff_match_patch.Diff(DIFF_INSERT,e.substring(0,g)),new diff_match_patch.Diff(DIFF_EQUAL,f),new diff_match_patch.Diff(DIFF_INSERT,e.substring(g+f.length))],a.length>b.length&&(c[0][0]=c[2][0]=DIFF_DELETE),c):1==f.length?[new diff_match_patch.Diff(DIFF_DELETE,
a),new diff_match_patch.Diff(DIFF_INSERT,b)]:(e=this.diff_halfMatch_(a,b))?(b=e[1],f=e[3],a=e[4],e=this.diff_main(e[0],e[2],c,d),c=this.diff_main(b,f,c,d),e.concat([new diff_match_patch.Diff(DIFF_EQUAL,a)],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,d):this.diff_bisect_(a,b,d)};
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case DIFF_INSERT:e++;g+=a[b][1];break;case DIFF_DELETE:d++;f+=a[b][1];break;case DIFF_EQUAL:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,
0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=2*f,h=Array(g),l=Array(g),k=0;k<g;k++)h[k]=-1,l[k]=-1;h[f+1]=0;l[f+1]=0;k=d-e;for(var m=0!=k%2,p=0,x=0,w=0,q=0,t=0;t<f&&!((new Date).getTime()>c);t++){for(var v=-t+p;v<=t-x;v+=2){var n=f+v;var r=v==-t||v!=t&&h[n-1]<h[n+1]?h[n+1]:h[n-1]+1;for(var y=r-v;r<d&&y<e&&a.charAt(r)==b.charAt(y);)r++,y++;h[n]=r;if(r>d)x+=2;else if(y>e)p+=2;else if(m&&(n=f+k-v,0<=n&&n<g&&-1!=l[n])){var u=d-l[n];if(r>=
u)return this.diff_bisectSplit_(a,b,r,y,c)}}for(v=-t+w;v<=t-q;v+=2){n=f+v;u=v==-t||v!=t&&l[n-1]<l[n+1]?l[n+1]:l[n-1]+1;for(r=u-v;u<d&&r<e&&a.charAt(d-u-1)==b.charAt(e-r-1);)u++,r++;l[n]=u;if(u>d)q+=2;else if(r>e)w+=2;else if(!m&&(n=f+k-v,0<=n&&n<g&&-1!=h[n]&&(r=h[n],y=f+r-n,u=d-u,r>=u)))return this.diff_bisectSplit_(a,b,r,y,c)}}return[new diff_match_patch.Diff(DIFF_DELETE,a),new diff_match_patch.Diff(DIFF_INSERT,b)]};
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,g=-1,h=d.length;g<a.length-1;){g=a.indexOf("\n",c);-1==g&&(g=a.length-1);var l=a.substring(c,g+1);(e.hasOwnProperty?e.hasOwnProperty(l):void 0!==e[l])?b+=String.fromCharCode(e[l]):(h==f&&(l=a.substring(c),g=a.length),b+=String.fromCharCode(h),e[l]=h,d[h++]=l);c=g+1}return b}var d=[],e={};d[0]="";var f=4E4,g=c(a);f=65535;var h=c(b);return{chars1:g,chars2:h,lineArray:d}};
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;d=0;for(var e=1;;){var f=a.substring(c-e);f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,k,l,m;-1!=(e=b.indexOf(d,e+1));){var p=f.diff_commonPrefix(a.substring(c),b.substring(e)),u=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<u+p&&(g=b.substring(e-u,e)+b.substring(e,e+p),h=a.substring(0,c-u),k=a.substring(c+p),l=b.substring(0,e-u),m=b.substring(e+p))}return 2*g.length>=a.length?[h,k,l,m,g]:null}if(0>=this.Diff_Timeout)return null;
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4));d=c(d,e,Math.ceil(d.length/2));if(g||d)g=d?g?g[4].length>d[4].length?g:d:d:g;else return null;if(a.length>b.length){d=g[0];e=g[1];var h=g[2];var l=g[3]}else h=g[0],l=g[1],d=g[2],e=g[3];return[d,e,h,l,g[4]]};
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,l=0,k=0;f<a.length;)a[f][0]==DIFF_EQUAL?(c[d++]=f,g=l,h=k,k=l=0,e=a[f][1]):(a[f][0]==DIFF_INSERT?l+=a[f][1].length:k+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(l,k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,d--,f=0<d?c[d-1]:-1,k=l=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<
a.length;){if(a[f-1][0]==DIFF_DELETE&&a[f][0]==DIFF_INSERT){b=a[f-1][1];c=a[f][1];d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,c.substring(0,d))),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,b.substring(0,e))),a[f-1][0]=DIFF_INSERT,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=DIFF_DELETE,
a[f+1][1]=b.substring(e),f++;f++}f++}};
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_);c=g&&c.match(diff_match_patch.linebreakRegex_);d=h&&d.match(diff_match_patch.linebreakRegex_);var k=c&&a.match(diff_match_patch.blanklineEndRegex_),l=d&&b.match(diff_match_patch.blanklineStartRegex_);
return k||l?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(a[c-1][0]==DIFF_EQUAL&&a[c+1][0]==DIFF_EQUAL){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g){var h=e.substring(e.length-g);d=d.substring(0,d.length-g);e=h+e.substring(0,e.length-g);f=h+f}g=d;h=e;for(var l=f,k=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){d+=e.charAt(0);e=e.substring(1)+f.charAt(0);f=f.substring(1);var m=b(d,e)+b(e,f);m>=k&&(k=m,g=d,h=e,l=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-
1,1),c--),a[c][1]=h,l?a[c+1][1]=l:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,l=!1,k=!1;f<a.length;)a[f][0]==DIFF_EQUAL?(a[f][1].length<this.Diff_EditCost&&(l||k)?(c[d++]=f,g=l,h=k,e=a[f][1]):(d=0,e=null),l=k=!1):(a[f][0]==DIFF_DELETE?k=!0:l=!0,e&&(g&&h&&l&&k||e.length<this.Diff_EditCost/2&&3==g+h+l+k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,e=null,g&&h?(l=k=!0,d=0):(d--,f=0<d?c[d-1]:-1,l=k=!1),b=!0)),f++;b&&this.diff_cleanupMerge(a)};
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case DIFF_INSERT:d++;f+=a[b][1];b++;break;case DIFF_DELETE:c++;e+=a[b][1];b++;break;case DIFF_EQUAL:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&a[b-c-d-1][0]==DIFF_EQUAL?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,new diff_match_patch.Diff(DIFF_EQUAL,f.substring(0,g))),b++),f=f.substring(g),e=e.substring(g)),
g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-g),e=e.substring(0,e.length-g))),b-=c+d,a.splice(b,c+d),e.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_DELETE,e)),b++),f.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_INSERT,f)),b++),b++):0!==b&&a[b-1][0]==DIFF_EQUAL?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)a[b-1][0]==DIFF_EQUAL&&a[b+1][0]==DIFF_EQUAL&&(a[b][1].substring(a[b][1].length-
a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};
diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){a[g][0]!==DIFF_INSERT&&(c+=a[g][1].length);a[g][0]!==DIFF_DELETE&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&a[g][0]===DIFF_DELETE?f:f+(b-e)};
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],l=a[g][1].replace(c,"&amp;").replace(d,"&lt;").replace(e,"&gt;").replace(f,"&para;<br>");switch(h){case DIFF_INSERT:b[g]='<ins style="background:#e6ffe6;">'+l+"</ins>";break;case DIFF_DELETE:b[g]='<del style="background:#ffe6e6;">'+l+"</del>";break;case DIFF_EQUAL:b[g]="<span>"+l+"</span>"}}return b.join("")};
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_INSERT&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_DELETE&&(b[c]=a[c][1]);return b.join("")};
diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][1];switch(a[e][0]){case DIFF_INSERT:c+=f.length;break;case DIFF_DELETE:d+=f.length;break;case DIFF_EQUAL:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case DIFF_INSERT:b[c]="+"+encodeURI(a[c][1]);break;case DIFF_DELETE:b[c]="-"+a[c][1].length;break;case DIFF_EQUAL:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=new diff_match_patch.Diff(DIFF_INSERT,decodeURI(h))}catch(k){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var l=parseInt(h,10);if(isNaN(l)||0>l)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=l);"="==f[g].charAt(0)?c[d++]=new diff_match_patch.Diff(DIFF_EQUAL,h):c[d++]=
new diff_match_patch.Diff(DIFF_DELETE,h);break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return f.Match_Distance?e+g/f.Match_Distance:g?1:e}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));var l=1<<b.length-1;h=-1;for(var k,m,p=b.length+a.length,x,w=0;w<b.length;w++){k=0;for(m=p;k<m;)d(w,
c+m)<=g?k=m:p=m,m=Math.floor((p-k)/2+k);p=m;k=Math.max(1,c-m+1);var q=Math.min(c+m,a.length)+b.length;m=Array(q+2);for(m[q+1]=(1<<w)-1;q>=k;q--){var t=e[a.charAt(q-1)];m[q]=0===w?(m[q+1]<<1|1)&t:(m[q+1]<<1|1)&t|(x[q+1]|x[q])<<1|1|x[q+1];if(m[q]&l&&(t=d(w,q-1),t<=g))if(g=t,h=q-1,h>c)k=Math.max(1,2*c-h);else break}if(d(w+1,c)>g)break;x=m}return h};
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){if(null===a.start2)throw Error("patch not initialized");for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));(d=b.substring(a.start2+a.length1,
a.start2+a.length1+d))&&a.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,d));a.start1-=c.length;a.start2-=c.length;a.length1+=c.length+d.length;a.length2+=c.length+d.length}};
diff_match_patch.prototype.patch_make=function(a,b,c){if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c){var d=a;b=this.diff_main(d,b,!0);2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b))}else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,l=0;l<b.length;l++){var k=b[l][0],m=b[l][1];e||k===DIFF_EQUAL||(a.start1=f,a.start2=g);switch(k){case DIFF_INSERT:a.diffs[e++]=b[l];a.length2+=m.length;d=d.substring(0,g)+m+d.substring(g);break;case DIFF_DELETE:a.length1+=m.length;a.diffs[e++]=b[l];d=d.substring(0,g)+d.substring(g+m.length);break;case DIFF_EQUAL:m.length<=2*this.Patch_Margin&&e&&b.length!=l+1?(a.diffs[e++]=b[l],a.length1+=m.length,a.length2+=m.length):
m.length>=2*this.Patch_Margin&&e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}k!==DIFF_INSERT&&(f+=m.length);k!==DIFF_DELETE&&(g+=m.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};
diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=new diff_match_patch.Diff(d.diffs[f][0],d.diffs[f][1]);e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),l=-1;if(h.length>this.Match_MaxBits){var k=this.match_main(b,h.substring(0,this.Match_MaxBits),g);-1!=k&&(l=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==l||k>=l)&&(k=-1)}else k=this.match_main(b,h,
g);if(-1==k)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=k-g,g=-1==l?b.substring(k,k+h.length):b.substring(k,l+this.Match_MaxBits),h==g)b=b.substring(0,k)+this.diff_text2(a[f].diffs)+b.substring(k+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);h=0;var m;for(l=0;l<a[f].diffs.length;l++){var p=a[f].diffs[l];p[0]!==DIFF_EQUAL&&(m=this.diff_xIndex(g,h));p[0]===
DIFF_INSERT?b=b.substring(0,k+m)+p[1]+b.substring(k+m):p[0]===DIFF_DELETE&&(b=b.substring(0,k+m)+b.substring(k+this.diff_xIndex(g,h+p[1].length)));p[0]!==DIFF_DELETE&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;d=a[0];var e=d.diffs;if(0==e.length||e[0][0]!=DIFF_EQUAL)e.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;
0==e.length||e[e.length-1][0]!=DIFF_EQUAL?(e.push(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,l=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g)));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){g=d.diffs[0][0];var k=d.diffs[0][1];g===DIFF_INSERT?(h.length2+=
k.length,f+=k.length,h.diffs.push(d.diffs.shift()),l=!1):g===DIFF_DELETE&&1==h.diffs.length&&h.diffs[0][0]==DIFF_EQUAL&&k.length>2*b?(h.length1+=k.length,e+=k.length,l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),d.diffs.shift()):(k=k.substring(0,b-h.length1-this.Patch_Margin),h.length1+=k.length,e+=k.length,g===DIFF_EQUAL?(h.length2+=k.length,f+=k.length):l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),k==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(k.length))}g=this.diff_text2(h.diffs);
g=g.substring(g.length-this.Patch_Margin);k=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==k&&(h.length1+=k.length,h.length2+=k.length,0!==h.diffs.length&&h.diffs[h.diffs.length-1][0]===DIFF_EQUAL?h.diffs[h.diffs.length-1][1]+=k:h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,k)));l||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_DELETE,g));else if("+"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_INSERT,g));else if(" "==e)f.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g));else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};
diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
diff_match_patch.patch_obj.prototype.toString=function(){for(var a=["@@ -"+(0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1)+" +"+(0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2)+" @@\n"],b,c=0;c<this.diffs.length;c++){switch(this.diffs[c][0]){case DIFF_INSERT:b="+";break;case DIFF_DELETE:b="-";break;case DIFF_EQUAL:b=" "}a[c+1]=b+encodeURI(this.diffs[c][1])+"\n"}return a.join("").replace(/%20/g," ")};
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=DIFF_DELETE;this.DIFF_INSERT=DIFF_INSERT;this.DIFF_EQUAL=DIFF_EQUAL;

View File

@ -143,9 +143,9 @@
<div class="app-body">
<Router {routes} on:routeLoading={handleRouteLoading} on:conditionsFailed={handleRouteFailure} />
<Toasts />
</div>
</div>
<Toasts />
<Confirmation />

View File

@ -4,6 +4,9 @@
import "prismjs/components/prism-dart.js";
import "@/scss/prism_light.scss";
let classes = "";
export { classes as class }; // export reserved keyword
export let content = "";
export let language = "javascript"; // javascript, html
@ -28,7 +31,7 @@
}
</script>
<div class="code-wrapper prism-light">
<div class="code-wrapper prism-light {classes}">
<code>{@html formattedContent}</code>
</div>
@ -43,6 +46,9 @@
.code-wrapper {
display: block;
width: 100%;
max-height: 100%;
overflow: auto; /* fallback */
overflow: overlay;
}
.prism-light code {
color: var(--txtPrimaryColor);

View File

@ -0,0 +1,352 @@
<script>
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import Field from "@/components/base/Field.svelte";
import CodeBlock from "@/components/base/CodeBlock.svelte";
import { onMount } from "svelte";
const uniqueId = "exports_" + CommonHelper.randomString(5);
let collections = [];
let isLoadingCollections = false;
loadCollections();
async function loadCollections() {
isLoadingCollections = true;
try {
collections = await ApiClient.collections.getFullList(100, {
$cancelKey: uniqueId,
});
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoadingCollections = false;
}
let oldSchema = "";
let newSchema = "";
function diff_prettyHtml(diffs, showInsert) {
const html = [];
const pattern_amp = /&/g;
const pattern_lt = /</g;
const pattern_gt = />/g;
const pattern_para = /\n/g;
for (let x = 0; x < diffs.length; x++) {
let op = diffs[x][0]; // Operation (insert, delete, equal)
let data = diffs[x][1]; // Text of change.
let text = data
.replace(pattern_amp, "&amp;")
.replace(pattern_lt, "&lt;")
.replace(pattern_gt, "&gt;")
.replace(pattern_para, "<br>");
// text = CommonHelper.stripTags(text);
switch (op) {
case DIFF_INSERT:
if (showInsert) {
html[x] = '<ins class="block">' + text + "</ins>";
}
break;
case DIFF_DELETE:
if (!showInsert) {
html[x] = '<del class="block">' + text + "</del>";
}
break;
case DIFF_EQUAL:
html[x] = "<span>" + text + "</span>";
break;
}
}
return html.join("");
}
onMount(() => {
var dmp = new diff_match_patch();
const text1 = [
{
id: "zwWlxR46txtoAwx",
created: "2022-08-01 17:32:24.329",
updated: "2022-08-04 10:19:57.248",
name: "profia sdles",
system: true,
listRule: "userId = @request.user.id",
viewRule: "userId = @request.user.id",
createRule: "userId = @request.user.id",
updateRule: "userId = @request.user.id",
deleteRule: null,
schema: [
{
id: "nsght7oy",
name: "userId",
type: "user",
system: true,
required: true,
unique: true,
options: {
maxSelect: 1,
cascadeDelete: true,
},
},
{
id: "atpc4yjm",
name: "name",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
id: "akb4s9de",
name: "avatar",
type: "file",
system: false,
required: false,
unique: false,
options: {
maxSelect: 1,
maxSize: 5242880,
mimeTypes: ["image/jpg", "image/jpeg", "image/png", "image/svg+xml", "image/gif"],
thumbs: null,
},
},
],
},
{
id: "IV8FbE78jmXF56d",
created: "",
updated: "2022-08-04 10:21:54.100",
name: "abc",
system: false,
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
schema: [
{
id: "t2pukeas",
name: "demo",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
id: "dddddd",
name: "aaaa",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
id: "squmamtm",
name: "test",
type: "date",
system: false,
required: false,
unique: false,
options: {
min: "",
max: "",
},
},
],
},
];
const text2 = [
{
id: "zwWlxR46txtoAwx",
created: "2022-08-01 17:32:24.329",
updated: "2022-08-04 10:19:57.248",
name: "Demo",
system: true,
listRule: "userId = @request.user.id",
viewRule: "userId = @request.user.id",
createRule: "userId = @request.user.id",
updateRule: "userId = @request.user.id",
deleteRule: null,
schema: [
{
id: "nsght7oy",
name: "userId",
type: "user",
system: true,
required: true,
unique: true,
options: {
maxSelect: 1,
cascadeDelete: true,
},
},
{
id: "atpc4yjm",
name: "name",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
id: "akb4s9de",
name: "avatar",
type: "file",
system: false,
required: false,
unique: true,
options: {
maxSelect: 1,
maxSize: 5242880,
mimeTypes: ["image/jpg", "image/jpeg", "image/png", "image/svg+xml", "image/gif"],
thumbs: null,
},
},
],
},
{
id: "IV8FbE78jmXF56d",
created: "",
updated: "2022-08-04 10:21:54.100",
name: "abc",
system: false,
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
schema: [
{
id: "t2pukeas",
name: "demo",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
id: "dddddd",
name: "aaaa",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
{
id: "squmamtm",
name: "test",
type: "date",
system: false,
required: false,
unique: false,
options: {
min: "",
max: "",
},
},
],
},
{
id: "GGACt8sa1tcJp7T",
created: "2022-08-04 10:22:15.871",
updated: "2022-08-04 10:22:15.871",
name: "asdasd",
system: true,
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
schema: [
{
id: "0eklwfvl",
name: "field",
type: "text",
system: false,
required: false,
unique: false,
options: {
min: null,
max: null,
pattern: "",
},
},
],
},
];
// var diffs = dmp.diff_main(JSON.stringify(text1, null, 2), JSON.stringify(text2, null, 2));
var a = dmp.diff_linesToChars_(JSON.stringify(text1, null, 2), JSON.stringify(text2, null, 2));
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmp.diff_main(lineText1, lineText2, false);
dmp.diff_charsToLines_(diffs, lineArray);
oldSchema = diff_prettyHtml(diffs, false);
newSchema = diff_prettyHtml(diffs, true);
});
</script>
<br />
<div class="grid">
<div class="col-6">
<code>
{@html oldSchema}
</code>
</div>
<div class="col-6">
<code>
{@html newSchema}
</code>
</div>
</div>
<style lang="scss">
.collections-list {
column-count: 2;
column-gap: var(--baseSpacing);
}
code {
display: block;
width: 100%;
overflow: auto;
padding: var(--xsSpacing);
white-space: pre;
background: var(--baseAlt1Color);
}
</style>

View File

@ -0,0 +1,29 @@
<script>
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
import CollectionsExportForm from "@/components/collections/CollectionsExportForm.svelte";
let overlayPanel;
export function show() {
return overlayPanel?.show();
}
export function hide() {
return overlayPanel?.hide();
}
</script>
<OverlayPanel
bind:this={overlayPanel}
class="overlay-panel-xl collections-export-panel"
on:hide
on:show
popup
active
>
<svelte:fragment slot="header">
<h4>Export collections schema</h4>
</svelte:fragment>
<CollectionsExportForm />
</OverlayPanel>

View File

@ -13,6 +13,7 @@
import CollectionsSidebar from "@/components/collections/CollectionsSidebar.svelte";
import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte";
import CollectionDocsPanel from "@/components/collections/docs/CollectionDocsPanel.svelte";
import CollectionsExportPanel from "@/components/collections/CollectionsExportPanel.svelte";
import RecordUpsertPanel from "@/components/records/RecordUpsertPanel.svelte";
import RecordsList from "@/components/records/RecordsList.svelte";
@ -20,6 +21,7 @@
const queryParams = new URLSearchParams($querystring);
let collectionsExportPanel;
let collectionUpsertPanel;
let collectionDocsPanel;
let recordPanel;
@ -84,16 +86,18 @@
<div class="breadcrumb-item">{$activeCollection.name}</div>
</nav>
<button
type="button"
class="btn btn-secondary btn-circle"
use:tooltip={{ text: "Edit collection", position: "right" }}
on:click={() => collectionUpsertPanel?.show($activeCollection)}
>
<i class="ri-settings-4-line" />
</button>
<div class="inline-flex gap-5">
<button
type="button"
class="btn btn-secondary btn-circle"
use:tooltip={{ text: "Edit collection", position: "right" }}
on:click={() => collectionUpsertPanel?.show($activeCollection)}
>
<i class="ri-settings-4-line" />
</button>
<RefreshButton on:refresh={() => recordsList?.load()} />
<RefreshButton on:refresh={() => recordsList?.load()} />
</div>
<div class="btns-group">
<button
@ -128,6 +132,8 @@
</main>
{/if}
<CollectionsExportPanel bind:this={collectionsExportPanel} />
<CollectionUpsertPanel bind:this={collectionUpsertPanel} />
<CollectionDocsPanel bind:this={collectionDocsPanel} />

View File

@ -0,0 +1,105 @@
<script>
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { pageTitle } from "@/stores/app";
import { addInfoToast } from "@/stores/toasts";
import CodeBlock from "@/components/base/CodeBlock.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
$pageTitle = "Export collections";
const uniqueId = "export_" + CommonHelper.randomString(5);
let collections = [];
let isLoadingCollections = false;
$: schema = JSON.stringify(collections, null, 2);
loadCollections();
async function loadCollections() {
isLoadingCollections = true;
try {
collections = await ApiClient.collections.getFullList(100, {
$cancelKey: uniqueId,
});
// delete timestamps
for (let collection of collections) {
delete collection.created;
delete collection.updated;
}
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoadingCollections = false;
}
function download() {
CommonHelper.downloadJson(collections, "pb_schema");
}
function copy() {
CommonHelper.copyToClipboard(schema);
addInfoToast("The schema was copied to your clipboard!", 3000);
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">{$pageTitle}</div>
</nav>
</header>
<div class="wrapper">
<div class="panel">
{#if isLoadingCollections}
<div class="loader" />
{:else}
<div class="content txt-xl m-b-base">
<p>
Below you'll find your current collections schema that you could import later in
another PocketBase environment.
</p>
</div>
<div class="export-preview">
<button
type="button"
class="btn btn-sm btn-secondary fade copy-schema"
on:click={() => copy()}
>
<span class="txt">Copy</span>
</button>
<CodeBlock content={schema} />
</div>
<div class="flex m-t-base">
<div class="flex-fill" />
<button type="button" class="btn btn-expanded" on:click={() => download()}>
<i class="ri-download-line" />
<span class="txt">Download as JSON</span>
</button>
</div>
{/if}
</div>
</div>
</main>
<style>
.export-preview {
position: relative;
height: 500px;
}
.export-preview .copy-schema {
position: absolute;
right: 15px;
top: 15px;
}
</style>

View File

@ -0,0 +1,221 @@
<script>
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import { pageTitle } from "@/stores/app";
import { addInfoToast, addErrorToast } from "@/stores/toasts";
import Field from "@/components/base/Field.svelte";
import CodeBlock from "@/components/base/CodeBlock.svelte";
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
$pageTitle = "Import collections";
let uniquePageId = "import_" + CommonHelper.randomString(5);
let fileInput;
let schema = "";
let isImporting = false;
let isLoadingFile = false;
let newCollections = [];
let oldCollections = [];
let isLoadingOldCollections = false;
$: if (typeof schema !== "undefined") {
loadNewCollections(schema);
}
$: isValid =
!!schema &&
newCollections.length &&
newCollections.length === newCollections.filter((item) => !!item.id && !!item.name).length;
$: canImport = isValid && !isLoadingOldCollections;
$: collectionsToDelete = oldCollections.filter((collection) => {
return !CommonHelper.findByKey(newCollections, "id", collection.id);
});
$: collectionsToAdd = newCollections.filter((collection) => {
return !CommonHelper.findByKey(oldCollections, "id", collection.id);
});
$: collectionsToModify = newCollections.filter((newCollection) => {
const oldCollection = CommonHelper.findByKey(oldCollections, "id", newCollection.id);
if (!oldCollection?.id) {
return false;
}
return JSON.stringify(oldCollection) !== JSON.stringify(newCollection);
});
loadOldCollections();
async function loadOldCollections() {
isLoadingOldCollections = true;
try {
oldCollections = await ApiClient.collections.getFullList(100, {
$cancelKey: uniquePageId,
});
// delete timestamps
for (let collection of oldCollections) {
delete collection.created;
delete collection.updated;
}
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isLoadingOldCollections = false;
}
function loadNewCollections() {
newCollections = [];
try {
newCollections = JSON.parse(schema);
} catch (_) {}
if (!Array.isArray(newCollections)) {
newCollections = [];
}
// delete timestamps
for (let collection of newCollections) {
delete collection.created;
delete collection.updated;
}
}
function loadFile(file) {
isLoadingFile = true;
const reader = new FileReader();
reader.onload = (event) => {
schema = event.target.result;
isLoadingFile = false;
fileInput.value = ""; // reset
};
reader.onerror = (err) => {
console.log(err);
addErrorToast("Failed to load the imported JSON.");
isLoadingFile = false;
fileInput.value = ""; // reset
};
reader.readAsText(file);
}
function submitImport() {
isImporting = true;
try {
const newCollections = JSON.parse(schema);
ApiClient.collections.import(newCollections);
} catch (err) {
ApiClient.errorResponseHandler(err);
}
isImporting = false;
}
</script>
<SettingsSidebar />
<main class="page-wrapper">
<header class="page-header">
<nav class="breadcrumbs">
<div class="breadcrumb-item">Settings</div>
<div class="breadcrumb-item">{$pageTitle}</div>
</nav>
</header>
<div class="wrapper">
<div class="panel">
<div class="content txt-xl m-b-base">
<input
bind:this={fileInput}
type="file"
class="hidden"
accept=".json"
on:change={() => {
if (fileInput.files.length) {
loadFile(fileInput.files[0]);
}
}}
/>
<p>
Paste below the collections schema you want to import or
<button
class="btn btn-outline btn-sm"
class:btn-loading={isLoadingFile}
on:click={() => {
fileInput.click();
}}
>
<span class="txt">Import from JSON file</span>
</button>
</p>
</div>
<Field class="form-field {!isValid ? 'field-error' : ''}" name="collections" let:uniqueId>
<label for={uniqueId}>Collections schema</label>
<textarea
id={uniqueId}
class="json-editor"
spellcheck="false"
rows="20"
required
bind:value={schema}
/>
{#if !!schema && !isValid}
<div class="help-block help-block-error">Invalid collections schema.</div>
{/if}
</Field>
<div class="section-title">Detected changes</div>
<p>No changes to your current collections schema were found.</p>
{#each collectionsToDelete as collection (collection.id)}
Delete {collection.name}
<br />
{/each}
{#each collectionsToModify as collection (collection.id)}
Modify {collection.name}
<br />
{/each}
{#each collectionsToAdd as collection (collection.id)}
Add {collection.name}
<br />
{/each}
<div class="flex m-t-base">
<div class="flex-fill" />
<button
type="button"
class="btn btn-expanded"
class:btn-loading={isImporting}
disabled={!canImport}
on:click={() => submitImport()}
>
<span class="txt">Import</span>
</button>
</div>
</div>
</div>
</main>
<style>
.json-editor {
font-size: 15px;
line-height: 1.379rem;
font-family: var(--monospaceFontFamily);
}
</style>

View File

@ -29,6 +29,26 @@
<span class="txt">Files storage</span>
</a>
<div class="sidebar-title">Sync</div>
<a
href="/settings/export-collections"
class="sidebar-list-item"
use:active={{ path: "/settings/export-collections/?.*" }}
use:link
>
<i class="ri-uninstall-line" />
<span class="txt">Export collections</span>
</a>
<a
href="/settings/import-collections"
class="sidebar-list-item"
use:active={{ path: "/settings/import-collections/?.*" }}
use:link
>
<i class="ri-install-line" />
<span class="txt">Import collections</span>
</a>
<div class="sidebar-title">Authentication</div>
<a
href="/settings/auth-providers"

View File

@ -55,10 +55,11 @@
clearList();
}
return ApiClient.users.getList(page, 50, {
sort: sort || "-created",
filter: filter,
})
return ApiClient.users
.getList(page, 50, {
sort: sort || "-created",
filter: filter,
})
.then((result) => {
isLoadingUsers = false;
users = users.concat(result.items);

View File

@ -1,17 +1,19 @@
import { replace } from "svelte-spa-router";
import { wrap } from "svelte-spa-router/wrap";
import ApiClient from "@/utils/ApiClient";
import PageIndex from "@/components/PageIndex.svelte";
import PageLogs from "@/components/logs/PageLogs.svelte";
import PageRecords from "@/components/records/PageRecords.svelte";
import PageUsers from "@/components/users/PageUsers.svelte";
import PageAdmins from "@/components/admins/PageAdmins.svelte";
import PageAdminLogin from "@/components/admins/PageAdminLogin.svelte";
import PageApplication from "@/components/settings/PageApplication.svelte";
import PageMail from "@/components/settings/PageMail.svelte";
import PageStorage from "@/components/settings/PageStorage.svelte";
import PageAuthProviders from "@/components/settings/PageAuthProviders.svelte";
import PageTokenOptions from "@/components/settings/PageTokenOptions.svelte";
import { replace } from "svelte-spa-router";
import { wrap } from "svelte-spa-router/wrap";
import ApiClient from "@/utils/ApiClient";
import PageIndex from "@/components/PageIndex.svelte";
import PageLogs from "@/components/logs/PageLogs.svelte";
import PageRecords from "@/components/records/PageRecords.svelte";
import PageUsers from "@/components/users/PageUsers.svelte";
import PageAdmins from "@/components/admins/PageAdmins.svelte";
import PageAdminLogin from "@/components/admins/PageAdminLogin.svelte";
import PageApplication from "@/components/settings/PageApplication.svelte";
import PageMail from "@/components/settings/PageMail.svelte";
import PageStorage from "@/components/settings/PageStorage.svelte";
import PageAuthProviders from "@/components/settings/PageAuthProviders.svelte";
import PageTokenOptions from "@/components/settings/PageTokenOptions.svelte";
import PageExportCollections from "@/components/settings/PageExportCollections.svelte";
import PageImportCollections from "@/components/settings/PageImportCollections.svelte";
const baseConditions = [
async (details) => {
@ -134,6 +136,18 @@ const routes = {
userData: { showAppSidebar: true },
}),
"/settings/export-collections": wrap({
component: PageExportCollections,
conditions: baseConditions.concat([(_) => ApiClient.authStore.isValid]),
userData: { showAppSidebar: true },
}),
"/settings/import-collections": wrap({
component: PageImportCollections,
conditions: baseConditions.concat([(_) => ApiClient.authStore.isValid]),
userData: { showAppSidebar: true },
}),
// fallback
"*": wrap({
component: PageIndex,

View File

@ -94,14 +94,14 @@
}
}
// toast alerts
.toasts-wrapper {
position: fixed;
z-index: 999999;
bottom: 0;
left: 0;
right: 0;
padding: 0 var(--smSpacing);
width: 100%;
width: auto;
display: block;
text-align: center;
pointer-events: none;
@ -112,4 +112,10 @@
margin: var(--baseSpacing) auto;
@include shadowize();
}
.app-sidebar ~ .app-body & {
left: var(--appSidebarWidth);
}
.app-sidebar ~ .app-body .page-sidebar ~ & {
left: calc(var(--appSidebarWidth) + var(--pageSidebarWidth));
}
}

View File

@ -63,6 +63,16 @@ em {
font-style: italic;
}
ins {
background: var(--successAltColor);
text-decoration: none;
}
del {
background: var(--dangerAltColor);
text-decoration: none;
}
strong {
font-weight: 600;
}

View File

@ -53,6 +53,7 @@ button {
height: 100%;
pointer-events: none;
user-select: none;
backface-visibility: hidden;
background: var(--primaryColor);
transition: filter var(--baseAnimationSpeed),
opacity var(--baseAnimationSpeed),
@ -483,13 +484,13 @@ select {
line-height: var(--smLineHeight);
color: var(--txtHintColor);
}
.help-block-error {
color: var(--dangerColor);
}
// states
&.error,
&.invalid {
.help-block-error {
color: var(--dangerColor);
}
> label {
color: var(--dangerColor);
}

View File

@ -600,6 +600,22 @@ export default class CommonHelper {
})
}
/**
* Downloads a json file created from the provide object.
*
* @param {mixed} obj The JS object to download.
* @param {String} name The result file name.
*/
static downloadJson(obj, name) {
const encodedObj = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj, null, 2));
const tempLink = document.createElement('a');
tempLink.setAttribute("href", encodedObj);
tempLink.setAttribute("download", name + ".json");
tempLink.click();
tempLink.remove();
}
/**
* Parses and returns the decoded jwt payload data.
*

View File

@ -24,12 +24,4 @@ export default defineConfig({
'@': __dirname + '/src',
}
},
css: {
preprocessorOptions: {
scss: {
includePaths: [__dirname + '/src/scss'],
prependData: `@import "${__dirname}/src/scss/mixins"; @import "${__dirname}/src/scss/vars";`,
},
},
},
})