Compare commits

..
39 Commits
Author SHA1 Message Date
Gani Georgiev c1921aeef8 updated changelog 2023-01-12 15:42:46 +02:00
Gani Georgiev 012546e838 removed delete worker pool since it is no longer needed and changed the files delete operation to run in the background (will be replaced with job queue) 2023-01-12 15:34:56 +02:00
Gani Georgiev f792a9e08d fixed ListBucket iterator to always break on seek/forward error 2023-01-12 15:19:27 +02:00
Gani Georgiev a8b2f0f6f1 updated min github action go version to 1.19.5 2023-01-11 14:53:24 +02:00
Gani Georgiev f7d4722052 [#1552] unescaped path parameter values 2023-01-09 22:36:28 +02:00
Gani Georgiev 3dc1bf6fa7 updated go deps 2023-01-08 11:32:19 +02:00
Gani Georgiev d3cc87abee updated rule field styles 2023-01-08 11:11:02 +02:00
Gani Georgiev 19ad827302 updated migration timestamp 2023-01-08 11:05:44 +02:00
Gani Georgiev 40830b6c43 updated API preview docs 2023-01-08 10:31:21 +02:00
Gani Georgiev f234132629 [#1523] added Docs link in the footer 2023-01-08 00:25:09 +02:00
Gani Georgiev 9b880f5ab4 filter enhancements 2023-01-07 22:27:11 +02:00
Gani Georgiev d5775ff657 updated ui/dist and changelog 2023-01-02 23:06:19 +02:00
Khairul Haaziq 41ba7e12e1 [#1469] added webp to the image mime types presets 2023-01-02 23:02:14 +02:00
Gani Georgiev 5ef6b3a8be updated changelog 2022-12-31 16:53:45 +02:00
Gani Georgiev c673d9d314 updated github and gitee optional email fetch handling 2022-12-31 16:45:42 +02:00
Gani Georgiev a7aa3da67e Merge branch 'master' into yuxiang-gao-gitee-oauth 2022-12-31 15:00:19 +02:00
Gani Georgiev 0439af458f updated the GitHub provider to ignore extra emails request errors in case of unsufficient custom scopes 2022-12-31 14:58:30 +02:00
Yuxiang Gao 6f3241399c Update UI
Signed-off-by: Yuxiang Gao <yuxiang-gao@outlook.com>
2022-12-31 18:05:49 +08:00
Yuxiang Gao 19ccc70fe5 Added gitee OAuth
Signed-off-by: Yuxiang Gao <yuxiang-gao@outlook.com>
2022-12-31 17:57:40 +08:00
Gani Georgiev 9033cd109e updated changelog 2022-12-31 11:01:58 +02:00
Gani Georgiev e1773eead0 synced with master 2022-12-31 10:58:25 +02:00
Gani Georgiev f6fff85d07 updated changelog 2022-12-31 10:11:24 +02:00
Gani Georgiev 166d2eafc7 [#1447] fixed records listing number field value output 2022-12-31 10:08:04 +02:00
szsascha d710446c71 Implement strava oauth2 as new auth provider 2022-12-31 01:21:41 +01:00
Gani Georgiev 8f6cb5ad2d updated changelog and godoc 2022-12-29 18:58:07 +02:00
David Schissler 775417ac2b [#1420] added filesystem.NewFileFromBytes 2022-12-29 18:51:27 +02:00
Gani Georgiev 079616ee8e [#1416] fixed listing searchbar text wrapping 2022-12-28 22:27:42 +02:00
Gani Georgiev 26b794aa08 updated changelog 2022-12-24 15:54:14 +02:00
tenthree d55610511d [#1370] added IME status checking to the textarea keydown handler 2022-12-24 15:50:51 +02:00
Gani Georgiev a00606d969 updated changelog and ui/dist 2022-12-24 08:40:08 +02:00
Gani Georgiev 4abc8ae021 [#1365] fixed Record.MergeExpand panic 2022-12-24 08:34:42 +02:00
Gani Georgiev f91f009fce updated go deps 2022-12-22 16:16:21 +02:00
Gani Georgiev 7fc1d979dd added fs.UploadFile unit test and updated changelog 2022-12-22 16:06:44 +02:00
Yuxiang Gao ede7804a80 [#1343] fixed s3 file upload error caused by underscore in metadata 2022-12-22 15:39:45 +02:00
Gani Georgiev cea287a2c1 updated ui/dist 2022-12-21 19:18:57 +02:00
Joysankar Majumdar 9e03ac4dc4 [#1332] fixed request verification docs api url 2022-12-21 19:17:05 +02:00
Gani Georgiev e713de1e44 updated excluded meta props in the relation select option 2022-12-21 09:45:14 +02:00
Gani Georgiev 233ab62f8e updated js deps 2022-12-20 20:27:13 +02:00
Gani Georgiev 3449084e54 hinted indexed map size 2022-12-20 20:25:38 +02:00
132 changed files with 5183 additions and 1414 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '>=1.19.4'
go-version: '>=1.19.5'
# This step usually is not needed because the /ui/dist is pregenerated locally
# but its here to ensure that each release embeds the latest admin ui artifacts.
+121
View File
@@ -1,3 +1,124 @@
## v0.11.2
- Fixed `fs.DeleteByPrefix()` hang on invalid S3 settings ([#1575](https://github.com/pocketbase/pocketbase/discussions/1575#discussioncomment-4661089)).
- Updated file(s) delete to run in the background on record/collection delete to avoid blocking the delete model transaction.
_Currently the cascade files delete operation is treated as "non-critical" and in case of an error it is just logged during debug._
_This will be improved in the near future with the planned async job queue implementation._
## v0.11.1
- Unescaped path parameter values ([#1552](https://github.com/pocketbase/pocketbase/issues/1552)).
## v0.11.0
- Added `+` and `-` body field modifiers for `number`, `files`, `select` and `relation` fields.
```js
{
// oldValue + 2
"someNumber+": 2,
// oldValue + ["id1", "id2"] - ["id3"]
"someRelation+": ["id1", "id2"],
"someRelation-": ["id3"],
// delete single file by its name (file fields supports only the "-" modifier!)
"someFile-": "filename.png",
}
```
_Note1: `@request.data.someField` will contain the final resolved value._
_Note2: The old index (`"field.0":null`) and filename (`"field.filename.png":null`) based suffixed syntax for deleting files is still supported._
- ! Added support for multi-match/match-all request data and collection multi-valued fields (`select`, `relation`) conditions.
If you want a "at least one of" type of condition, you can prefix the operator with `?`.
```js
// for each someRelA.someRelB record require the "status" field to be "active"
someRelA.someRelB.status = "active"
// OR for "at least one of" condition
someRelA.someRelB.status ?= "active"
```
_**Note: Previously the behavior for multi-valued fields was as the "at least one of" type.
The release comes with system db migration that will update your existing API rules (if needed) to preserve the compatibility.
If you have multi-select or multi-relation filter checks in your client-side code and want to preserve the old behavior, you'll have to prefix with `?` your operators.**_
- Added support for querying `@request.data.someRelField.*` relation fields.
```js
// example submitted data: {"someRel": "REL_RECORD_ID"}
@request.data.someRel.status = "active"
```
- Added `:isset` modifier for the static request data fields.
```js
// prevent changing the "role" field
@request.data.role:isset = false
```
- Added `:length` modifier for the arrayable request data and collection fields (`select`, `file`, `relation`).
```js
// example submitted data: {"someSelectField": ["val1", "val2"]}
@request.data.someSelectField:length = 2
// check existing record field length
someSelectField:length = 2
```
- Added `:each` modifier support for the multi-`select` request data and collection field.
```js
// check if all selected rows has "pb_" prefix
roles:each ~ 'pb_%'
```
- Improved the Admin UI filters autocomplete.
- Added `@random` sort key for `RANDOM()` sorted list results.
- Added Strava OAuth2 provider ([#1443](https://github.com/pocketbase/pocketbase/pull/1443); thanks @szsascha).
- Added Gitee OAuth2 provider ([#1448](https://github.com/pocketbase/pocketbase/pull/1448); thanks @yuxiang-gao).
- Added IME status check to the textarea keydown handler ([#1370](https://github.com/pocketbase/pocketbase/pull/1370); thanks @tenthree).
- Added `filesystem.NewFileFromBytes()` helper ([#1420](https://github.com/pocketbase/pocketbase/pull/1420); thanks @dschissler).
- Added support for reordering uploaded multiple files.
- Added `webp` to the default images mime type presets list ([#1469](https://github.com/pocketbase/pocketbase/pull/1469); thanks @khairulhaaziq).
- Added the OAuth2 refresh token to the auth meta response ([#1487](https://github.com/pocketbase/pocketbase/issues/1487)).
- Fixed the text wrapping in the Admin UI listing searchbar ([#1416](https://github.com/pocketbase/pocketbase/issues/1416)).
- Fixed number field value output in the records listing ([#1447](https://github.com/pocketbase/pocketbase/issues/1447)).
- Fixed duplicated settings view pages caused by uncompleted transitions ([#1498](https://github.com/pocketbase/pocketbase/issues/1498)).
- Allowed sending `Authorization` header with the `/auth-with-password` record and admin login requests ([#1494](https://github.com/pocketbase/pocketbase/discussions/1494)).
- `migrate down` now reverts migrations in the applied order.
- Added additional list-bucket check in the S3 config test API.
- Other minor improvements.
## v0.10.4
- Fixed `Record.MergeExpand` panic when the main model expand map is not initialized ([#1365](https://github.com/pocketbase/pocketbase/issues/1365)).
## v0.10.3
- ! Renamed the metadata key `original_filename` to `original-filename` due to an S3 file upload error caused by the underscore character ([#1343](https://github.com/pocketbase/pocketbase/pull/1343); thanks @yuxiang-gao).
- Fixed request verification docs api url ([#1332](https://github.com/pocketbase/pocketbase/pull/1332); thanks @JoyMajumdar2001)
- Excluded `collectionId` and `collectionName` from the displayable relation props list ([1322](https://github.com/pocketbase/pocketbase/issues/1322); thanks @dhall2).
## v0.10.2
- Fixed nested multiple expands with shared path ([#586](https://github.com/pocketbase/pocketbase/issues/586#issuecomment-1357784227)).
+1 -1
View File
@@ -1,5 +1,5 @@
The MIT License (MIT)
Copyright (c) 2022, Gani Georgiev
Copyright (c) 2022 - present, Gani Georgiev
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
+1 -1
View File
@@ -18,7 +18,7 @@ func bindAdminApi(app core.App, rg *echo.Group) {
api := adminApi{app: app}
subGroup := rg.Group("/admins", ActivityLogger(app))
subGroup.POST("/auth-with-password", api.authWithPassword, RequireGuestOnly())
subGroup.POST("/auth-with-password", api.authWithPassword)
subGroup.POST("/request-password-reset", api.requestPasswordReset)
subGroup.POST("/confirm-password-reset", api.confirmPasswordReset)
subGroup.POST("/auth-refresh", api.authRefresh, RequireAdminAuth())
+14 -8
View File
@@ -48,6 +48,20 @@ func TestAdminAuthWithEmail(t *testing.T) {
ExpectedStatus: 400,
ExpectedContent: []string{`"data":{}`},
},
{
Name: "valid email/password (guest)",
Method: http.MethodPost,
Url: "/api/admins/auth-with-password",
Body: strings.NewReader(`{"identity":"test@example.com","password":"1234567890"}`),
ExpectedStatus: 200,
ExpectedContent: []string{
`"admin":{"id":"sywbhecnh46rhm0"`,
`"token":`,
},
ExpectedEvents: map[string]int{
"OnAdminAuthRequest": 1,
},
},
{
Name: "valid email/password (already authorized)",
Method: http.MethodPost,
@@ -56,14 +70,6 @@ func TestAdminAuthWithEmail(t *testing.T) {
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4MTYwMH0.han3_sG65zLddpcX2ic78qgy7FKecuPfOpFa8Dvi5Bg",
},
ExpectedStatus: 400,
ExpectedContent: []string{`"message":"The request can be accessed only by guests.","data":{}`},
},
{
Name: "valid email/password (guest)",
Method: http.MethodPost,
Url: "/api/admins/auth-with-password",
Body: strings.NewReader(`{"identity":"test@example.com","password":"1234567890"}`),
ExpectedStatus: 200,
ExpectedContent: []string{
`"admin":{"id":"sywbhecnh46rhm0"`,
+7
View File
@@ -26,6 +26,13 @@ func InitApi(app core.App) (*echo.Echo, error) {
e := echo.New()
e.Debug = app.IsDebug()
// configure a custom router
e.ResetRouterCreator(func(ec *echo.Echo) echo.Router {
return echo.NewRouter(echo.RouterConfig{
UnescapePathParamValues: true,
})
})
// default middlewares
e.Pre(middleware.RemoveTrailingSlashWithConfig(middleware.RemoveTrailingSlashConfig{
Skipper: func(c echo.Context) bool {
+16
View File
@@ -66,6 +66,22 @@ func TestCustomRoutesAndErrorsHandling(t *testing.T) {
ExpectedStatus: 200,
ExpectedContent: []string{"test123"},
},
{
Name: "custom route with url encoded parameter",
Method: http.MethodGet,
Url: "/a%2Bb%2Bc",
BeforeTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
e.AddRoute(echo.Route{
Method: http.MethodGet,
Path: "/:param",
Handler: func(c echo.Context) error {
return c.String(200, c.PathParam("param"))
},
})
},
ExpectedStatus: 200,
ExpectedContent: []string{"a+b+c"},
},
{
Name: "route with HTTPError",
Method: http.MethodGet,
+15 -10
View File
@@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
"testing"
"time"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase/models"
@@ -42,7 +43,7 @@ func TestCollectionsList(t *testing.T) {
ExpectedContent: []string{
`"page":1`,
`"perPage":30`,
`"totalItems":7`,
`"totalItems":8`,
`"items":[{`,
`"id":"_pb_users_auth_"`,
`"id":"v851q4r790rhknl"`,
@@ -51,6 +52,7 @@ func TestCollectionsList(t *testing.T) {
`"id":"sz5l5z67tg7gku0"`,
`"id":"wzlqyes4orhoygb"`,
`"id":"4d1blo5cuycfaca"`,
`"id":"9n89pl5vkct6330"`,
`"type":"auth"`,
`"type":"base"`,
},
@@ -69,10 +71,10 @@ func TestCollectionsList(t *testing.T) {
ExpectedContent: []string{
`"page":2`,
`"perPage":2`,
`"totalItems":7`,
`"totalItems":8`,
`"items":[{`,
`"id":"v851q4r790rhknl"`,
`"id":"4d1blo5cuycfaca"`,
`"id":"wzlqyes4orhoygb"`,
},
ExpectedEvents: map[string]int{
"OnCollectionsListRequest": 1,
@@ -99,12 +101,13 @@ func TestCollectionsList(t *testing.T) {
ExpectedContent: []string{
`"page":1`,
`"perPage":30`,
`"totalItems":4`,
`"totalItems":5`,
`"items":[{`,
`"id":"wsmn24bux7wo113"`,
`"id":"sz5l5z67tg7gku0"`,
`"id":"wzlqyes4orhoygb"`,
`"id":"4d1blo5cuycfaca"`,
`"id":"9n89pl5vkct6330"`,
},
ExpectedEvents: map[string]int{
"OnCollectionsListRequest": 1,
@@ -230,6 +233,7 @@ func TestCollectionDelete(t *testing.T) {
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
},
Delay: 100 * time.Millisecond,
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelBeforeDelete": 1,
@@ -248,6 +252,7 @@ func TestCollectionDelete(t *testing.T) {
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
},
Delay: 100 * time.Millisecond,
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelBeforeDelete": 1,
@@ -786,7 +791,7 @@ func TestCollectionImport(t *testing.T) {
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
t.Fatal(err)
}
expected := 7
expected := 8
if len(collections) != expected {
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
}
@@ -814,7 +819,7 @@ func TestCollectionImport(t *testing.T) {
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
t.Fatal(err)
}
expected := 7
expected := 8
if len(collections) != expected {
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
}
@@ -856,7 +861,7 @@ func TestCollectionImport(t *testing.T) {
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
t.Fatal(err)
}
expected := 7
expected := 8
if len(collections) != expected {
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
}
@@ -909,7 +914,7 @@ func TestCollectionImport(t *testing.T) {
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
t.Fatal(err)
}
expected := 10
expected := 11
if len(collections) != expected {
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
}
@@ -996,8 +1001,8 @@ func TestCollectionImport(t *testing.T) {
ExpectedEvents: map[string]int{
"OnCollectionsAfterImportRequest": 1,
"OnCollectionsBeforeImportRequest": 1,
"OnModelBeforeDelete": 5,
"OnModelAfterDelete": 5,
"OnModelBeforeDelete": 6,
"OnModelAfterDelete": 6,
"OnModelBeforeUpdate": 2,
"OnModelAfterUpdate": 2,
"OnModelBeforeCreate": 1,
+2 -2
View File
@@ -35,8 +35,8 @@ func bindRecordAuthApi(app core.App, rg *echo.Group) {
subGroup.GET("/auth-methods", api.authMethods)
subGroup.POST("/auth-refresh", api.authRefresh, RequireSameContextRecordAuth())
subGroup.POST("/auth-with-oauth2", api.authWithOAuth2) // allow anyone so that we can link the OAuth2 profile with the authenticated record
subGroup.POST("/auth-with-password", api.authWithPassword, RequireGuestOnly())
subGroup.POST("/auth-with-oauth2", api.authWithOAuth2)
subGroup.POST("/auth-with-password", api.authWithPassword)
subGroup.POST("/request-password-reset", api.requestPasswordReset)
subGroup.POST("/confirm-password-reset", api.confirmPasswordReset)
subGroup.POST("/request-verification", api.requestVerification)
+46 -20
View File
@@ -66,26 +66,6 @@ func TestRecordAuthMethodsList(t *testing.T) {
func TestRecordAuthWithPassword(t *testing.T) {
scenarios := []tests.ApiScenario{
{
Name: "authenticated record",
Method: http.MethodPost,
Url: "/api/collections/users/auth-with-password",
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
},
ExpectedStatus: 400,
ExpectedContent: []string{`"data":{}`},
},
{
Name: "authenticated admin",
Method: http.MethodPost,
Url: "/api/collections/users/auth-with-password",
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
},
ExpectedStatus: 400,
ExpectedContent: []string{`"data":{}`},
},
{
Name: "invalid body format",
Method: http.MethodPost,
@@ -226,6 +206,52 @@ func TestRecordAuthWithPassword(t *testing.T) {
"OnRecordAuthRequest": 1,
},
},
// with already authenticated record or admin
{
Name: "authenticated record",
Method: http.MethodPost,
Url: "/api/collections/users/auth-with-password",
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
},
Body: strings.NewReader(`{
"identity":"test@example.com",
"password":"1234567890"
}`),
ExpectedStatus: 200,
ExpectedContent: []string{
`"record":{`,
`"token":"`,
`"id":"4q1xlclmfloku33"`,
`"email":"test@example.com"`,
},
ExpectedEvents: map[string]int{
"OnRecordAuthRequest": 1,
},
},
{
Name: "authenticated admin",
Method: http.MethodPost,
Url: "/api/collections/users/auth-with-password",
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
},
Body: strings.NewReader(`{
"identity":"test@example.com",
"password":"1234567890"
}`),
ExpectedStatus: 200,
ExpectedContent: []string{
`"record":{`,
`"token":"`,
`"id":"4q1xlclmfloku33"`,
`"email":"test@example.com"`,
},
ExpectedEvents: map[string]int{
"OnRecordAuthRequest": 1,
},
},
}
for _, scenario := range scenarios {
+24 -7
View File
@@ -166,6 +166,20 @@ func (api *recordApi) create(c echo.Context) error {
// temporary save the record and check it against the create rule
if requestData.Admin == nil && collection.CreateRule != nil {
testRecord := models.NewRecord(collection)
// replace modifiers fields so that the resolved value is always
// available when accessing requestData.Data using just the field name
if requestData.HasModifierDataKeys() {
requestData.Data = testRecord.ReplaceModifers(requestData.Data)
}
testForm := forms.NewRecordUpsert(api.app, testRecord)
testForm.SetFullManageAccess(true)
if err := testForm.LoadRequest(c.Request(), ""); err != nil {
return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
}
createRuleFunc := func(q *dbx.SelectQuery) error {
if *collection.CreateRule == "" {
return nil // no create rule to resolve
@@ -181,13 +195,6 @@ func (api *recordApi) create(c echo.Context) error {
return nil
}
testRecord := models.NewRecord(collection)
testForm := forms.NewRecordUpsert(api.app, testRecord)
testForm.SetFullManageAccess(true)
if err := testForm.LoadRequest(c.Request(), ""); err != nil {
return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
}
testErr := testForm.DrySubmit(func(txDao *daos.Dao) error {
foundRecord, err := txDao.FindRecordById(collection.Id, testRecord.Id, createRuleFunc)
if err != nil {
@@ -258,6 +265,16 @@ func (api *recordApi) update(c echo.Context) error {
return NewForbiddenError("Only admins can perform this action.", nil)
}
// eager fetch the record so that the modifier field values are replaced
// and available when accessing requestData.Data using just the field name
if requestData.HasModifierDataKeys() {
record, err := api.app.Dao().FindRecordById(collection.Id, recordId)
if err != nil || record == nil {
return NewNotFoundError("", err)
}
requestData.Data = record.ReplaceModifers(requestData.Data)
}
ruleFunc := func(q *dbx.SelectQuery) error {
if requestData.Admin == nil && collection.UpdateRule != nil && *collection.UpdateRule != "" {
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
+162 -1
View File
@@ -2,10 +2,12 @@ package apis_test
import (
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase/models"
@@ -209,6 +211,50 @@ func TestRecordCrudList(t *testing.T) {
},
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
},
{
Name: ":rule modifer",
Method: http.MethodGet,
Url: "/api/collections/demo5/records",
ExpectedStatus: 200,
ExpectedContent: []string{
`"page":1`,
`"perPage":30`,
`"totalPages":1`,
`"totalItems":1`,
`"items":[{`,
`"id":"qjeql998mtp1azp"`,
},
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
},
{
Name: "multi-match - at least one of",
Method: http.MethodGet,
Url: "/api/collections/demo4/records?filter=" + url.QueryEscape("rel_many_no_cascade_required.files:length?=2"),
ExpectedStatus: 200,
ExpectedContent: []string{
`"page":1`,
`"perPage":30`,
`"totalPages":1`,
`"totalItems":1`,
`"items":[{`,
`"id":"qzaqccwrmva4o1n"`,
},
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
},
{
Name: "multi-match - all",
Method: http.MethodGet,
Url: "/api/collections/demo4/records?filter=" + url.QueryEscape("rel_many_no_cascade_required.files:length=2"),
ExpectedStatus: 200,
ExpectedContent: []string{
`"page":1`,
`"perPage":30`,
`"totalPages":0`,
`"totalItems":0`,
`"items":[]`,
},
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
},
// auth collection checks
// -----------------------------------------------------------
@@ -691,6 +737,7 @@ func TestRecordCrudDelete(t *testing.T) {
// users, test@example.com
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
},
Delay: 100 * time.Millisecond,
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelAfterDelete": 3, // +2 because of the external auths
@@ -716,6 +763,25 @@ func TestRecordCrudDelete(t *testing.T) {
}
},
},
{
Name: "@request :isset (rule failure check)",
Method: http.MethodDelete,
Url: "/api/collections/demo5/records/la4y2w4o98acwuj",
ExpectedStatus: 404,
ExpectedContent: []string{`"data":{}`},
},
{
Name: "@request :isset (rule pass check)",
Method: http.MethodDelete,
Url: "/api/collections/demo5/records/la4y2w4o98acwuj?test=1",
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelAfterDelete": 1,
"OnModelBeforeDelete": 1,
"OnRecordAfterDeleteRequest": 1,
"OnRecordBeforeDeleteRequest": 1,
},
},
// cascade delete checks
// -----------------------------------------------------------
@@ -730,7 +796,7 @@ func TestRecordCrudDelete(t *testing.T) {
ExpectedContent: []string{`"data":{}`},
ExpectedEvents: map[string]int{
"OnRecordBeforeDeleteRequest": 1,
"OnModelBeforeUpdate": 1, // self_rel_many update of test1 record
"OnModelBeforeUpdate": 2, // self_rel_many update of test1 record + rel_one_cascade demo4 cascaded in demo5
"OnModelBeforeDelete": 2, // the record itself + rel_one_cascade of test1 record
},
},
@@ -758,6 +824,7 @@ func TestRecordCrudDelete(t *testing.T) {
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
},
Delay: 100 * time.Millisecond,
ExpectedStatus: 204,
ExpectedEvents: map[string]int{
"OnModelBeforeDelete": 2,
@@ -1092,6 +1159,63 @@ func TestRecordCrudCreate(t *testing.T) {
},
},
// fields modifier checks
// -----------------------------------------------------------
{
Name: "trying to delete a record while being part of a non-cascade required relation",
Method: http.MethodDelete,
Url: "/api/collections/demo3/records/7nwo8tuiatetxdm",
RequestHeaders: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
},
ExpectedStatus: 400,
ExpectedContent: []string{`"data":{}`},
ExpectedEvents: map[string]int{
"OnRecordBeforeDeleteRequest": 1,
"OnModelBeforeUpdate": 2, // self_rel_many update of test1 record + rel_one_cascade demo4 cascaded in demo5
"OnModelBeforeDelete": 2, // the record itself + rel_one_cascade of test1 record
},
},
// check whether if @request.data modifer fields are properly resolved
// -----------------------------------------------------------
{
Name: "@request.data.field with compute modifers (rule failure check)",
Method: http.MethodPost,
Url: "/api/collections/demo5/records",
Body: strings.NewReader(`{
"total":1,
"total+":4,
"total-":1
}`),
ExpectedStatus: 400,
ExpectedContent: []string{
`"data":{}`,
},
},
{
Name: "@request.data.field with compute modifers (rule pass check)",
Method: http.MethodPost,
Url: "/api/collections/demo5/records",
Body: strings.NewReader(`{
"total":1,
"total+":3,
"total-":1
}`),
ExpectedStatus: 200,
ExpectedContent: []string{
`"id":"`,
`"collectionName":"demo5"`,
`"total":3`,
},
ExpectedEvents: map[string]int{
"OnModelAfterCreate": 1,
"OnModelBeforeCreate": 1,
"OnRecordAfterCreateRequest": 1,
"OnRecordBeforeCreateRequest": 1,
},
},
// auth records
// -----------------------------------------------------------
{
@@ -1501,6 +1625,43 @@ func TestRecordCrudUpdate(t *testing.T) {
},
},
// check whether if @request.data modifer fields are properly resolved
// -----------------------------------------------------------
{
Name: "@request.data.field with compute modifers (rule failure check)",
Method: http.MethodPatch,
Url: "/api/collections/demo5/records/la4y2w4o98acwuj",
Body: strings.NewReader(`{
"total+":3,
"total-":1
}`),
ExpectedStatus: 404,
ExpectedContent: []string{
`"data":{}`,
},
},
{
Name: "@request.data.field with compute modifers (rule pass check)",
Method: http.MethodPatch,
Url: "/api/collections/demo5/records/la4y2w4o98acwuj",
Body: strings.NewReader(`{
"total+":2,
"total-":1
}`),
ExpectedStatus: 200,
ExpectedContent: []string{
`"id":"la4y2w4o98acwuj"`,
`"collectionName":"demo5"`,
`"total":3`,
},
ExpectedEvents: map[string]int{
"OnModelAfterUpdate": 1,
"OnModelBeforeUpdate": 1,
"OnRecordAfterUpdateRequest": 1,
"OnRecordBeforeUpdateRequest": 1,
},
},
// auth records
// -----------------------------------------------------------
{
-5
View File
@@ -15,11 +15,6 @@ import (
const ContextRequestDataKey = "requestData"
// Deprecated: Will be removed after v0.9. Use apis.RequestData(c) instead.
func GetRequestData(c echo.Context) *models.RequestData {
return RequestData(c)
}
// RequestData exports cached common request data fields
// (query, body, logged auth state, etc.) from the provided context.
func RequestData(c echo.Context) *models.RequestData {
+7 -3
View File
@@ -1,6 +1,7 @@
package apis
import (
"fmt"
"net/http"
validation "github.com/go-ozzo/ozzo-validation/v4"
@@ -91,14 +92,17 @@ func (api *settingsApi) testS3(c echo.Context) error {
}
defer fs.Close()
testFileKey := "pb_test_" + security.PseudorandomString(5) + "/test.txt"
testPrefix := "pb_settings_test_" + security.PseudorandomString(5)
testFileKey := testPrefix + "/test.txt"
// try to upload a test file
if err := fs.Upload([]byte("test"), testFileKey); err != nil {
return NewBadRequestError("Failed to upload a test file. Raw error: \n"+err.Error(), nil)
}
if err := fs.Delete(testFileKey); err != nil {
return NewBadRequestError("Failed to delete a test file. Raw error: \n"+err.Error(), nil)
// test prefix deletion (ensures that both bucket list and delete works)
if errs := fs.DeletePrefix(testPrefix); len(errs) > 0 {
return NewBadRequestError(fmt.Sprintf("Failed to delete a test file. Raw error: %v", errs), nil)
}
return c.NoContent(http.StatusNoContent)
+6
View File
@@ -58,6 +58,8 @@ func TestSettingsList(t *testing.T) {
`"spotifyAuth":{`,
`"kakaoAuth":{`,
`"twitchAuth":{`,
`"stravaAuth":{`,
`"giteeAuth":{`,
`"secret":"******"`,
`"clientSecret":"******"`,
},
@@ -125,6 +127,8 @@ func TestSettingsSet(t *testing.T) {
`"spotifyAuth":{`,
`"kakaoAuth":{`,
`"twitchAuth":{`,
`"stravaAuth":{`,
`"giteeAuth":{`,
`"secret":"******"`,
`"clientSecret":"******"`,
`"appName":"acme_test"`,
@@ -181,6 +185,8 @@ func TestSettingsSet(t *testing.T) {
`"spotifyAuth":{`,
`"kakaoAuth":{`,
`"twitchAuth":{`,
`"stravaAuth":{`,
`"giteeAuth":{`,
`"secret":"******"`,
`"clientSecret":"******"`,
`"appName":"update_test"`,
+2 -1
View File
@@ -78,7 +78,8 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
GetCertificate: certManager.GetCertificate,
NextProtos: []string{acme.ALPNProto},
},
ReadTimeout: 60 * time.Second,
ReadTimeout: 5 * time.Minute,
ReadHeaderTimeout: 30 * time.Second,
// WriteTimeout: 60 * time.Second, // breaks sse!
Handler: router,
Addr: mainAddr,
+1 -1
View File
@@ -18,7 +18,7 @@ import (
type App interface {
// Deprecated:
// This method may get removed in the near future.
// It is recommended to access the logs db instance from app.Dao().DB() or
// It is recommended to access the app db instance from app.Dao().DB() or
// if you want more flexibility - app.Dao().ConcurrentDB() and app.Dao().NonconcurrentDB().
//
// DB returns the default app database instance.
+17 -7
View File
@@ -17,6 +17,7 @@ import (
"github.com/pocketbase/pocketbase/tools/filesystem"
"github.com/pocketbase/pocketbase/tools/hook"
"github.com/pocketbase/pocketbase/tools/mailer"
"github.com/pocketbase/pocketbase/tools/routine"
"github.com/pocketbase/pocketbase/tools/store"
"github.com/pocketbase/pocketbase/tools/subscriptions"
)
@@ -939,20 +940,29 @@ func (app *BaseApp) registerDefaultHooks() {
failed := fs.DeletePrefix(prefix)
if len(failed) > 0 {
return errors.New("Failed to delete the files at " + prefix)
return errors.New("failed to delete the files at " + prefix)
}
return nil
}
// delete storage files from deleted Collection, Records, etc.
// try to delete the storage files from deleted Collection, Records, etc. model
app.OnModelAfterDelete().Add(func(e *ModelEvent) error {
if m, ok := e.Model.(models.FilesManager); ok && m.BaseFilesPath() != "" {
if err := deletePrefix(m.BaseFilesPath()); err != nil && app.IsDebug() {
// non critical error - only log for debug
// (usually could happen because of S3 api limits)
log.Println(err)
}
prefix := m.BaseFilesPath()
// run in the background for "optimistic" delete to avoid
// blocking the delete transaction
//
// @todo consider creating a bg process queue so that the
// call could be "retried" in case of a failure.
routine.FireAndForget(func() {
if err := deletePrefix(prefix); err != nil && app.IsDebug() {
// non critical error - only log for debug
// (usually could happen because of S3 api limits)
log.Println(err)
}
})
}
return nil
+2 -2
View File
@@ -97,8 +97,8 @@ func (dao *Dao) IsAdminEmailUnique(email string, excludeIds ...string) bool {
AndWhere(dbx.HashExp{"email": email}).
Limit(1)
if len(excludeIds) > 0 {
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(excludeIds)...))
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(uniqueExcludeIds)...))
}
var exists bool
+5 -4
View File
@@ -65,8 +65,7 @@ func (dao *Dao) IsCollectionNameUnique(name string, excludeIds ...string) bool {
AndWhere(dbx.NewExp("LOWER([[name]])={:name}", dbx.Params{"name": strings.ToLower(name)})).
Limit(1)
if len(excludeIds) > 0 {
uniqueExcludeIds := list.NonzeroUniques(excludeIds)
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(uniqueExcludeIds)...))
}
@@ -85,15 +84,17 @@ func (dao *Dao) FindCollectionReferences(collection *models.Collection, excludeI
collections := []*models.Collection{}
query := dao.CollectionQuery()
if len(excludeIds) > 0 {
uniqueExcludeIds := list.NonzeroUniques(excludeIds)
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(uniqueExcludeIds)...))
}
if err := query.All(&collections); err != nil {
return nil, err
}
result := map[*models.Collection][]*schema.SchemaField{}
for _, c := range collections {
for _, f := range c.Schema.Fields() {
if f.Type != schema.FieldTypeRelation {
+15 -8
View File
@@ -37,7 +37,7 @@ func TestFindCollectionsByType(t *testing.T) {
{"", false, 0},
{"unknown", false, 0},
{models.CollectionTypeAuth, false, 3},
{models.CollectionTypeBase, false, 4},
{models.CollectionTypeBase, false, 5},
}
for i, scenario := range scenarios {
@@ -122,7 +122,13 @@ func TestFindCollectionReferences(t *testing.T) {
t.Fatal(err)
}
result, err := app.Dao().FindCollectionReferences(collection, collection.Id)
result, err := app.Dao().FindCollectionReferences(
collection,
collection.Id,
// test whether "nonempty" exclude ids condition will be skipped
"",
"",
)
if err != nil {
t.Fatal(err)
}
@@ -296,7 +302,7 @@ func TestImportCollections(t *testing.T) {
name: "empty collections",
jsonData: `[]`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
},
{
name: "minimal collection import",
@@ -306,7 +312,7 @@ func TestImportCollections(t *testing.T) {
]`,
deleteMissing: false,
expectError: false,
expectCollectionsCount: 9,
expectCollectionsCount: 10,
},
{
name: "minimal collection import + failed beforeRecordsSync",
@@ -318,7 +324,7 @@ func TestImportCollections(t *testing.T) {
},
deleteMissing: false,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
},
{
name: "minimal collection import + successful beforeRecordsSync",
@@ -330,7 +336,7 @@ func TestImportCollections(t *testing.T) {
},
deleteMissing: false,
expectError: false,
expectCollectionsCount: 8,
expectCollectionsCount: 9,
},
{
name: "new + update + delete system collection",
@@ -366,7 +372,7 @@ func TestImportCollections(t *testing.T) {
]`,
deleteMissing: true,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
},
{
name: "new + update + delete non-system collection",
@@ -495,7 +501,7 @@ func TestImportCollections(t *testing.T) {
]`,
deleteMissing: false,
expectError: false,
expectCollectionsCount: 8,
expectCollectionsCount: 9,
afterTestFunc: func(testApp *tests.TestApp, resultCollections []*models.Collection) {
expectedCollectionFields := map[string]int{
"nologin": 1,
@@ -503,6 +509,7 @@ func TestImportCollections(t *testing.T) {
"demo2": 2,
"demo3": 2,
"demo4": 11,
"demo5": 5,
"new_import": 1,
}
for name, expectedCount := range expectedCollectionFields {
+18 -47
View File
@@ -3,7 +3,6 @@ package daos
import (
"errors"
"fmt"
"math"
"strings"
"github.com/pocketbase/dbx"
@@ -84,7 +83,7 @@ func (dao *Dao) FindRecordsByIds(
}
}
rows := []dbx.NullStringMap{}
rows := make([]dbx.NullStringMap, 0, len(recordIds))
if err := query.All(&rows); err != nil {
return nil, err
}
@@ -192,8 +191,7 @@ func (dao *Dao) IsRecordValueUnique(
AndWhere(expr).
Limit(1)
if len(excludeIds) > 0 {
uniqueExcludeIds := list.NonzeroUniques(excludeIds)
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
query.AndWhere(dbx.NotIn(collection.Name+".id", list.ToInterfaceSlice(uniqueExcludeIds)...))
}
@@ -390,8 +388,7 @@ func (dao *Dao) DeleteRecord(record *models.Record) error {
})
}
// cascadeRecordDelete triggers cascade deletion for the provided references
// and split the work to a batched set of go routines.
// cascadeRecordDelete triggers cascade deletion for the provided references.
//
// NB! This method is expected to be called inside a transaction.
func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.Collection][]*schema.SchemaField) error {
@@ -401,20 +398,21 @@ func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.
for _, field := range fields {
recordTableName := inflector.Columnify(refCollection.Name)
prefixedFieldName := recordTableName + "." + inflector.Columnify(field.Name)
// @todo optimize single relation lookup in v0.12+
query := dao.RecordQuery(refCollection).
Distinct(true).
LeftJoin(fmt.Sprintf(
// note: the case is used to normalize value access for single and multiple relations.
AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id})).
InnerJoin(fmt.Sprintf(
// note: the case is used to normalize the value access
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END) as {{%s}}`,
prefixedFieldName, prefixedFieldName, prefixedFieldName, uniqueJsonEachAlias,
), nil).
AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id})).
AndWhere(dbx.HashExp{uniqueJsonEachAlias + ".value": mainRecord.Id})
), dbx.HashExp{uniqueJsonEachAlias + ".value": mainRecord.Id})
// trigger cascade for each 1000 rel items until there is none
batchSize := 1000
// trigger cascade for each batchSize rel items until there is none
batchSize := 4000
rows := make([]dbx.NullStringMap, 0, batchSize)
for {
rows := make([]dbx.NullStringMap, 0, batchSize)
if err := query.Limit(int64(batchSize)).All(&rows); err != nil {
return err
}
@@ -424,45 +422,18 @@ func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.
break
}
perWorker := 50
workers := int(math.Ceil(float64(total) / float64(perWorker)))
refRecords := models.NewRecordsFromNullStringMaps(refCollection, rows)
batchErr := func() error {
ch := make(chan error)
defer close(ch)
for i := 0; i < workers; i++ {
var chunks []dbx.NullStringMap
if len(rows) <= perWorker {
chunks = rows
rows = nil
} else {
chunks = rows[:perWorker]
rows = rows[perWorker:]
}
go func() {
refRecords := models.NewRecordsFromNullStringMaps(refCollection, chunks)
ch <- dao.deleteRefRecords(mainRecord, refRecords, field)
}()
}
for i := 0; i < workers; i++ {
if err := <-ch; err != nil {
return err
}
}
return nil
}()
if batchErr != nil {
return batchErr
err := dao.deleteRefRecords(mainRecord, refRecords, field)
if err != nil {
return err
}
if total < batchSize {
break // no more items
}
rows = rows[:0] // keep allocated memory
}
}
}
+2 -2
View File
@@ -663,11 +663,11 @@ func TestDeleteRecord(t *testing.T) {
}
// ensure that the json rel fields were prefixed
joinedQueries := strings.Join(calledQueries, " ")
expectedRelManyJoin := "`demo1` LEFT JOIN json_each(CASE WHEN json_valid([[demo1.rel_many]]) THEN [[demo1.rel_many]] ELSE json_array([[demo1.rel_many]]) END)"
expectedRelManyJoin := "`demo1` INNER JOIN json_each(CASE WHEN json_valid([[demo1.rel_many]]) THEN [[demo1.rel_many]] ELSE json_array([[demo1.rel_many]]) END)"
if !strings.Contains(joinedQueries, expectedRelManyJoin) {
t.Fatalf("(rec3) Expected the cascade delete to call the query \n%v, got \n%v", expectedRelManyJoin, calledQueries)
}
expectedRelOneJoin := "`demo1` LEFT JOIN json_each(CASE WHEN json_valid([[demo1.rel_one]]) THEN [[demo1.rel_one]] ELSE json_array([[demo1.rel_one]]) END)"
expectedRelOneJoin := "`demo1` INNER JOIN json_each(CASE WHEN json_valid([[demo1.rel_one]]) THEN [[demo1.rel_one]] ELSE json_array([[demo1.rel_one]]) END)"
if !strings.Contains(joinedQueries, expectedRelOneJoin) {
t.Fatalf("(rec3) Expected the cascade delete to call the query \n%v, got \n%v", expectedRelOneJoin, calledQueries)
}
+1 -1
View File
@@ -215,7 +215,7 @@ func (form *CollectionUpsert) ensureExistingRelationCollectionId(value any) erro
continue
}
if _, err := form.dao.FindCollectionByNameOrId(options.CollectionId); err != nil {
if err := form.dao.FindById(&models.Collection{}, options.CollectionId); err != nil {
return validation.Errors{fmt.Sprint(i): validation.NewError(
"validation_field_invalid_relation",
"The relation collection doesn't exist.",
+9 -9
View File
@@ -52,7 +52,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
"collections": []
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: nil,
},
{
@@ -82,7 +82,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeCreate": 2,
},
@@ -101,7 +101,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeCreate": 2,
},
@@ -137,7 +137,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: false,
expectCollectionsCount: 10,
expectCollectionsCount: 11,
expectEvents: map[string]int{
"OnModelBeforeCreate": 3,
"OnModelAfterCreate": 3,
@@ -160,7 +160,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeCreate": 1,
},
@@ -202,7 +202,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: true,
expectCollectionsCount: 7,
expectCollectionsCount: 8,
expectEvents: map[string]int{
"OnModelBeforeDelete": 5,
},
@@ -253,7 +253,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
]
}`,
expectError: false,
expectCollectionsCount: 9,
expectCollectionsCount: 10,
expectEvents: map[string]int{
"OnModelBeforeUpdate": 1,
"OnModelAfterUpdate": 1,
@@ -341,8 +341,8 @@ func TestCollectionsImportSubmit(t *testing.T) {
"OnModelAfterUpdate": 2,
"OnModelBeforeCreate": 1,
"OnModelAfterCreate": 1,
"OnModelBeforeDelete": 5,
"OnModelAfterDelete": 5,
"OnModelBeforeDelete": 6,
"OnModelAfterDelete": 6,
},
},
}
+40 -36
View File
@@ -7,7 +7,6 @@ import (
"log"
"net/http"
"regexp"
"strconv"
"strings"
validation "github.com/go-ozzo/ozzo-validation/v4"
@@ -219,11 +218,6 @@ func (form *RecordUpsert) extractMultipartFormData(
// and lods it into the form.
//
// File upload is supported only via multipart/form-data.
//
// To DELETE previously uploaded file(s) you can suffix the field name
// with the file index or filename (eg. `myfile.0`) and set it to null or empty string.
// For single file upload fields, you can skip the index and directly
// reset the field using its field name (eg. `myfile = null`).
func (form *RecordUpsert) LoadRequest(r *http.Request, keyPrefix string) error {
requestData, uploadedFiles, err := form.extractRequestData(r, keyPrefix)
if err != nil {
@@ -345,29 +339,24 @@ func (form *RecordUpsert) RemoveFiles(key string, toDelete ...string) error {
}
// LoadData loads and normalizes the provided regular record data fields into the form.
//
// To DELETE previously uploaded file(s) you can suffix the field name
// with the file index or filename (eg. `myfile.0`) and set it to null or empty string.
// For single file upload fields, you can skip the index and directly
// reset the field using its field name (eg. `myfile = null`).
func (form *RecordUpsert) LoadData(requestData map[string]any) error {
// load base system fields
if v, ok := requestData["id"]; ok {
if v, ok := requestData[schema.FieldNameId]; ok {
form.Id = cast.ToString(v)
}
// load auth system fields
if form.record.Collection().IsAuth() {
if v, ok := requestData["username"]; ok {
if v, ok := requestData[schema.FieldNameUsername]; ok {
form.Username = cast.ToString(v)
}
if v, ok := requestData["email"]; ok {
if v, ok := requestData[schema.FieldNameEmail]; ok {
form.Email = cast.ToString(v)
}
if v, ok := requestData["emailVisibility"]; ok {
if v, ok := requestData[schema.FieldNameEmailVisibility]; ok {
form.EmailVisibility = cast.ToBool(v)
}
if v, ok := requestData["verified"]; ok {
if v, ok := requestData[schema.FieldNameVerified]; ok {
form.Verified = cast.ToBool(v)
}
if v, ok := requestData["password"]; ok {
@@ -381,8 +370,16 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
}
}
// extend the record schema data with the request data
extendedData := form.record.SchemaData()
// replace modifiers (if any)
requestData = form.record.ReplaceModifers(requestData)
// create a shallow copy of form.data
var extendedData = make(map[string]any, len(form.data))
for k, v := range form.data {
extendedData[k] = v
}
// extend form.data with the request data
rawData, err := json.Marshal(requestData)
if err != nil {
return err
@@ -393,8 +390,7 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
for _, field := range form.record.Collection().Schema.Fields() {
key := field.Name
value := extendedData[key]
value = field.PrepareValue(value)
value := field.PrepareValue(extendedData[key])
if field.Type != schema.FieldTypeFile {
form.data[key] = value
@@ -405,30 +401,32 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
// Delete previously uploaded file(s)
// -----------------------------------------------------------
oldNames := list.ToUniqueStringSlice(form.data[key])
oldNames := form.record.GetStringSlice(key)
submittedNames := list.ToUniqueStringSlice(value)
// ensure that all submitted names are existing to prevent accidental files deletions
if len(submittedNames) > len(oldNames) || len(list.SubtractSlice(submittedNames, oldNames)) != 0 {
return validation.Errors{
key: validation.NewError(
"validation_unknown_filenames",
"The field contains unknown filenames.",
),
}
}
// if empty value was set, mark all previously uploaded files for deletion
if len(list.ToUniqueStringSlice(value)) == 0 && len(oldNames) > 0 {
// otherwise check for "deleted" (aka. unsubmitted) file names
if len(submittedNames) == 0 && len(oldNames) > 0 {
form.RemoveFiles(key)
} else if len(oldNames) > 0 {
toDelete := []string{}
// search for individual file name to delete (eg. "file.test.png = null")
for _, name := range oldNames {
if v, ok := extendedData[key+"."+name]; ok && cast.ToString(v) == "" {
// submitted as a modifier or a new array
if !list.ExistInSlice(name, submittedNames) {
toDelete = append(toDelete, name)
}
}
// search for individual file index to delete (eg. "file.0 = null")
keyExp, _ := regexp.Compile(`^` + regexp.QuoteMeta(key) + `\.\d+$`)
for indexedKey := range extendedData {
if keyExp.MatchString(indexedKey) && cast.ToString(extendedData[indexedKey]) == "" {
index, indexErr := strconv.Atoi(indexedKey[len(key)+1:])
if indexErr != nil || index >= len(oldNames) {
continue
}
toDelete = append(toDelete, oldNames[index])
continue
}
}
@@ -436,6 +434,12 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
form.RemoveFiles(key, toDelete...)
}
}
// allow file key reasignments for file names sorting
// (only if all submitted values already exists)
if len(submittedNames) > 0 && len(list.SubtractSlice(submittedNames, oldNames)) == 0 {
form.data[key] = submittedNames
}
}
return nil
+16 -14
View File
@@ -89,9 +89,10 @@ func TestRecordUpsertLoadRequestJson(t *testing.T) {
"unknown": "test456",
// file fields unset/delete
"file_one": nil,
"file_many.0": "", // delete by index
"file_many.1": "test.png", // should be ignored
"file_many.300_WlbFWSGmW9.png": nil, // delete by filename
"file_many.0": "", // delete by index
"file_many-": []string{"test_MaWC6mWyrP.txt", "test_tC1Yc87DfC.txt"}, // multiple delete with modifier
"file_many.300_WlbFWSGmW9.png": nil, // delete by filename
"file_many.2": "test.png", // should be ignored
},
},
}
@@ -149,11 +150,12 @@ func TestRecordUpsertLoadRequestMultipart(t *testing.T) {
"a.b.text": "test123",
"a.b.unknown": "test456",
// file fields unset/delete
"a.b.file_one": "",
"a.b.file_many.0": "",
"a.b.file_many.300_WlbFWSGmW9.png": "test.png", // delete by name
"a.b.file_many.1": "test.png", // should be ignored
}, "file_many")
"a.b.file_one-": "test_d61b33QdDU.txt", // delete with modifier
"a.b.file_many.0": "", // delete by index
"a.b.file_many-": "test_tC1Yc87DfC.txt", // delete with modifier
"a.b.file_many.300_WlbFWSGmW9.png": "", // delete by filename
"a.b.file_many.2": "test.png", // should be ignored
}, "a.b.file_many")
if err != nil {
t.Fatal(err)
}
@@ -191,7 +193,7 @@ func TestRecordUpsertLoadRequestMultipart(t *testing.T) {
t.Fatal("Expect file_many field to be set")
}
manyfilesRemains := len(list.ToUniqueStringSlice(fileMany))
expectedRemains := 2 // -2 from 3 removed + 1 new upload
expectedRemains := 3 // 5 old; 3 deleted and 1 new uploaded
if manyfilesRemains != expectedRemains {
t.Fatalf("Expect file_many to be %d, got %d (%v)", expectedRemains, manyfilesRemains, fileMany)
}
@@ -465,7 +467,7 @@ func TestRecordUpsertSubmitFailure(t *testing.T) {
if v := recordAfter.Get("email"); v == "invalid" {
t.Fatalf("Expected record.email not to change, got %v", v)
}
if v := recordAfter.GetStringSlice("file_many"); len(v) != 3 {
if v := recordAfter.GetStringSlice("file_many"); len(v) != 5 {
t.Fatalf("Expected record.file_many not to change, got %v", v)
}
@@ -537,8 +539,8 @@ func TestRecordUpsertSubmitSuccess(t *testing.T) {
}
fileMany := (recordAfter.GetStringSlice("file_many"))
if len(fileMany) != 4 { // 1 replace + 1 new
t.Fatalf("Expected 4 record.file_many, got %d (%v)", len(fileMany), fileMany)
if len(fileMany) != 6 { // 1 replace + 1 new
t.Fatalf("Expected 6 record.file_many, got %d (%v)", len(fileMany), fileMany)
}
for _, f := range fileMany {
if !hasRecordFile(app, recordAfter, f) {
@@ -1009,7 +1011,7 @@ func TestRecordUpsertAddAndRemoveFiles(t *testing.T) {
}
fileMany := recordAfter.GetStringSlice("file_many")
if len(fileMany) != 3 {
t.Fatalf("Expected file_many to be 3, got %v", fileMany)
if len(fileMany) != 5 {
t.Fatalf("Expected file_many to be 5, got %v", fileMany)
}
}
+25 -24
View File
@@ -4,14 +4,14 @@ go 1.18
require (
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/aws/aws-sdk-go v1.44.161
github.com/aws/aws-sdk-go v1.44.178
github.com/disintegration/imaging v1.6.2
github.com/domodwyer/mailyak/v3 v3.3.4
github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86
github.com/dop251/goja_nodejs v0.0.0-20221009164102-3aa5028e57f6
github.com/fatih/color v1.13.0
github.com/gabriel-vasile/mimetype v1.4.1
github.com/ganigeorgiev/fexpr v0.1.1
github.com/ganigeorgiev/fexpr v0.3.0
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/golang-jwt/jwt/v4 v4.4.3
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198
@@ -19,22 +19,22 @@ require (
github.com/pocketbase/dbx v1.8.0
github.com/spf13/cast v1.5.0
github.com/spf13/cobra v1.6.1
gocloud.dev v0.27.0
golang.org/x/crypto v0.4.0
golang.org/x/net v0.4.0
golang.org/x/oauth2 v0.3.0
gocloud.dev v0.28.0
golang.org/x/crypto v0.5.0
golang.org/x/net v0.5.0
golang.org/x/oauth2 v0.4.0
golang.org/x/sync v0.1.0
modernc.org/sqlite v1.20.0
modernc.org/sqlite v1.20.2
)
require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.5 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.44 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.47 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
@@ -43,12 +43,13 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.27 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
@@ -59,30 +60,30 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/image v0.2.0 // indirect
golang.org/x/image v0.3.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/term v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/term v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.4.0 // indirect
golang.org/x/tools v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.105.0 // indirect
google.golang.org/api v0.106.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.21.5 // indirect
modernc.org/libc v1.22.2 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/opt v0.1.3 // indirect
+589 -42
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -133,6 +133,7 @@ func init() {
"image/png",
"image/svg+xml",
"image/gif",
"image/webp",
},
},
},
@@ -0,0 +1,215 @@
package migrations
import (
"regexp"
"strings"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/models/schema"
)
// This migration replaces for backward compatibility the default operators
// (=, !=, >, etc.) with their any/opt equivalent (?=, ?=, ?>, etc.)
// in any muli-rel expression collection rule.
func init() {
AppMigrations.Register(func(db dbx.Builder) error {
dao := daos.New(db)
exprRegex := regexp.MustCompile(`([\@\'\"\w\.]+)\s*(=|!=|~|!~|>|>=|<|<=)\s*([\@\'\"\w\.]+)`)
collections := []*models.Collection{}
if err := dao.CollectionQuery().All(&collections); err != nil {
return err
}
findCollection := func(nameOrId string) *models.Collection {
for _, c := range collections {
if c.Id == nameOrId || c.Name == nameOrId {
return c
}
}
return nil
}
var isMultiRelLiteral func(mainCollection *models.Collection, literal string) bool
isMultiRelLiteral = func(mainCollection *models.Collection, literal string) bool {
if strings.HasPrefix(literal, "@collection.") {
return true
}
if strings.HasPrefix(literal, `"`) ||
strings.HasPrefix(literal, `'`) ||
strings.HasPrefix(literal, "@request.method") ||
strings.HasPrefix(literal, "@request.data") ||
strings.HasPrefix(literal, "@request.query") {
return false
}
parts := strings.Split(literal, ".")
if len(parts) <= 1 {
return false
}
if strings.HasPrefix(literal, "@request.auth") && len(parts) >= 4 {
// check each auth collection
for _, c := range collections {
if c.IsAuth() && isMultiRelLiteral(c, strings.Join(parts[2:], ".")) {
return true
}
}
return false
}
activeCollection := mainCollection
for i, p := range parts {
f := activeCollection.Schema.GetFieldByName(p)
if f == nil || f.Type != schema.FieldTypeRelation {
return false // not a relation field
}
// is multi-relation and not the last prop
opt, ok := f.Options.(*schema.RelationOptions)
if ok && (opt.MaxSelect == nil || *opt.MaxSelect != 1) && i != len(parts)-1 {
return true
}
activeCollection = findCollection(opt.CollectionId)
if activeCollection == nil {
return false
}
}
return false
}
// replace all multi-match operators to their any/opt equivalent, eg. "=" => "?="
migrateRule := func(collection *models.Collection, rule *string) (*string, error) {
if rule == nil || *rule == "" {
return rule, nil
}
newRule := *rule
parts := exprRegex.FindAllStringSubmatch(newRule, -1)
for _, p := range parts {
if isMultiRelLiteral(collection, p[1]) || isMultiRelLiteral(collection, p[3]) {
newRule = strings.ReplaceAll(newRule, p[0], p[1]+" ?"+p[2]+" "+p[3])
}
}
return &newRule, nil
}
var ruleErr error
for _, c := range collections {
c.ListRule, ruleErr = migrateRule(c, c.ListRule)
if ruleErr != nil {
return ruleErr
}
c.ViewRule, ruleErr = migrateRule(c, c.ViewRule)
if ruleErr != nil {
return ruleErr
}
c.CreateRule, ruleErr = migrateRule(c, c.CreateRule)
if ruleErr != nil {
return ruleErr
}
c.UpdateRule, ruleErr = migrateRule(c, c.UpdateRule)
if ruleErr != nil {
return ruleErr
}
c.DeleteRule, ruleErr = migrateRule(c, c.DeleteRule)
if ruleErr != nil {
return ruleErr
}
if c.IsAuth() {
opt := c.AuthOptions()
opt.ManageRule, ruleErr = migrateRule(c, opt.ManageRule)
if ruleErr != nil {
return ruleErr
}
c.SetOptions(opt)
}
if err := dao.Save(c); err != nil {
return err
}
}
return nil
}, func(db dbx.Builder) error {
dao := daos.New(db)
collections := []*models.Collection{}
if err := dao.CollectionQuery().All(&collections); err != nil {
return err
}
anyOpRegex := regexp.MustCompile(`\?(=|!=|~|!~|>|>=|<|<=)`)
// replace any/opt operators to their old versions, eg. "?=" => "="
revertRule := func(rule *string) (*string, error) {
if rule == nil || *rule == "" {
return rule, nil
}
newRule := *rule
newRule = anyOpRegex.ReplaceAllString(newRule, "${1}")
return &newRule, nil
}
var ruleErr error
for _, c := range collections {
c.ListRule, ruleErr = revertRule(c.ListRule)
if ruleErr != nil {
return ruleErr
}
c.ViewRule, ruleErr = revertRule(c.ViewRule)
if ruleErr != nil {
return ruleErr
}
c.CreateRule, ruleErr = revertRule(c.CreateRule)
if ruleErr != nil {
return ruleErr
}
c.UpdateRule, ruleErr = revertRule(c.UpdateRule)
if ruleErr != nil {
return ruleErr
}
c.DeleteRule, ruleErr = revertRule(c.DeleteRule)
if ruleErr != nil {
return ruleErr
}
if c.IsAuth() {
opt := c.AuthOptions()
opt.ManageRule, ruleErr = revertRule(opt.ManageRule)
if ruleErr != nil {
return ruleErr
}
c.SetOptions(opt)
}
if err := dao.Save(c); err != nil {
return err
}
}
return nil
})
}
+107 -6
View File
@@ -4,6 +4,8 @@ import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strconv"
"time"
"github.com/pocketbase/dbx"
@@ -57,7 +59,7 @@ func nullStringMapValue(data dbx.NullStringMap, key string) any {
// NewRecordFromNullStringMap initializes a single new Record model
// with data loaded from the provided NullStringMap.
func NewRecordFromNullStringMap(collection *Collection, data dbx.NullStringMap) *Record {
resultMap := map[string]any{}
resultMap := make(map[string]any, len(data))
// load schema fields
for _, field := range collection.Schema.Fields() {
@@ -139,6 +141,10 @@ func (m *Record) SetExpand(expand map[string]any) {
// then both old and new records will be merged into a new slice (aka. a :merge: [b,c] => [a,b,c]).
// Otherwise the "old" expanded record will be replace with the "new" one (aka. a :merge: aNew => aNew).
func (m *Record) MergeExpand(expand map[string]any) {
if m.expand == nil && len(expand) > 0 {
m.expand = make(map[string]any, len(expand))
}
for key, new := range expand {
old, ok := m.expand[key]
if !ok {
@@ -174,7 +180,7 @@ func (m *Record) MergeExpand(expand map[string]any) {
continue
}
oldIndexed := map[string]*Record{}
oldIndexed := make(map[string]*Record, len(oldSlice))
for _, oldRecord := range oldSlice {
oldIndexed[oldRecord.Id] = oldRecord
}
@@ -190,7 +196,7 @@ func (m *Record) MergeExpand(expand map[string]any) {
}
}
if wasOldSlice || wasNewSlice {
if wasOldSlice || wasNewSlice || len(oldSlice) == 0 {
m.expand[key] = oldSlice
} else {
m.expand[key] = oldSlice[0]
@@ -200,7 +206,7 @@ func (m *Record) MergeExpand(expand map[string]any) {
// SchemaData returns a shallow copy ONLY of the defined record schema fields data.
func (m *Record) SchemaData() map[string]any {
result := map[string]any{}
result := make(map[string]any, len(m.collection.Schema.Fields()))
for _, field := range m.collection.Schema.Fields() {
if v, ok := m.data[field.Name]; ok {
@@ -366,7 +372,7 @@ func (m *Record) Load(data map[string]any) {
// ColumnValueMap implements [ColumnValueMapper] interface.
func (m *Record) ColumnValueMap() map[string]any {
result := map[string]any{}
result := make(map[string]any, len(m.collection.Schema.Fields())+3)
// export schema field values
for _, field := range m.collection.Schema.Fields() {
@@ -392,7 +398,7 @@ func (m *Record) ColumnValueMap() map[string]any {
//
// Fields marked as hidden will be exported only if `m.IgnoreEmailVisibility(true)` is set.
func (m *Record) PublicExport() map[string]any {
result := map[string]any{}
result := make(map[string]any, len(m.collection.Schema.Fields())+5)
// export unknown data fields if allowed
if m.exportUnknown {
@@ -453,6 +459,101 @@ func (m *Record) UnmarshalJSON(data []byte) error {
return nil
}
// ReplaceModifers returns a new map with applied modifier
// values based on the current record and the specified data.
//
// The resolved modifier keys will be removed.
//
// Multiple modifiers will be applied one after another,
// while reusing the previous base key value result (eg. 1; -5; +2 => -2).
//
// Example usage:
//
// newData := record.ReplaceModifers(data)
// // record: {"field": 10}
// // data: {"field+": 5}
// // newData: {"field": 15}
func (m *Record) ReplaceModifers(data map[string]any) map[string]any {
var clone = shallowCopy(data)
if len(clone) == 0 {
return clone
}
var recordDataCache map[string]any
// export recordData lazily
recordData := func() map[string]any {
if recordDataCache == nil {
recordDataCache = m.SchemaData()
}
return recordDataCache
}
modifiers := schema.FieldValueModifiers()
for _, field := range m.Collection().Schema.Fields() {
key := field.Name
for _, m := range modifiers {
if mv, mOk := clone[key+m]; mOk {
if _, ok := clone[key]; !ok {
// get base value from the merged data
clone[key] = recordData()[key]
}
clone[key] = field.PrepareValueWithModifier(clone[key], m, mv)
delete(clone, key+m)
}
}
if field.Type != schema.FieldTypeFile {
continue
}
// -----------------------------------------------------------
// legacy file field modifiers (kept for backward compatability)
// -----------------------------------------------------------
var oldNames []string
var toDelete []string
if _, ok := clone[key]; ok {
oldNames = list.ToUniqueStringSlice(clone[key])
} else {
// get oldNames from the model
oldNames = list.ToUniqueStringSlice(recordData()[key])
}
// search for individual file name to delete (eg. "file.test.png = null")
for _, name := range oldNames {
suffixedKey := key + "." + name
if v, ok := clone[suffixedKey]; ok && cast.ToString(v) == "" {
toDelete = append(toDelete, name)
delete(clone, suffixedKey)
continue
}
}
// search for individual file index to delete (eg. "file.0 = null")
keyExp, _ := regexp.Compile(`^` + regexp.QuoteMeta(key) + `\.\d+$`)
for indexedKey := range clone {
if keyExp.MatchString(indexedKey) && cast.ToString(clone[indexedKey]) == "" {
index, indexErr := strconv.Atoi(indexedKey[len(key)+1:])
if indexErr != nil || index < 0 || index >= len(oldNames) {
continue
}
toDelete = append(toDelete, oldNames[index])
delete(clone, indexedKey)
}
}
if toDelete != nil {
clone[key] = field.PrepareValue(list.SubtractSlice(oldNames, toDelete))
}
}
return clone
}
// getNormalizeDataValueForDB returns the "key" data value formatted for db storage.
func (m *Record) getNormalizeDataValueForDB(key string) any {
var val any
+148 -4
View File
@@ -138,7 +138,7 @@ func TestNewRecordFromNullStringMap(t *testing.T) {
Valid: true,
},
"field5": sql.NullString{
String: `["test1","test2"]`, // will select only the first elem
String: `["test1","test2"]`, // will select only the last elem
Valid: true,
},
"field6": sql.NullString{
@@ -157,11 +157,11 @@ func TestNewRecordFromNullStringMap(t *testing.T) {
}{
{
models.CollectionTypeBase,
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test1","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z"}`,
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test2","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z"}`,
},
{
models.CollectionTypeAuth,
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test1","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z","username":"test_username","verified":false}`,
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test2","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z","username":"test_username","verified":false}`,
},
}
@@ -375,7 +375,7 @@ func TestRecordSetAndGetExpand(t *testing.T) {
}
}
func TestRecordMergeExpandDirect(t *testing.T) {
func TestRecordMergeExpand(t *testing.T) {
collection := &models.Collection{}
m := models.NewRecord(collection)
m.Id = "m"
@@ -467,6 +467,47 @@ func TestRecordMergeExpandDirect(t *testing.T) {
}
}
func TestRecordMergeExpandNilCheck(t *testing.T) {
collection := &models.Collection{}
scenarios := []struct {
name string
expand map[string]any
expected string
}{
{
"nil expand",
nil,
`{"collectionId":"","collectionName":"","created":"","id":"","updated":""}`,
},
{
"empty expand",
map[string]any{},
`{"collectionId":"","collectionName":"","created":"","id":"","updated":""}`,
},
{
"non-empty expand",
map[string]any{"test": models.NewRecord(collection)},
`{"collectionId":"","collectionName":"","created":"","expand":{"test":{"collectionId":"","collectionName":"","created":"","id":"","updated":""}},"id":"","updated":""}`,
},
}
for _, s := range scenarios {
m := models.NewRecord(collection)
m.MergeExpand(s.expand)
raw, err := json.Marshal(m)
if err != nil {
t.Fatal(err)
}
rawStr := string(raw)
if rawStr != s.expected {
t.Fatalf("[%s] Expected \n%v, \ngot \n%v", s.name, s.expected, rawStr)
}
}
}
func TestRecordSchemaData(t *testing.T) {
collection := &models.Collection{
Type: models.CollectionTypeAuth,
@@ -1384,6 +1425,109 @@ func TestRecordUnmarshalJSON(t *testing.T) {
}
}
func TestRecordReplaceModifers(t *testing.T) {
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "text",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "number",
Type: schema.FieldTypeNumber,
},
&schema.SchemaField{
Name: "rel_one",
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)},
},
&schema.SchemaField{
Name: "rel_many",
Type: schema.FieldTypeRelation,
},
&schema.SchemaField{
Name: "select_one",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "select_many",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 10},
},
&schema.SchemaField{
Name: "file_one",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "file_one_index",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "file_one_name",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "file_many",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 10},
},
),
}
record := models.NewRecord(collection)
record.Load(map[string]any{
"text": "test",
"number": 10,
"rel_one": "a",
"rel_many": []string{"a", "b"},
"select_one": "a",
"select_many": []string{"a", "b", "c"},
"file_one": "a",
"file_one_index": "b",
"file_one_name": "c",
"file_many": []string{"a", "b", "c", "d", "e", "f"},
})
result := record.ReplaceModifers(map[string]any{
"text-": "m-",
"text+": "m+",
"number-": 3,
"number+": 5,
"rel_one-": "a",
"rel_one+": "b",
"rel_many-": []string{"a"},
"rel_many+": []string{"c", "d", "e"},
"select_one-": "a",
"select_one+": "c",
"select_many-": []string{"b", "c"},
"select_many+": []string{"d", "e"},
"file_one+": "skip", // should be ignored
"file_one-": "a",
"file_one_index.0": "",
"file_one_name.c": "",
"file_many+": []string{"e", "f"}, // should be ignored
"file_many-": []string{"c", "d"},
"file_many.f": nil,
"file_many.0": nil,
})
raw, err := json.Marshal(result)
if err != nil {
t.Fatal(err)
}
expected := `{"file_many":["b","e"],"file_one":"","file_one_index":"","file_one_name":"","number":12,"rel_many":["b","c","d","e"],"rel_one":"b","select_many":["a","d","e"],"select_one":"c","text":"test"}`
if v := string(raw); v != expected {
t.Fatalf("Expected \n%s, \ngot \n%s", expected, v)
}
}
// -------------------------------------------------------------------
// Auth helpers:
// -------------------------------------------------------------------
+21
View File
@@ -1,5 +1,11 @@
package models
import (
"strings"
"github.com/pocketbase/pocketbase/models/schema"
)
// RequestData defines a HTTP request data struct, usually used
// as part of the `@request.*` filter resolver.
type RequestData struct {
@@ -9,3 +15,18 @@ type RequestData struct {
AuthRecord *Record `json:"authRecord"`
Admin *Admin `json:"admin"`
}
// HasModifierDataKeys loosely checks if the current struct has any modifier Data keys.
func (r *RequestData) HasModifierDataKeys() bool {
allModifiers := schema.FieldValueModifiers()
for key := range r.Data {
for _, m := range allModifiers {
if strings.HasSuffix(key, m) {
return true
}
}
}
return false
}
+58
View File
@@ -0,0 +1,58 @@
package models_test
import (
"testing"
"github.com/pocketbase/pocketbase/models"
)
func TestRequestDataHasModifierDataKeys(t *testing.T) {
scenarios := []struct {
name string
requestData *models.RequestData
expected bool
}{
{
"empty",
&models.RequestData{},
false,
},
{
"Data with regular fields",
&models.RequestData{
Query: map[string]any{"data+": "demo"}, // should be ignored
Data: map[string]any{"a": 123, "b": "test", "c.d": false},
},
false,
},
{
"Data with +modifier fields",
&models.RequestData{
Data: map[string]any{"a+": 123, "b": "test", "c.d": false},
},
true,
},
{
"Data with -modifier fields",
&models.RequestData{
Data: map[string]any{"a": 123, "b-": "test", "c.d": false},
},
true,
},
{
"Data with mixed modifier fields",
&models.RequestData{
Data: map[string]any{"a": 123, "b-": "test", "c.d+": false},
},
true,
},
}
for _, s := range scenarios {
result := s.requestData.HasModifierDataKeys()
if result != s.expected {
t.Fatalf("[%s] Expected %v, got %v", s.name, s.expected, result)
}
}
}
+73 -19
View File
@@ -15,22 +15,36 @@ import (
var schemaFieldNameRegex = regexp.MustCompile(`^\w+$`)
// field value modifiers
const (
FieldValueModifierAdd string = "+"
FieldValueModifierSubtract string = "-"
)
// FieldValueModifiers returns a list with all available field modifier tokens.
func FieldValueModifiers() []string {
return []string{
FieldValueModifierAdd,
FieldValueModifierSubtract,
}
}
// commonly used field names
const (
FieldNameId = "id"
FieldNameCreated = "created"
FieldNameUpdated = "updated"
FieldNameCollectionId = "collectionId"
FieldNameCollectionName = "collectionName"
FieldNameExpand = "expand"
FieldNameUsername = "username"
FieldNameEmail = "email"
FieldNameEmailVisibility = "emailVisibility"
FieldNameVerified = "verified"
FieldNameTokenKey = "tokenKey"
FieldNamePasswordHash = "passwordHash"
FieldNameLastResetSentAt = "lastResetSentAt"
FieldNameLastVerificationSentAt = "lastVerificationSentAt"
FieldNameId string = "id"
FieldNameCreated string = "created"
FieldNameUpdated string = "updated"
FieldNameCollectionId string = "collectionId"
FieldNameCollectionName string = "collectionName"
FieldNameExpand string = "expand"
FieldNameUsername string = "username"
FieldNameEmail string = "email"
FieldNameEmailVisibility string = "emailVisibility"
FieldNameVerified string = "verified"
FieldNameTokenKey string = "tokenKey"
FieldNamePasswordHash string = "passwordHash"
FieldNameLastResetSentAt string = "lastResetSentAt"
FieldNameLastVerificationSentAt string = "lastVerificationSentAt"
)
// BaseModelFieldNames returns the field names that all models have (id, created, updated).
@@ -168,8 +182,8 @@ func (f SchemaField) Validate() error {
f.InitOptions()
excludeNames := BaseModelFieldNames()
// exclude filter literals
excludeNames = append(excludeNames, "null", "true", "false")
// exclude special filter literals
excludeNames = append(excludeNames, "null", "true", "false", "isset")
// exclude system literals
excludeNames = append(excludeNames, SystemFieldNames()...)
@@ -276,7 +290,7 @@ func (f *SchemaField) PrepareValue(value any) any {
options, _ := f.Options.(*SelectOptions)
if options.MaxSelect <= 1 {
if len(val) > 0 {
return val[0]
return val[len(val)-1] // the last selected
}
return ""
}
@@ -288,7 +302,7 @@ func (f *SchemaField) PrepareValue(value any) any {
options, _ := f.Options.(*FileOptions)
if options.MaxSelect <= 1 {
if len(val) > 0 {
return val[0]
return val[len(val)-1] // the last selected
}
return ""
}
@@ -300,7 +314,7 @@ func (f *SchemaField) PrepareValue(value any) any {
options, _ := f.Options.(*RelationOptions)
if options.MaxSelect != nil && *options.MaxSelect <= 1 {
if len(ids) > 0 {
return ids[0]
return ids[len(ids)-1] // the last selected
}
return ""
}
@@ -311,6 +325,46 @@ func (f *SchemaField) PrepareValue(value any) any {
}
}
// PrepareValueWithModifier returns normalized and properly formatted field value
// by "merging" baseValue with the modifierValue based on the specified modifier (+ or -).
func (f *SchemaField) PrepareValueWithModifier(baseValue any, modifier string, modifierValue any) any {
resolvedValue := baseValue
switch f.Type {
case FieldTypeNumber:
switch modifier {
case FieldValueModifierAdd:
resolvedValue = cast.ToFloat64(baseValue) + cast.ToFloat64(modifierValue)
case FieldValueModifierSubtract:
resolvedValue = cast.ToFloat64(baseValue) - cast.ToFloat64(modifierValue)
}
case FieldTypeSelect, FieldTypeRelation:
switch modifier {
case FieldValueModifierAdd:
resolvedValue = append(
list.ToUniqueStringSlice(baseValue),
list.ToUniqueStringSlice(modifierValue)...,
)
case FieldValueModifierSubtract:
resolvedValue = list.SubtractSlice(
list.ToUniqueStringSlice(baseValue),
list.ToUniqueStringSlice(modifierValue),
)
}
case FieldTypeFile:
// note: file for now supports only the subtract modifier
switch modifier {
case FieldValueModifierSubtract:
resolvedValue = list.SubtractSlice(
list.ToUniqueStringSlice(baseValue),
list.ToUniqueStringSlice(modifierValue),
)
}
}
return f.PrepareValue(resolvedValue)
}
// -------------------------------------------------------------------
// FieldOptions interfaces that defines common methods that every field options struct has.
+694 -4
View File
@@ -603,7 +603,7 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
{schema.SchemaField{Type: schema.FieldTypeSelect}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, 123, `"123"`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, []string{"test1", "test2"}, `"test1"`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, []string{"test1", "test2"}, `"test2"`},
{
// no values validation/filtering
schema.SchemaField{
@@ -680,7 +680,7 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
{schema.SchemaField{Type: schema.FieldTypeFile}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeFile}, 123, `"123"`},
{schema.SchemaField{Type: schema.FieldTypeFile}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeFile}, []string{"test1", "test2"}, `"test1"`},
{schema.SchemaField{Type: schema.FieldTypeFile}, []string{"test1", "test2"}, `"test2"`},
// file (multiple)
{
schema.SchemaField{
@@ -785,7 +785,7 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
{
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724"},
`"1ba88b4f-e9da-42f0-9764-9a55c953e724"`,
`"2ba88b4f-e9da-42f0-9764-9a55c953e724"`,
},
// relation (multiple)
{
@@ -863,6 +863,696 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
}
}
func TestSchemaFieldPrepareValueWithModifier(t *testing.T) {
scenarios := []struct {
name string
field schema.SchemaField
baseValue any
modifier string
modifierValue any
expectJson string
}{
// text
{
"text with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeText},
"base",
"+",
"new",
`"base"`,
},
{
"text with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeText},
"base",
"-",
"new",
`"base"`,
},
{
"text with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeText},
"base",
"?",
"new",
`"base"`,
},
{
"text cast check",
schema.SchemaField{Type: schema.FieldTypeText},
123,
"?",
"new",
`"123"`,
},
// number
{
"number with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeNumber},
1,
"+",
4,
`5`,
},
{
"number with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeNumber},
1,
"-",
4,
`-3`,
},
{
"number with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeNumber},
"1",
"?",
4,
`1`,
},
{
"number cast check",
schema.SchemaField{Type: schema.FieldTypeNumber},
"test",
"+",
"4",
`4`,
},
// bool
{
"bool with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeBool},
true,
"+",
false,
`true`,
},
{
"bool with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeBool},
true,
"-",
false,
`true`,
},
{
"bool with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeBool},
true,
"?",
false,
`true`,
},
{
"bool cast check",
schema.SchemaField{Type: schema.FieldTypeBool},
"true",
"?",
false,
`true`,
},
// email
{
"email with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeEmail},
"base",
"+",
"new",
`"base"`,
},
{
"email with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeEmail},
"base",
"-",
"new",
`"base"`,
},
{
"email with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeEmail},
"base",
"?",
"new",
`"base"`,
},
{
"email cast check",
schema.SchemaField{Type: schema.FieldTypeEmail},
123,
"?",
"new",
`"123"`,
},
// url
{
"url with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeUrl},
"base",
"+",
"new",
`"base"`,
},
{
"url with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeUrl},
"base",
"-",
"new",
`"base"`,
},
{
"url with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeUrl},
"base",
"?",
"new",
`"base"`,
},
{
"url cast check",
schema.SchemaField{Type: schema.FieldTypeUrl},
123,
"-",
"new",
`"123"`,
},
// date
{
"date with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeDate},
"2023-01-01 00:00:00.123",
"+",
"2023-02-01 00:00:00.456",
`"2023-01-01 00:00:00.123Z"`,
},
{
"date with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeDate},
"2023-01-01 00:00:00.123Z",
"-",
"2023-02-01 00:00:00.456Z",
`"2023-01-01 00:00:00.123Z"`,
},
{
"date with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeDate},
"2023-01-01 00:00:00.123",
"?",
"2023-01-01 00:00:00.456",
`"2023-01-01 00:00:00.123Z"`,
},
{
"date cast check",
schema.SchemaField{Type: schema.FieldTypeDate},
1672524000, // 2022-12-31 22:00:00.000Z
"+",
100,
`"2022-12-31 22:00:00.000Z"`,
},
// json
{
"json with '+' modifier",
schema.SchemaField{Type: schema.FieldTypeJson},
10,
"+",
5,
`10`,
},
{
"json with '+' modifier (slice)",
schema.SchemaField{Type: schema.FieldTypeJson},
[]string{"a", "b"},
"+",
"c",
`["a","b"]`,
},
{
"json with '-' modifier",
schema.SchemaField{Type: schema.FieldTypeJson},
10,
"-",
5,
`10`,
},
{
"json with '-' modifier (slice)",
schema.SchemaField{Type: schema.FieldTypeJson},
`["a","b"]`,
"-",
"c",
`["a","b"]`,
},
{
"json with unknown modifier",
schema.SchemaField{Type: schema.FieldTypeJson},
`"base"`,
"?",
`"new"`,
`"base"`,
},
// single select
{
"single select with '+' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"",
"+",
"b",
`"b"`,
},
{
"single select with '+' modifier (nonempty base)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"a",
"+",
"b",
`"b"`,
},
{
"single select with '-' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"",
"-",
"a",
`""`,
},
{
"single select with '-' modifier (nonempty base and empty modifier value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"a",
"-",
"",
`"a"`,
},
{
"single select with '-' modifier (nonempty base and different value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"a",
"-",
"b",
`"a"`,
},
{
"single select with '-' modifier (nonempty base and matching value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"a",
"-",
"a",
`""`,
},
{
"single select with '-' modifier (nonempty base and matching value in a slice)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"a",
"-",
[]string{"b", "a", "c", "123"},
`""`,
},
{
"single select with unknown modifier (nonempty)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
"",
"?",
"a",
`""`,
},
// multi select
{
"multi select with '+' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
nil,
"+",
"b",
`["b"]`,
},
{
"multi select with '+' modifier (nonempty base)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
[]string{"a"},
"+",
[]string{"b", "c"},
`["a","b","c"]`,
},
{
"multi select with '+' modifier (nonempty base; already existing value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
[]string{"a", "b"},
"+",
"b",
`["a","b"]`,
},
{
"multi select with '-' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
nil,
"-",
[]string{"a"},
`[]`,
},
{
"multi select with '-' modifier (nonempty base and empty modifier value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
"a",
"-",
"",
`["a"]`,
},
{
"multi select with '-' modifier (nonempty base and different value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
"a",
"-",
"b",
`["a"]`,
},
{
"multi select with '-' modifier (nonempty base and matching value)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
[]string{"a", "b", "c", "d"},
"-",
"c",
`["a","b","d"]`,
},
{
"multi select with '-' modifier (nonempty base and matching value in a slice)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
[]string{"a", "b", "c", "d"},
"-",
[]string{"b", "a", "123"},
`["c","d"]`,
},
{
"multi select with unknown modifier (nonempty)",
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
[]string{"a", "b"},
"?",
"a",
`["a","b"]`,
},
// single relation
{
"single relation with '+' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"",
"+",
"b",
`"b"`,
},
{
"single relation with '+' modifier (nonempty base)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"a",
"+",
"b",
`"b"`,
},
{
"single relation with '-' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"",
"-",
"a",
`""`,
},
{
"single relation with '-' modifier (nonempty base and empty modifier value)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"a",
"-",
"",
`"a"`,
},
{
"single relation with '-' modifier (nonempty base and different value)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"a",
"-",
"b",
`"a"`,
},
{
"single relation with '-' modifier (nonempty base and matching value)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"a",
"-",
"a",
`""`,
},
{
"single relation with '-' modifier (nonempty base and matching value in a slice)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"a",
"-",
[]string{"b", "a", "c", "123"},
`""`,
},
{
"single relation with unknown modifier (nonempty)",
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
"",
"?",
"a",
`""`,
},
// multi relation
{
"multi relation with '+' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeRelation},
nil,
"+",
"b",
`["b"]`,
},
{
"multi relation with '+' modifier (nonempty base)",
schema.SchemaField{Type: schema.FieldTypeRelation},
[]string{"a"},
"+",
[]string{"b", "c"},
`["a","b","c"]`,
},
{
"multi relation with '+' modifier (nonempty base; already existing value)",
schema.SchemaField{Type: schema.FieldTypeRelation},
[]string{"a", "b"},
"+",
"b",
`["a","b"]`,
},
{
"multi relation with '-' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeRelation},
nil,
"-",
[]string{"a"},
`[]`,
},
{
"multi relation with '-' modifier (nonempty base and empty modifier value)",
schema.SchemaField{Type: schema.FieldTypeRelation},
"a",
"-",
"",
`["a"]`,
},
{
"multi relation with '-' modifier (nonempty base and different value)",
schema.SchemaField{Type: schema.FieldTypeRelation},
"a",
"-",
"b",
`["a"]`,
},
{
"multi relation with '-' modifier (nonempty base and matching value)",
schema.SchemaField{Type: schema.FieldTypeRelation},
[]string{"a", "b", "c", "d"},
"-",
"c",
`["a","b","d"]`,
},
{
"multi relation with '-' modifier (nonempty base and matching value in a slice)",
schema.SchemaField{Type: schema.FieldTypeRelation},
[]string{"a", "b", "c", "d"},
"-",
[]string{"b", "a", "123"},
`["c","d"]`,
},
{
"multi relation with unknown modifier (nonempty)",
schema.SchemaField{Type: schema.FieldTypeRelation},
[]string{"a", "b"},
"?",
"a",
`["a","b"]`,
},
// single file
{
"single file with '+' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"",
"+",
"b",
`""`,
},
{
"single file with '+' modifier (nonempty base)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"a",
"+",
"b",
`"a"`,
},
{
"single file with '-' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"",
"-",
"a",
`""`,
},
{
"single file with '-' modifier (nonempty base and empty modifier value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"a",
"-",
"",
`"a"`,
},
{
"single file with '-' modifier (nonempty base and different value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"a",
"-",
"b",
`"a"`,
},
{
"single file with '-' modifier (nonempty base and matching value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"a",
"-",
"a",
`""`,
},
{
"single file with '-' modifier (nonempty base and matching value in a slice)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"a",
"-",
[]string{"b", "a", "c", "123"},
`""`,
},
{
"single file with unknown modifier (nonempty)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
"",
"?",
"a",
`""`,
},
// multi file
{
"multi file with '+' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
nil,
"+",
"b",
`[]`,
},
{
"multi file with '+' modifier (nonempty base)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
[]string{"a"},
"+",
[]string{"b", "c"},
`["a"]`,
},
{
"multi file with '+' modifier (nonempty base; already existing value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
[]string{"a", "b"},
"+",
"b",
`["a","b"]`,
},
{
"multi file with '-' modifier (empty base)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
nil,
"-",
[]string{"a"},
`[]`,
},
{
"multi file with '-' modifier (nonempty base and empty modifier value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
"a",
"-",
"",
`["a"]`,
},
{
"multi file with '-' modifier (nonempty base and different value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
"a",
"-",
"b",
`["a"]`,
},
{
"multi file with '-' modifier (nonempty base and matching value)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
[]string{"a", "b", "c", "d"},
"-",
"c",
`["a","b","d"]`,
},
{
"multi file with '-' modifier (nonempty base and matching value in a slice)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
[]string{"a", "b", "c", "d"},
"-",
[]string{"b", "a", "123"},
`["c","d"]`,
},
{
"multi file with unknown modifier (nonempty)",
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
[]string{"a", "b"},
"?",
"a",
`["a","b"]`,
},
}
for _, s := range scenarios {
result := s.field.PrepareValueWithModifier(s.baseValue, s.modifier, s.modifierValue)
encoded, err := json.Marshal(result)
if err != nil {
t.Fatalf("[%s] %v", s.name, err)
}
if string(encoded) != s.expectJson {
t.Fatalf("[%s], Expected %v, got %v", s.name, s.expectJson, string(encoded))
}
}
}
// -------------------------------------------------------------------
type fieldOptionsScenario struct {
@@ -1306,7 +1996,7 @@ func TestRelationOptionsValidate(t *testing.T) {
[]string{"maxSelect"},
},
{
"MaxSelect > 0 && non-empty CollectionId",
"MaxSelect > 0 && nonempty CollectionId",
schema.RelationOptions{
CollectionId: "abc",
MaxSelect: types.Pointer(1),
+14
View File
@@ -44,6 +44,8 @@ type Settings struct {
SpotifyAuth AuthProviderConfig `form:"spotifyAuth" json:"spotifyAuth"`
KakaoAuth AuthProviderConfig `form:"kakaoAuth" json:"kakaoAuth"`
TwitchAuth AuthProviderConfig `form:"twitchAuth" json:"twitchAuth"`
StravaAuth AuthProviderConfig `form:"stravaAuth" json:"stravaAuth"`
GiteeAuth AuthProviderConfig `form:"giteeAuth" json:"giteeAuth"`
}
// New creates and returns a new default Settings instance.
@@ -124,6 +126,12 @@ func New() *Settings {
TwitchAuth: AuthProviderConfig{
Enabled: false,
},
StravaAuth: AuthProviderConfig{
Enabled: false,
},
GiteeAuth: AuthProviderConfig{
Enabled: false,
},
}
}
@@ -153,6 +161,8 @@ func (s *Settings) Validate() error {
validation.Field(&s.SpotifyAuth),
validation.Field(&s.KakaoAuth),
validation.Field(&s.TwitchAuth),
validation.Field(&s.StravaAuth),
validation.Field(&s.GiteeAuth),
)
}
@@ -207,6 +217,8 @@ func (s *Settings) RedactClone() (*Settings, error) {
&clone.SpotifyAuth.ClientSecret,
&clone.KakaoAuth.ClientSecret,
&clone.TwitchAuth.ClientSecret,
&clone.StravaAuth.ClientSecret,
&clone.GiteeAuth.ClientSecret,
}
// mask all sensitive fields
@@ -236,6 +248,8 @@ func (s *Settings) NamedAuthProviderConfigs() map[string]AuthProviderConfig {
auth.NameSpotify: s.SpotifyAuth,
auth.NameKakao: s.KakaoAuth,
auth.NameTwitch: s.TwitchAuth,
auth.NameStrava: s.StravaAuth,
auth.NameGitee: s.GiteeAuth,
}
}
+17 -1
View File
@@ -48,6 +48,10 @@ func TestSettingsValidate(t *testing.T) {
s.KakaoAuth.ClientId = ""
s.TwitchAuth.Enabled = true
s.TwitchAuth.ClientId = ""
s.StravaAuth.Enabled = true
s.StravaAuth.ClientId = ""
s.GiteeAuth.Enabled = true
s.GiteeAuth.ClientId = ""
// check if Validate() is triggering the members validate methods.
err := s.Validate()
@@ -76,6 +80,8 @@ func TestSettingsValidate(t *testing.T) {
`"spotifyAuth":{`,
`"kakaoAuth":{`,
`"twitchAuth":{`,
`"stravaAuth":{`,
`"giteeAuth":{`,
}
errBytes, _ := json.Marshal(err)
@@ -124,6 +130,10 @@ func TestSettingsMerge(t *testing.T) {
s2.KakaoAuth.ClientId = "kakao_test"
s2.TwitchAuth.Enabled = true
s2.TwitchAuth.ClientId = "twitch_test"
s2.StravaAuth.Enabled = true
s2.StravaAuth.ClientId = "strava_test"
s2.GiteeAuth.Enabled = true
s2.GiteeAuth.ClientId = "gitee_test"
if err := s1.Merge(s2); err != nil {
t.Fatal(err)
@@ -195,6 +205,8 @@ func TestSettingsRedactClone(t *testing.T) {
s1.SpotifyAuth.ClientSecret = "test123"
s1.KakaoAuth.ClientSecret = "test123"
s1.TwitchAuth.ClientSecret = "test123"
s1.StravaAuth.ClientSecret = "test123"
s1.GiteeAuth.ClientSecret = "test123"
s2, err := s1.RedactClone()
if err != nil {
@@ -206,7 +218,7 @@ func TestSettingsRedactClone(t *testing.T) {
t.Fatal(err)
}
expected := `{"meta":{"appName":"test123","appUrl":"http://localhost:8090","hideControls":false,"senderName":"Support","senderAddress":"support@example.com","verificationTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eThank you for joining us at {APP_NAME}.\u003c/p\u003e\n\u003cp\u003eClick on the button below to verify your email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eVerify\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Verify your {APP_NAME} email","actionUrl":"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}"},"resetPasswordTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to reset your password.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eReset password\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to reset your password, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Reset your {APP_NAME} password","actionUrl":"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}"},"confirmEmailChangeTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to confirm your new email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eConfirm new email\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to change your email address, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Confirm your {APP_NAME} new email address","actionUrl":"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"}},"logs":{"maxDays":5},"smtp":{"enabled":false,"host":"smtp.example.com","port":587,"username":"","password":"******","authMethod":"","tls":true},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","secret":"******","forcePathStyle":false},"adminAuthToken":{"secret":"******","duration":1209600},"adminPasswordResetToken":{"secret":"******","duration":1800},"recordAuthToken":{"secret":"******","duration":1209600},"recordPasswordResetToken":{"secret":"******","duration":1800},"recordEmailChangeToken":{"secret":"******","duration":1800},"recordVerificationToken":{"secret":"******","duration":604800},"emailAuth":{"enabled":false,"exceptDomains":null,"onlyDomains":null,"minPasswordLength":0},"googleAuth":{"enabled":false,"clientSecret":"******"},"facebookAuth":{"enabled":false,"clientSecret":"******"},"githubAuth":{"enabled":false,"clientSecret":"******"},"gitlabAuth":{"enabled":false,"clientSecret":"******"},"discordAuth":{"enabled":false,"clientSecret":"******"},"twitterAuth":{"enabled":false,"clientSecret":"******"},"microsoftAuth":{"enabled":false,"clientSecret":"******"},"spotifyAuth":{"enabled":false,"clientSecret":"******"},"kakaoAuth":{"enabled":false,"clientSecret":"******"},"twitchAuth":{"enabled":false,"clientSecret":"******"}}`
expected := `{"meta":{"appName":"test123","appUrl":"http://localhost:8090","hideControls":false,"senderName":"Support","senderAddress":"support@example.com","verificationTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eThank you for joining us at {APP_NAME}.\u003c/p\u003e\n\u003cp\u003eClick on the button below to verify your email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eVerify\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Verify your {APP_NAME} email","actionUrl":"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}"},"resetPasswordTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to reset your password.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eReset password\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to reset your password, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Reset your {APP_NAME} password","actionUrl":"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}"},"confirmEmailChangeTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to confirm your new email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eConfirm new email\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to change your email address, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Confirm your {APP_NAME} new email address","actionUrl":"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"}},"logs":{"maxDays":5},"smtp":{"enabled":false,"host":"smtp.example.com","port":587,"username":"","password":"******","authMethod":"","tls":true},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","secret":"******","forcePathStyle":false},"adminAuthToken":{"secret":"******","duration":1209600},"adminPasswordResetToken":{"secret":"******","duration":1800},"recordAuthToken":{"secret":"******","duration":1209600},"recordPasswordResetToken":{"secret":"******","duration":1800},"recordEmailChangeToken":{"secret":"******","duration":1800},"recordVerificationToken":{"secret":"******","duration":604800},"emailAuth":{"enabled":false,"exceptDomains":null,"onlyDomains":null,"minPasswordLength":0},"googleAuth":{"enabled":false,"clientSecret":"******"},"facebookAuth":{"enabled":false,"clientSecret":"******"},"githubAuth":{"enabled":false,"clientSecret":"******"},"gitlabAuth":{"enabled":false,"clientSecret":"******"},"discordAuth":{"enabled":false,"clientSecret":"******"},"twitterAuth":{"enabled":false,"clientSecret":"******"},"microsoftAuth":{"enabled":false,"clientSecret":"******"},"spotifyAuth":{"enabled":false,"clientSecret":"******"},"kakaoAuth":{"enabled":false,"clientSecret":"******"},"twitchAuth":{"enabled":false,"clientSecret":"******"},"stravaAuth":{"enabled":false,"clientSecret":"******"},"giteeAuth":{"enabled":false,"clientSecret":"******"}}`
if encodedStr := string(encoded); encodedStr != expected {
t.Fatalf("Expected\n%v\ngot\n%v", expected, encodedStr)
@@ -227,6 +239,8 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
s.SpotifyAuth.ClientId = "spotify_test"
s.KakaoAuth.ClientId = "kakao_test"
s.TwitchAuth.ClientId = "twitch_test"
s.StravaAuth.ClientId = "strava_test"
s.GiteeAuth.ClientId = "gitee_test"
result := s.NamedAuthProviderConfigs()
@@ -247,6 +261,8 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
`"twitter":{"enabled":false,"clientId":"twitter_test"}`,
`"kakao":{"enabled":false,"clientId":"kakao_test"}`,
`"twitch":{"enabled":false,"clientId":"twitch_test"}`,
`"strava":{"enabled":false,"clientId":"strava_test"}`,
`"gitee":{"enabled":false,"clientId":"gitee_test"}`,
}
for _, p := range expectedParts {
if !strings.Contains(encodedStr, p) {
+5 -3
View File
@@ -1,8 +1,10 @@
package jsvm
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/dop251/goja_nodejs/console"
"github.com/dop251/goja_nodejs/require"
@@ -75,7 +77,7 @@ func RegisterMigrations(app core.App, options *MigrationsOptions) error {
_, err := vm.RunString(string(content))
if err != nil {
return err
return fmt.Errorf("failed to run migration %s: %w", file, err)
}
}
@@ -97,8 +99,8 @@ func readDirFiles(dirPath string) (map[string][]byte, error) {
result := map[string][]byte{}
for _, f := range files {
if f.IsDir() {
continue
if f.IsDir() || !strings.HasSuffix(f.Name(), ".js") {
continue // not a .js file
}
raw, err := os.ReadFile(filepath.Join(dirPath, f.Name()))
if err != nil {
+70
View File
@@ -0,0 +1,70 @@
package resolvers
import (
"fmt"
"strings"
"github.com/pocketbase/dbx"
)
var _ dbx.Expression = (*multiMatchSubquery)(nil)
// join defines the specification for a single SQL JOIN clause.
type join struct {
tableName string
tableAlias string
on dbx.Expression
}
// multiMatchSubquery defines a record multi-match subquery expression.
type multiMatchSubquery struct {
baseTableAlias string
fromTableName string
fromTableAlias string
valueIdentifier string
joins []*join
params dbx.Params
}
// Build converts the expression into a SQL fragment.
//
// Implements [dbx.Expression] interface.
func (m *multiMatchSubquery) Build(db *dbx.DB, params dbx.Params) string {
if m.baseTableAlias == "" || m.fromTableName == "" || m.fromTableAlias == "" {
return "0=1"
}
if params == nil {
params = m.params
} else {
// merge by updating the parent params
for k, v := range m.params {
params[k] = v
}
}
var mergedJoins strings.Builder
for i, j := range m.joins {
if i > 0 {
mergedJoins.WriteString(" ")
}
mergedJoins.WriteString("LEFT JOIN ")
mergedJoins.WriteString(db.QuoteTableName(j.tableName))
mergedJoins.WriteString(" ")
mergedJoins.WriteString(db.QuoteTableName(j.tableAlias))
if j.on != nil {
mergedJoins.WriteString(" ON ")
mergedJoins.WriteString(j.on.Build(db, params))
}
}
return fmt.Sprintf(
`SELECT %s as [[multiMatchValue]] FROM %s %s %s WHERE %s = %s`,
db.QuoteColumnName(m.valueIdentifier),
db.QuoteTableName(m.fromTableName),
db.QuoteTableName(m.fromTableAlias),
mergedJoins.String(),
db.QuoteColumnName(m.fromTableAlias+".id"),
db.QuoteColumnName(m.baseTableAlias+".id"),
)
}
+589
View File
@@ -0,0 +1,589 @@
package resolvers
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/models/schema"
"github.com/pocketbase/pocketbase/tools/inflector"
"github.com/pocketbase/pocketbase/tools/list"
"github.com/pocketbase/pocketbase/tools/search"
"github.com/pocketbase/pocketbase/tools/security"
)
// parseAndRun starts a new one-off RecordFieldResolver.Resolve execution.
func parseAndRun(fieldName string, resolver *RecordFieldResolver) (*search.ResolverResult, error) {
r := &runner{
fieldName: fieldName,
resolver: resolver,
}
return r.run()
}
type runner struct {
used bool // indicates whether the runner was already executed
resolver *RecordFieldResolver // resolver is the shared expression fields resolver
fieldName string // the name of the single field expression the runner is responsible for
// shared processing state
// ---------------------------------------------------------------
activeProps []string // holds the active props that remains to be processed
activeCollectionName string // the last used collection name
activeTableAlias string // the last used table alias
allowHiddenFields bool // indicates whether hidden fields (eg. email) should be allowed without extra conditions
nullifyMisingField bool // indicating whether to return null on missing field or return an error
withMultiMatch bool // indicates whether to attach a multiMatchSubquery condition to the ResolverResult
multiMatchActiveTableAlias string // the last used multi-match table alias
multiMatch *multiMatchSubquery // the multi-match subquery expression generated from the fieldName
}
func (r *runner) run() (*search.ResolverResult, error) {
if r.used {
return nil, fmt.Errorf("the runner was already used")
}
if len(r.resolver.allowedFields) > 0 && !list.ExistInSliceWithRegex(r.fieldName, r.resolver.allowedFields) {
return nil, fmt.Errorf("failed to resolve field %q", r.fieldName)
}
defer func() {
r.used = true
}()
r.prepare()
// check for @collection field (aka. non-relational join)
// must be in the format "@collection.COLLECTION_NAME.FIELD[.FIELD2....]"
if r.activeProps[0] == "@collection" {
return r.processCollectionField()
}
if r.activeProps[0] == "@request" {
if r.resolver.requestData == nil {
return &search.ResolverResult{Identifier: "NULL"}, nil
}
if strings.HasPrefix(r.fieldName, "@request.auth.") {
return r.processRequestAuthField()
}
if strings.HasPrefix(r.fieldName, "@request.data.") && len(r.activeProps) > 2 {
name, modifier, err := splitModifier(r.activeProps[2])
if err != nil {
return nil, err
}
dataField := r.resolver.baseCollection.Schema.GetFieldByName(name)
if dataField == nil {
return r.resolver.resolveStaticRequestField(r.activeProps[1:]...)
}
dataField.InitOptions()
// check for data relation field
if dataField.Type == schema.FieldTypeRelation && len(r.activeProps) > 3 {
return r.processRequestDataRelationField(dataField)
}
// check for select:each field
if modifier == eachModifier && dataField.Type == schema.FieldTypeSelect && len(r.activeProps) == 3 {
return r.processRequestDataSelectEachModifier(dataField)
}
// check for data arrayble fields ":length" modifier
if modifier == lengthModifier && list.ExistInSlice(dataField.Type, schema.ArraybleFieldTypes()) && len(r.activeProps) == 3 {
return r.processRequestDataLengthModifier(dataField)
}
}
// some other @request.* static field
return r.resolver.resolveStaticRequestField(r.activeProps[1:]...)
}
// regular field
return r.processActiveProps()
}
func (r *runner) prepare() {
r.activeProps = strings.Split(r.fieldName, ".")
r.activeCollectionName = r.resolver.baseCollection.Name
r.activeTableAlias = inflector.Columnify(r.activeCollectionName)
r.allowHiddenFields = r.resolver.allowHiddenFields
// always allow hidden fields since the @.* filter is a system one
if r.activeProps[0] == "@collection" || r.activeProps[0] == "@request" {
r.allowHiddenFields = true
}
// enable the ignore flag for missing @request.* fields for backward
// compatibility and consistency with all @request.* filter fields and types
r.nullifyMisingField = r.activeProps[0] == "@request"
// prepare a multi-match subquery
r.multiMatch = &multiMatchSubquery{
baseTableAlias: r.activeTableAlias,
params: dbx.Params{},
}
r.multiMatch.fromTableName = inflector.Columnify(r.activeCollectionName)
r.multiMatch.fromTableAlias = "__mm_" + r.activeTableAlias
r.multiMatchActiveTableAlias = r.multiMatch.fromTableAlias
r.withMultiMatch = false
}
func (r *runner) processCollectionField() (*search.ResolverResult, error) {
if len(r.activeProps) < 3 {
return nil, fmt.Errorf("invalid @collection field path in %q", r.fieldName)
}
collection, err := r.resolver.loadCollection(r.activeProps[1])
if err != nil {
return nil, fmt.Errorf("failed to load collection %q from field path %q", r.activeProps[1], r.fieldName)
}
r.activeCollectionName = collection.Name
r.activeTableAlias = inflector.Columnify("__collection_" + r.activeCollectionName)
r.withMultiMatch = true
// join the collection to the main query
r.resolver.registerJoin(inflector.Columnify(collection.Name), r.activeTableAlias, nil)
// join the collection to the multi-match subquery
r.multiMatchActiveTableAlias = "__mm" + r.activeTableAlias
r.multiMatch.joins = append(r.multiMatch.joins, &join{
tableName: inflector.Columnify(collection.Name),
tableAlias: r.multiMatchActiveTableAlias,
})
// leave only the collection fields
// aka. @collection.someCollection.fieldA.fieldB -> fieldA.fieldB
r.activeProps = r.activeProps[2:]
return r.processActiveProps()
}
func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
// plain auth field
// ---
if list.ExistInSlice(r.fieldName, plainRequestAuthFields) {
return r.resolver.resolveStaticRequestField(r.activeProps[1:]...)
}
// resolve the auth collection field
// ---
if r.resolver.requestData == nil || r.resolver.requestData.AuthRecord == nil || r.resolver.requestData.AuthRecord.Collection() == nil {
return &search.ResolverResult{Identifier: "NULL"}, nil
}
collection := r.resolver.requestData.AuthRecord.Collection()
r.resolver.loadedCollections = append(r.resolver.loadedCollections, collection)
r.activeCollectionName = collection.Name
r.activeTableAlias = "__auth_" + inflector.Columnify(r.activeCollectionName)
// join the auth collection to the main query
r.resolver.registerJoin(
inflector.Columnify(r.activeCollectionName),
r.activeTableAlias,
dbx.HashExp{
// aka. __auth_users.id = :userId
(r.activeTableAlias + ".id"): r.resolver.requestData.AuthRecord.Id,
},
)
// join the auth collection to the multi-match subquery
r.multiMatchActiveTableAlias = "__mm_" + r.activeTableAlias
r.multiMatch.joins = append(
r.multiMatch.joins,
&join{
tableName: inflector.Columnify(r.activeCollectionName),
tableAlias: r.multiMatchActiveTableAlias,
on: dbx.HashExp{
(r.multiMatchActiveTableAlias + ".id"): r.resolver.requestData.AuthRecord.Id,
},
},
)
// leave only the auth relation fields
// aka. @request.auth.fieldA.fieldB -> fieldA.fieldB
r.activeProps = r.activeProps[2:]
return r.processActiveProps()
}
func (r *runner) processRequestDataLengthModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
result := &search.ResolverResult{
Identifier: fmt.Sprintf("%d", len(dataItems)),
}
return result, nil
}
func (r *runner) processRequestDataSelectEachModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
options, ok := dataField.Options.(*schema.SelectOptions)
if !ok {
return nil, fmt.Errorf("failed to initialize field %q options", dataField.Name)
}
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
rawJson, err := json.Marshal(dataItems)
if err != nil {
return nil, fmt.Errorf("cannot marshalize the data select item for field %q", r.activeProps[2])
}
placeholder := "dataSelect" + security.PseudorandomString(4)
cleanFieldName := inflector.Columnify(dataField.Name)
jeTable := fmt.Sprintf("json_each({:%s})", placeholder)
jeAlias := "__dataSelect_" + cleanFieldName + "_je"
r.resolver.registerJoin(jeTable, jeAlias, nil)
result := &search.ResolverResult{
Identifier: fmt.Sprintf("[[%s.value]]", jeAlias),
Params: dbx.Params{placeholder: rawJson},
}
if options.MaxSelect != 1 {
r.withMultiMatch = true
}
if r.withMultiMatch {
placeholder2 := "mm" + placeholder
jeTable2 := fmt.Sprintf("json_each({:%s})", placeholder2)
jeAlias2 := "__mm" + jeAlias
r.multiMatch.joins = append(r.multiMatch.joins, &join{
tableName: jeTable2,
tableAlias: jeAlias2,
})
r.multiMatch.params[placeholder2] = rawJson
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.value]]", jeAlias2)
result.MultiMatchSubQuery = r.multiMatch
}
return result, nil
}
func (r *runner) processRequestDataRelationField(dataField *schema.SchemaField) (*search.ResolverResult, error) {
options, ok := dataField.Options.(*schema.RelationOptions)
if !ok {
return nil, fmt.Errorf("failed to initialize data field %q options", dataField.Name)
}
dataRelCollection, err := r.resolver.loadCollection(options.CollectionId)
if err != nil {
return nil, fmt.Errorf("failed to load collection %q from data field %q", options.CollectionId, dataField.Name)
}
var dataRelIds []string
if r.resolver.requestData != nil && len(r.resolver.requestData.Data) != 0 {
dataRelIds = list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
}
if len(dataRelIds) == 0 {
return &search.ResolverResult{Identifier: "NULL"}, nil
}
r.activeCollectionName = dataRelCollection.Name
r.activeTableAlias = inflector.Columnify("__data_" + dataRelCollection.Name)
// join the data rel collection to the main collection
r.resolver.registerJoin(
inflector.Columnify(r.activeCollectionName),
r.activeTableAlias,
dbx.In(
fmt.Sprintf("[[%s.id]]", inflector.Columnify(r.activeTableAlias)),
list.ToInterfaceSlice(dataRelIds)...,
),
)
if options.MaxSelect == nil || *options.MaxSelect != 1 {
r.withMultiMatch = true
}
// join the data rel collection to the multi-match subquery
r.multiMatchActiveTableAlias = inflector.Columnify("__data_mm_" + dataRelCollection.Name)
r.multiMatch.joins = append(
r.multiMatch.joins,
&join{
tableName: inflector.Columnify(r.activeCollectionName),
tableAlias: r.multiMatchActiveTableAlias,
on: dbx.In(r.multiMatchActiveTableAlias+".id", list.ToInterfaceSlice(dataRelIds)...),
},
)
// leave only the data relation fields
// aka. @request.data.someRel.fieldA.fieldB -> fieldA.fieldB
r.activeProps = r.activeProps[3:]
return r.processActiveProps()
}
func (r *runner) processActiveProps() (*search.ResolverResult, error) {
totalProps := len(r.activeProps)
for i, prop := range r.activeProps {
collection, err := r.resolver.loadCollection(r.activeCollectionName)
if err != nil {
return nil, fmt.Errorf("failed to resolve field %q", prop)
}
// last prop
if i == totalProps-1 {
// system field, aka. internal model prop
// (always available but not part of the collection schema)
// -------------------------------------------------------
if list.ExistInSlice(prop, resolvableSystemFieldNames(collection)) {
result := &search.ResolverResult{
Identifier: fmt.Sprintf("[[%s.%s]]", r.activeTableAlias, inflector.Columnify(prop)),
}
// allow querying only auth records with emails marked as public
if prop == schema.FieldNameEmail && !r.allowHiddenFields {
result.AfterBuild = func(expr dbx.Expression) dbx.Expression {
return dbx.And(expr, dbx.NewExp(fmt.Sprintf(
"[[%s.%s]] = TRUE",
r.activeTableAlias,
schema.FieldNameEmailVisibility,
)))
}
}
if r.withMultiMatch {
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.%s]]", r.multiMatchActiveTableAlias, inflector.Columnify(prop))
result.MultiMatchSubQuery = r.multiMatch
}
return result, nil
}
name, modifier, err := splitModifier(prop)
if err != nil {
return nil, err
}
field := collection.Schema.GetFieldByName(name)
if field == nil {
if r.nullifyMisingField {
return &search.ResolverResult{Identifier: "NULL"}, nil
}
return nil, fmt.Errorf("unknown field %q", name)
}
cleanFieldName := inflector.Columnify(field.Name)
// arrayble fields ":length" modifier
// -------------------------------------------------------
if modifier == lengthModifier && list.ExistInSlice(field.Type, schema.ArraybleFieldTypes()) {
jePair := r.activeTableAlias + "." + cleanFieldName
result := &search.ResolverResult{
Identifier: jsonArrayLength(jePair),
}
if r.withMultiMatch {
jePair2 := r.multiMatchActiveTableAlias + "." + cleanFieldName
r.multiMatch.valueIdentifier = jsonArrayLength(jePair2)
result.MultiMatchSubQuery = r.multiMatch
}
return result, nil
}
// select field with ":each" modifier
// -------------------------------------------------------
if field.Type == schema.FieldTypeSelect && modifier == eachModifier {
jePair := r.activeTableAlias + "." + cleanFieldName
jeAlias := r.activeTableAlias + "_" + cleanFieldName + "_je"
r.resolver.registerJoin(jsonEach(jePair), jeAlias, nil)
result := &search.ResolverResult{
Identifier: fmt.Sprintf("[[%s.value]]", jeAlias),
}
field.InitOptions()
options, ok := field.Options.(*schema.SelectOptions)
if !ok {
return nil, fmt.Errorf("failed to initialize field %q options", prop)
}
if options.MaxSelect != 1 {
r.withMultiMatch = true
}
if r.withMultiMatch {
jePair2 := r.multiMatchActiveTableAlias + "." + cleanFieldName
jeAlias2 := r.multiMatchActiveTableAlias + "_" + cleanFieldName + "_je"
r.multiMatch.joins = append(r.multiMatch.joins, &join{
tableName: jsonEach(jePair2),
tableAlias: jeAlias2,
})
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.value]]", jeAlias2)
result.MultiMatchSubQuery = r.multiMatch
}
return result, nil
}
// default
// -------------------------------------------------------
result := &search.ResolverResult{
Identifier: fmt.Sprintf("[[%s.%s]]", r.activeTableAlias, cleanFieldName),
}
if r.withMultiMatch {
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.%s]]", r.multiMatchActiveTableAlias, cleanFieldName)
result.MultiMatchSubQuery = r.multiMatch
}
return result, nil
}
field := collection.Schema.GetFieldByName(prop)
if field == nil {
if r.nullifyMisingField {
return &search.ResolverResult{Identifier: "NULL"}, nil
}
return nil, fmt.Errorf("unknown field %q", prop)
}
// check if it is a json field
if field.Type == schema.FieldTypeJson {
var jsonPath strings.Builder
jsonPath.WriteString("$")
for _, p := range r.activeProps[i+1:] {
if _, err := strconv.Atoi(p); err == nil {
jsonPath.WriteString("[")
jsonPath.WriteString(inflector.Columnify(p))
jsonPath.WriteString("]")
} else {
jsonPath.WriteString(".")
jsonPath.WriteString(inflector.Columnify(p))
}
}
result := &search.ResolverResult{
Identifier: fmt.Sprintf(
"JSON_EXTRACT([[%s.%s]], '%s')",
r.activeTableAlias,
inflector.Columnify(prop),
jsonPath.String(),
),
}
if r.withMultiMatch {
r.multiMatch.valueIdentifier = fmt.Sprintf(
"JSON_EXTRACT([[%s.%s]], '%s')",
r.multiMatchActiveTableAlias,
inflector.Columnify(prop),
jsonPath.String(),
)
result.MultiMatchSubQuery = r.multiMatch
}
return result, nil
}
// check if it is a relation field
if field.Type != schema.FieldTypeRelation {
return nil, fmt.Errorf("field %q is not a valid relation", prop)
}
// join the relation to the main query
// ---
field.InitOptions()
options, ok := field.Options.(*schema.RelationOptions)
if !ok {
return nil, fmt.Errorf("failed to initialize field %q options", prop)
}
relCollection, relErr := r.resolver.loadCollection(options.CollectionId)
if relErr != nil {
return nil, fmt.Errorf("failed to find field %q collection", prop)
}
cleanFieldName := inflector.Columnify(field.Name)
newCollectionName := relCollection.Name
newTableAlias := r.activeTableAlias + "_" + cleanFieldName
jeAlias := r.activeTableAlias + "_" + cleanFieldName + "_je"
jePair := r.activeTableAlias + "." + cleanFieldName
r.resolver.registerJoin(jsonEach(jePair), jeAlias, nil)
r.resolver.registerJoin(
inflector.Columnify(newCollectionName),
newTableAlias,
dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias, jeAlias)),
)
r.activeCollectionName = newCollectionName
r.activeTableAlias = newTableAlias
// ---
// join the relation to the multi-match subquery
// ---
if options.MaxSelect == nil || *options.MaxSelect != 1 {
r.withMultiMatch = true
}
newTableAlias2 := r.multiMatchActiveTableAlias + "_" + cleanFieldName
jeAlias2 := r.multiMatchActiveTableAlias + "_" + cleanFieldName + "_je"
jePair2 := r.multiMatchActiveTableAlias + "." + cleanFieldName
r.multiMatchActiveTableAlias = newTableAlias2
r.multiMatch.joins = append(
r.multiMatch.joins,
&join{
tableName: jsonEach(jePair2),
tableAlias: jeAlias2,
},
&join{
tableName: inflector.Columnify(newCollectionName),
tableAlias: newTableAlias2,
on: dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias2, jeAlias2)),
},
)
// ---
}
return nil, fmt.Errorf("failed to resolve field %q", r.fieldName)
}
func jsonArrayLength(tableColumnPair string) string {
return fmt.Sprintf(
// note: the case is used to normalize value access for single and multiple relations.
`json_array_length(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
tableColumnPair, tableColumnPair, tableColumnPair,
)
}
func jsonEach(tableColumnPair string) string {
return fmt.Sprintf(
// note: the case is used to normalize value access for single and multiple relations.
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
tableColumnPair, tableColumnPair, tableColumnPair,
)
}
func resolvableSystemFieldNames(collection *models.Collection) []string {
result := schema.BaseModelFieldNames()
if collection.IsAuth() {
result = append(
result,
schema.FieldNameUsername,
schema.FieldNameVerified,
schema.FieldNameEmailVisibility,
schema.FieldNameEmail,
)
}
return result
}
+129 -284
View File
@@ -10,18 +10,20 @@ import (
"github.com/pocketbase/pocketbase/daos"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/models/schema"
"github.com/pocketbase/pocketbase/tools/inflector"
"github.com/pocketbase/pocketbase/tools/list"
"github.com/pocketbase/pocketbase/tools/search"
"github.com/pocketbase/pocketbase/tools/security"
"github.com/spf13/cast"
)
// ensure that `search.FieldResolver` interface is implemented
var _ search.FieldResolver = (*RecordFieldResolver)(nil)
// filter modifiers
const (
eachModifier string = "each"
issetModifier string = "isset"
lengthModifier string = "length"
)
// list of auth filter fields that don't require join with the auth
// collection or any other extra checks to be resolved
// collection or any other extra checks to be resolved.
var plainRequestAuthFields = []string{
"@request.auth." + schema.FieldNameId,
"@request.auth." + schema.FieldNameCollectionId,
@@ -34,32 +36,28 @@ var plainRequestAuthFields = []string{
"@request.auth." + schema.FieldNameUpdated,
}
type join struct {
id string
table string
on dbx.Expression
}
// ensure that `search.FieldResolver` interface is implemented
var _ search.FieldResolver = (*RecordFieldResolver)(nil)
// RecordFieldResolver defines a custom search resolver struct for
// managing Record model search fields.
//
// Usually used together with `search.Provider`. Example:
// resolver := resolvers.NewRecordFieldResolver(
// app.Dao(),
// myCollection,
// &models.RequestData{...},
// true,
// )
// provider := search.NewProvider(resolver)
// ...
// resolver := resolvers.NewRecordFieldResolver(
// app.Dao(),
// myCollection,
// &models.RequestData{...},
// true,
// )
// provider := search.NewProvider(resolver)
// ...
type RecordFieldResolver struct {
dao *daos.Dao
baseCollection *models.Collection
allowHiddenFields bool
allowedFields []string
loadedCollections []*models.Collection
joins []join // we cannot use a map because the insertion order is not preserved
exprs []dbx.Expression
joins []*join // we cannot use a map because the insertion order is not preserved
requestData *models.RequestData
staticRequestData map[string]any
}
@@ -76,20 +74,18 @@ func NewRecordFieldResolver(
baseCollection: baseCollection,
requestData: requestData,
allowHiddenFields: allowHiddenFields,
joins: []join{},
exprs: []dbx.Expression{},
joins: []*join{},
loadedCollections: []*models.Collection{baseCollection},
allowedFields: []string{
`^\w+[\w\.]*$`,
`^\w+[\w\.\:]*$`,
`^\@request\.method$`,
`^\@request\.auth\.\w+[\w\.]*$`,
`^\@request\.data\.\w+[\w\.]*$`,
`^\@request\.query\.\w+[\w\.]*$`,
`^\@collection\.\w+\.\w+[\w\.]*$`,
`^\@request\.auth\.[\w\.\:]*\w+$`,
`^\@request\.data\.[\w\.\:]*\w+$`,
`^\@request\.query\.[\w\.\:]*\w+$`,
`^\@collection\.\w+\.[\w\.\:]*\w+$`,
},
}
// @todo remove after IN operator and multi-match filter enhancements
r.staticRequestData = map[string]any{}
if r.requestData != nil {
r.staticRequestData["method"] = r.requestData.Method
@@ -115,13 +111,10 @@ func (r *RecordFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
query.Distinct(true)
for _, join := range r.joins {
query.LeftJoin(join.table, join.on)
}
}
for _, expr := range r.exprs {
if expr != nil {
query.AndWhere(expr)
query.LeftJoin(
(join.tableName + " " + join.tableAlias),
join.on,
)
}
}
@@ -130,225 +123,63 @@ func (r *RecordFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
// Resolve implements `search.FieldResolver` interface.
//
// Example of resolvable field formats:
// id
// project.screen.status
// @request.status
// @request.auth.someRelation.name
// @collection.product.name
func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, placeholderParams dbx.Params, err error) {
if len(r.allowedFields) > 0 && !list.ExistInSliceWithRegex(fieldName, r.allowedFields) {
return "", nil, fmt.Errorf("Failed to resolve field %q", fieldName)
}
props := strings.Split(fieldName, ".")
currentCollectionName := r.baseCollection.Name
currentTableAlias := inflector.Columnify(currentCollectionName)
// flag indicating whether to return null on missing field or return on an error
nullifyMisingField := false
allowHiddenFields := r.allowHiddenFields
// check for @collection field (aka. non-relational join)
// must be in the format "@collection.COLLECTION_NAME.FIELD[.FIELD2....]"
if props[0] == "@collection" {
if len(props) < 3 {
return "", nil, fmt.Errorf("Invalid @collection field path in %q.", fieldName)
}
currentCollectionName = props[1]
currentTableAlias = inflector.Columnify("__collection_" + currentCollectionName)
collection, err := r.loadCollection(currentCollectionName)
if err != nil {
return "", nil, fmt.Errorf("Failed to load collection %q from field path %q.", currentCollectionName, fieldName)
}
// always allow hidden fields since the @collection.* filter is a system one
allowHiddenFields = true
r.registerJoin(inflector.Columnify(collection.Name), currentTableAlias, nil)
props = props[2:] // leave only the collection fields
} else if props[0] == "@request" {
if len(props) == 1 {
return "", nil, fmt.Errorf("Invalid @request data field path in %q.", fieldName)
}
if r.requestData == nil {
return "NULL", nil, nil
}
// plain @request.* field
if !strings.HasPrefix(fieldName, "@request.auth.") || list.ExistInSlice(fieldName, plainRequestAuthFields) {
return r.resolveStaticRequestField(props[1:]...)
}
// always allow hidden fields since the @request.* filter is a system one
allowHiddenFields = true
// enable the ignore flag for missing @request.auth.* fields
// for consistency with @request.data.* and @request.query.*
nullifyMisingField = true
// resolve the auth collection fields
// ---
if r.requestData == nil || r.requestData.AuthRecord == nil || r.requestData.AuthRecord.Collection() == nil {
return "NULL", nil, nil
}
collection := r.requestData.AuthRecord.Collection()
r.loadedCollections = append(r.loadedCollections, collection)
currentCollectionName = collection.Name
currentTableAlias = "__auth_" + inflector.Columnify(currentCollectionName)
authIdParamKey := "auth" + security.PseudorandomString(5)
authIdParams := dbx.Params{authIdParamKey: r.requestData.AuthRecord.Id}
// ---
// join the auth collection
r.registerJoin(
inflector.Columnify(collection.Name),
currentTableAlias,
dbx.NewExp(fmt.Sprintf(
// aka. __auth_users.id = :userId
"[[%s.id]] = {:%s}",
inflector.Columnify(currentTableAlias),
authIdParamKey,
), authIdParams),
)
props = props[2:] // leave only the auth relation fields
}
totalProps := len(props)
for i, prop := range props {
collection, err := r.loadCollection(currentCollectionName)
if err != nil {
return "", nil, fmt.Errorf("Failed to resolve field %q.", prop)
}
systemFieldNames := schema.BaseModelFieldNames()
if collection.IsAuth() {
systemFieldNames = append(
systemFieldNames,
schema.FieldNameUsername,
schema.FieldNameVerified,
schema.FieldNameEmailVisibility,
schema.FieldNameEmail,
)
}
// internal model prop (always available but not part of the collection schema)
if list.ExistInSlice(prop, systemFieldNames) {
// allow querying only auth records with emails marked as public
if prop == schema.FieldNameEmail && !allowHiddenFields {
r.registerExpr(dbx.NewExp(fmt.Sprintf(
"[[%s.%s]] = TRUE",
currentTableAlias,
inflector.Columnify(schema.FieldNameEmailVisibility),
)))
}
return fmt.Sprintf("[[%s.%s]]", currentTableAlias, inflector.Columnify(prop)), nil, nil
}
field := collection.Schema.GetFieldByName(prop)
if field == nil {
if nullifyMisingField {
return "NULL", nil, nil
}
return "", nil, fmt.Errorf("Unrecognized field %q.", prop)
}
// last prop
if i == totalProps-1 {
return fmt.Sprintf("[[%s.%s]]", currentTableAlias, inflector.Columnify(prop)), nil, nil
}
// check if it is a json field
if field.Type == schema.FieldTypeJson {
var jsonPath strings.Builder
jsonPath.WriteString("$")
for _, p := range props[i+1:] {
if _, err := strconv.Atoi(p); err == nil {
jsonPath.WriteString("[")
jsonPath.WriteString(inflector.Columnify(p))
jsonPath.WriteString("]")
} else {
jsonPath.WriteString(".")
jsonPath.WriteString(inflector.Columnify(p))
}
}
return fmt.Sprintf(
"JSON_EXTRACT([[%s.%s]], '%s')",
currentTableAlias,
inflector.Columnify(prop),
jsonPath.String(),
), nil, nil
}
// check if it is a relation field
if field.Type != schema.FieldTypeRelation {
return "", nil, fmt.Errorf("Field %q is not a valid relation.", prop)
}
// auto join the relation
// ---
field.InitOptions()
options, ok := field.Options.(*schema.RelationOptions)
if !ok {
return "", nil, fmt.Errorf("Failed to initialize field %q options.", prop)
}
relCollection, relErr := r.loadCollection(options.CollectionId)
if relErr != nil {
return "", nil, fmt.Errorf("Failed to find field %q collection.", prop)
}
cleanFieldName := inflector.Columnify(field.Name)
newCollectionName := relCollection.Name
newTableAlias := currentTableAlias + "_" + cleanFieldName
jeTable := currentTableAlias + "_" + cleanFieldName + "_je"
jePair := currentTableAlias + "." + cleanFieldName
r.registerJoin(
fmt.Sprintf(
// note: the case is used to normalize value access for single and multiple relations.
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
jePair, jePair, jePair,
),
jeTable,
nil,
)
r.registerJoin(
inflector.Columnify(newCollectionName),
newTableAlias,
dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias, jeTable)),
)
currentCollectionName = newCollectionName
currentTableAlias = newTableAlias
}
return "", nil, fmt.Errorf("Failed to resolve field %q.", fieldName)
// Example of some resolvable fieldName formats:
//
// id
// someSelect.each
// project.screen.status
// @request.status
// @request.query.filter
// @request.auth.someRelation.name
// @request.data.someRelation.name
// @request.data.someField
// @request.data.someSelect:each
// @request.data.someField:isset
// @collection.product.name
func (r *RecordFieldResolver) Resolve(fieldName string) (*search.ResolverResult, error) {
return parseAndRun(fieldName, r)
}
func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (resultName string, placeholderParams dbx.Params, err error) {
// ignore error because requestData is dynamic and some of the
// lookup keys may not be defined for the request
resultVal, _ := extractNestedMapVal(r.staticRequestData, path...)
func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (*search.ResolverResult, error) {
if len(path) == 0 {
return nil, fmt.Errorf("at least one path key should be provided")
}
lastProp, modifier, err := splitModifier(path[len(path)-1])
if err != nil {
return nil, err
}
path[len(path)-1] = lastProp
// extract value
resultVal, err := extractNestedMapVal(r.staticRequestData, path...)
if modifier == issetModifier {
if err != nil {
return &search.ResolverResult{Identifier: "FALSE"}, nil
}
return &search.ResolverResult{Identifier: "TRUE"}, nil
}
// note: we are ignoring the error because requestData is dynamic
// and some of the lookup keys may not be defined for the request
switch v := resultVal.(type) {
case nil:
return "NULL", nil, nil
case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
return &search.ResolverResult{Identifier: "NULL"}, nil
case string:
// check if it is a number field and explicitly try to cast to
// float in case of a numeric string value was used
// (this usually the case when the data is from a multipart/form-data request)
field := r.baseCollection.Schema.GetFieldByName(path[len(path)-1])
if field != nil && field.Type == schema.FieldTypeNumber {
if nv, err := strconv.ParseFloat(v, 64); err == nil {
resultVal = nv
}
}
// otherwise - no further processing is needed...
case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
// no further processing is needed...
default:
// non-plain value
@@ -367,33 +198,11 @@ func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (resultN
}
placeholder := "f" + security.PseudorandomString(5)
name := fmt.Sprintf("{:%s}", placeholder)
params := dbx.Params{placeholder: resultVal}
return name, params, nil
}
func extractNestedMapVal(m map[string]any, keys ...string) (result any, err error) {
var ok bool
if len(keys) == 0 {
return nil, fmt.Errorf("At least one key should be provided.")
}
if result, ok = m[keys[0]]; !ok {
return nil, fmt.Errorf("Invalid key path - missing key %q.", keys[0])
}
// end key reached
if len(keys) == 1 {
return result, nil
}
if m, ok = result.(map[string]any); !ok {
return nil, fmt.Errorf("Expected map structure, got %#v.", result)
}
return extractNestedMapVal(m, keys[1:]...)
return &search.ResolverResult{
Identifier: "{:" + placeholder + "}",
Params: dbx.Params{placeholder: resultVal},
}, nil
}
func (r *RecordFieldResolver) loadCollection(collectionNameOrId string) (*models.Collection, error) {
@@ -415,17 +224,15 @@ func (r *RecordFieldResolver) loadCollection(collectionNameOrId string) (*models
}
func (r *RecordFieldResolver) registerJoin(tableName string, tableAlias string, on dbx.Expression) {
tableExpr := (tableName + " " + tableAlias)
join := join{
id: tableAlias,
table: tableExpr,
on: on,
join := &join{
tableName: tableName,
tableAlias: tableAlias,
on: on,
}
// replace existing join
for i, j := range r.joins {
if j.id == join.id {
if j.tableAlias == join.tableAlias {
r.joins[i] = join
return
}
@@ -435,6 +242,44 @@ func (r *RecordFieldResolver) registerJoin(tableName string, tableAlias string,
r.joins = append(r.joins, join)
}
func (r *RecordFieldResolver) registerExpr(expr dbx.Expression) {
r.exprs = append(r.exprs, expr)
func extractNestedMapVal(m map[string]any, keys ...string) (any, error) {
if len(keys) == 0 {
return nil, fmt.Errorf("at least one key should be provided")
}
var result any
var ok bool
if result, ok = m[keys[0]]; !ok {
return nil, fmt.Errorf("invalid key path - missing key %q", keys[0])
}
// end key reached
if len(keys) == 1 {
return result, nil
}
if m, ok = result.(map[string]any); !ok {
return nil, fmt.Errorf("expected map, got %#v", result)
}
return extractNestedMapVal(m, keys[1:]...)
}
func splitModifier(combined string) (string, string, error) {
parts := strings.Split(combined, ":")
if len(parts) != 2 {
return combined, "", nil
}
// validate modifier
switch parts[1] {
case issetModifier,
eachModifier,
lengthModifier:
return parts[0], parts[1], nil
}
return "", "", fmt.Errorf("unknown modifier in %q", combined)
}
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":{"original-filename":"test.txt"},"md5":"2Oj8otwPiW/Xy0ywAxuiSQ=="}
@@ -0,0 +1 @@
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":{"original-filename":"test.txt"},"md5":"2Oj8otwPiW/Xy0ywAxuiSQ=="}
+12 -7
View File
@@ -9,13 +9,14 @@ import (
// AuthUser defines a standardized oauth2 user data structure.
type AuthUser struct {
Id string `json:"id"`
Name string `json:"name"`
Username string `json:"username"`
Email string `json:"email"`
AvatarUrl string `json:"avatarUrl"`
RawUser map[string]any `json:"rawUser"`
AccessToken string `json:"accessToken"`
Id string `json:"id"`
Name string `json:"name"`
Username string `json:"username"`
Email string `json:"email"`
AvatarUrl string `json:"avatarUrl"`
RawUser map[string]any `json:"rawUser"`
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
}
// Provider defines a common interface for an OAuth2 client.
@@ -105,6 +106,10 @@ func NewProviderByName(name string) (Provider, error) {
return NewKakaoProvider(), nil
case NameTwitch:
return NewTwitchProvider(), nil
case NameStrava:
return NewStravaProvider(), nil
case NameGitee:
return NewGiteeProvider(), nil
default:
return nil, errors.New("Missing provider " + name)
}
+18
View File
@@ -108,4 +108,22 @@ func TestNewProviderByName(t *testing.T) {
if _, ok := p.(*auth.Twitch); !ok {
t.Error("Expected to be instance of *auth.Twitch")
}
// strava
p, err = auth.NewProviderByName(auth.NameStrava)
if err != nil {
t.Errorf("Expected nil, got error %v", err)
}
if _, ok := p.(*auth.Strava); !ok {
t.Error("Expected to be instance of *auth.Strava")
}
// gitee
p, err = auth.NewProviderByName(auth.NameGitee)
if err != nil {
t.Errorf("Expected nil, got error %v", err)
}
if _, ok := p.(*auth.Gitee); !ok {
t.Error("Expected to be instance of *auth.Gitee")
}
}
+7 -6
View File
@@ -63,12 +63,13 @@ func (p *Discord) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
username := fmt.Sprintf("%s#%s", extracted.Username, extracted.Discriminator)
user := &AuthUser{
Id: extracted.Id,
Name: username,
Username: extracted.Username,
AvatarUrl: avatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Id,
Name: username,
Username: extracted.Username,
AvatarUrl: avatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
if extracted.Verified {
user.Email = extracted.Email
+7 -6
View File
@@ -54,12 +54,13 @@ func (p *Facebook) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: extracted.Id,
Name: extracted.Name,
Email: extracted.Email,
AvatarUrl: extracted.Picture.Data.Url,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Id,
Name: extracted.Name,
Email: extracted.Email,
AvatarUrl: extracted.Picture.Data.Url,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
+131
View File
@@ -0,0 +1,131 @@
package auth
import (
"encoding/json"
"io"
"strconv"
"github.com/go-ozzo/ozzo-validation/v4/is"
"golang.org/x/oauth2"
)
var _ Provider = (*Gitee)(nil)
// NameGitee is the unique name of the Gitee provider.
const NameGitee string = "gitee"
// Gitee allows authentication via Gitee OAuth2.
type Gitee struct {
*baseProvider
}
// NewGiteeProvider creates new Gitee provider instance with some defaults.
func NewGiteeProvider() *Gitee {
return &Gitee{&baseProvider{
scopes: []string{"user_info", "emails"},
authUrl: "https://gitee.com/oauth/authorize",
tokenUrl: "https://gitee.com/oauth/token",
userApiUrl: "https://gitee.com/api/v5/user",
}}
}
// FetchAuthUser returns an AuthUser instance based the Gitee's user api.
//
// API reference: https://gitee.com/api/v5/swagger#/getV5User
func (p *Gitee) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
data, err := p.FetchRawUserData(token)
if err != nil {
return nil, err
}
rawUser := map[string]any{}
if err := json.Unmarshal(data, &rawUser); err != nil {
return nil, err
}
extracted := struct {
Login string `json:"login"`
Id int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
AvatarUrl string `json:"avatar_url"`
}{}
if err := json.Unmarshal(data, &extracted); err != nil {
return nil, err
}
user := &AuthUser{
Id: strconv.Itoa(extracted.Id),
Name: extracted.Name,
Username: extracted.Login,
AvatarUrl: extracted.AvatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
if extracted.Email != "" && is.EmailFormat.Validate(extracted.Email) == nil {
// valid public primary email
user.Email = extracted.Email
} else {
// send an additional optional request to retrieve the email
email, err := p.fetchPrimaryEmail(token)
if err != nil {
return nil, err
}
user.Email = email
}
return user, nil
}
// fetchPrimaryEmail sends an API request to retrieve the verified primary email,
// in case the user hasn't set "Public email address" or has unchecked
// the "Access your emails data" permission during authentication.
//
// NB! This method can succeed and still return an empty email.
// Error responses that are result of insufficient scopes permissions are ignored.
//
// API reference: https://gitee.com/api/v5/swagger#/getV5Emails
func (p *Gitee) fetchPrimaryEmail(token *oauth2.Token) (string, error) {
client := p.Client(token)
response, err := client.Get("https://gitee.com/api/v5/emails")
if err != nil {
return "", err
}
defer response.Body.Close()
// ignore common http errors caused by insufficient scope permissions
if response.StatusCode == 401 || response.StatusCode == 403 || response.StatusCode == 404 {
return "", nil
}
content, err := io.ReadAll(response.Body)
if err != nil {
return "", err
}
emails := []struct {
Email string
State string
Scope []string
}{}
if err := json.Unmarshal(content, &emails); err != nil {
// ignore unmarshal error in case "Keep my email address private"
// was set because response.Body will be something like:
// {"email":"12285415+test@user.noreply.gitee.com"}
return "", nil
}
// extract the first verified primary email
for _, email := range emails {
for _, scope := range email.Scope {
if email.State == "confirmed" && scope == "primary" && is.EmailFormat.Validate(email.Email) == nil {
return email.Email, nil
}
}
}
return "", nil
}
+59 -36
View File
@@ -55,48 +55,71 @@ func (p *Github) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: strconv.Itoa(extracted.Id),
Name: extracted.Name,
Username: extracted.Login,
Email: extracted.Email,
AvatarUrl: extracted.AvatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: strconv.Itoa(extracted.Id),
Name: extracted.Name,
Username: extracted.Login,
Email: extracted.Email,
AvatarUrl: extracted.AvatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
// in case user set "Keep my email address private",
// email should be retrieved via extra API request
// in case user has set "Keep my email address private", send an
// **optional** API request to retrieve the verified primary email
if user.Email == "" {
client := p.Client(token)
response, err := client.Get(p.userApiUrl + "/emails")
email, err := p.fetchPrimaryEmail(token)
if err != nil {
return user, err
}
defer response.Body.Close()
content, err := io.ReadAll(response.Body)
if err != nil {
return user, err
}
emails := []struct {
Email string
Verified bool
Primary bool
}{}
if err := json.Unmarshal(content, &emails); err != nil {
return user, err
}
// extract the verified primary email
for _, email := range emails {
if email.Verified && email.Primary {
user.Email = email.Email
break
}
return nil, err
}
user.Email = email
}
return user, nil
}
// fetchPrimaryEmail sends an API request to retrieve the verified
// primary email, in case "Keep my email address private" was set.
//
// NB! This method can succeed and still return an empty email.
// Error responses that are result of insufficient scopes permissions are ignored.
//
// API reference: https://docs.github.com/en/rest/users/emails?apiVersion=2022-11-28
func (p *Github) fetchPrimaryEmail(token *oauth2.Token) (string, error) {
client := p.Client(token)
response, err := client.Get(p.userApiUrl + "/emails")
if err != nil {
return "", err
}
defer response.Body.Close()
// ignore common http errors caused by insufficient scope permissions
// (the email field is optional, aka. return the auth user without it)
if response.StatusCode == 401 || response.StatusCode == 403 || response.StatusCode == 404 {
return "", nil
}
content, err := io.ReadAll(response.Body)
if err != nil {
return "", err
}
emails := []struct {
Email string
Verified bool
Primary bool
}{}
if err := json.Unmarshal(content, &emails); err != nil {
return "", err
}
// extract the verified primary email
for _, email := range emails {
if email.Verified && email.Primary {
return email.Email, nil
}
}
return "", nil
}
+8 -7
View File
@@ -53,13 +53,14 @@ func (p *Gitlab) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: strconv.Itoa(extracted.Id),
Name: extracted.Name,
Username: extracted.Username,
Email: extracted.Email,
AvatarUrl: extracted.AvatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: strconv.Itoa(extracted.Id),
Name: extracted.Name,
Username: extracted.Username,
Email: extracted.Email,
AvatarUrl: extracted.AvatarUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
+7 -6
View File
@@ -52,12 +52,13 @@ func (p *Google) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: extracted.Id,
Name: extracted.Name,
Email: extracted.Email,
AvatarUrl: extracted.Picture,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Id,
Name: extracted.Name,
Email: extracted.Email,
AvatarUrl: extracted.Picture,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
+6 -5
View File
@@ -59,11 +59,12 @@ func (p *Kakao) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: strconv.Itoa(extracted.Id),
Username: extracted.Profile.Nickname,
AvatarUrl: extracted.Profile.ImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: strconv.Itoa(extracted.Id),
Username: extracted.Profile.Nickname,
AvatarUrl: extracted.Profile.ImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
if extracted.KakaoAccount.IsEmailValid && extracted.KakaoAccount.IsEmailVerified {
user.Email = extracted.KakaoAccount.Email
+6 -5
View File
@@ -53,11 +53,12 @@ func (p *Microsoft) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: extracted.Id,
Name: extracted.Name,
Email: extracted.Email,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Id,
Name: extracted.Name,
Email: extracted.Email,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
+5 -4
View File
@@ -61,10 +61,11 @@ func (p *Spotify) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: extracted.Id,
Name: extracted.Name,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Id,
Name: extracted.Name,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
if len(extracted.Images) > 0 {
user.AvatarUrl = extracted.Images[0].Url
+71
View File
@@ -0,0 +1,71 @@
package auth
import (
"encoding/json"
"strconv"
"golang.org/x/oauth2"
)
var _ Provider = (*Strava)(nil)
// NameStrava is the unique name of the Strava provider.
const NameStrava string = "strava"
// Strava allows authentication via Strava OAuth2.
type Strava struct {
*baseProvider
}
// NewStravaProvider creates new Strava provider instance with some defaults.
func NewStravaProvider() *Strava {
return &Strava{&baseProvider{
scopes: []string{
"profile:read_all",
},
authUrl: "https://www.strava.com/oauth/authorize",
tokenUrl: "https://www.strava.com/api/v3/oauth/token",
userApiUrl: "https://www.strava.com/api/v3/athlete",
}}
}
// FetchAuthUser returns an AuthUser instance based on the Strava's user api.
//
// API reference: https://developers.strava.com/docs/authentication/
func (p *Strava) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
data, err := p.FetchRawUserData(token)
if err != nil {
return nil, err
}
rawUser := map[string]any{}
if err := json.Unmarshal(data, &rawUser); err != nil {
return nil, err
}
extracted := struct {
Id int `json:"id"`
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
Username string `json:"username"`
ProfileImageUrl string `json:"profile"`
// At the time of writing, Strava OAuth2 doesn't support returning the user email address
// Email string `json:"email"`
}{}
if err := json.Unmarshal(data, &extracted); err != nil {
return nil, err
}
user := &AuthUser{
Id: strconv.Itoa(extracted.Id),
Name: extracted.FirstName + " " + extracted.LastName,
Username: extracted.Username,
AvatarUrl: extracted.ProfileImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
}
+8 -7
View File
@@ -61,13 +61,14 @@ func (p *Twitch) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: extracted.Data[0].Id,
Name: extracted.Data[0].DisplayName,
Username: extracted.Data[0].Login,
Email: extracted.Data[0].Email,
AvatarUrl: extracted.Data[0].ProfileImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Data[0].Id,
Name: extracted.Data[0].DisplayName,
Username: extracted.Data[0].Login,
Email: extracted.Data[0].Email,
AvatarUrl: extracted.Data[0].ProfileImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
+7 -6
View File
@@ -63,12 +63,13 @@ func (p *Twitter) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
}
user := &AuthUser{
Id: extracted.Data.Id,
Name: extracted.Data.Name,
Username: extracted.Data.Username,
AvatarUrl: extracted.Data.ProfileImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
Id: extracted.Data.Id,
Name: extracted.Data.Name,
Username: extracted.Data.Username,
AvatarUrl: extracted.Data.ProfileImageUrl,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}
return user, nil
+44 -1
View File
@@ -1,6 +1,8 @@
package filesystem
import (
"bytes"
"errors"
"fmt"
"io"
"mime/multipart"
@@ -46,6 +48,23 @@ func NewFileFromPath(path string) (*File, error) {
return f, nil
}
// NewFileFromBytes creates a new File instance from the provided byte slice.
func NewFileFromBytes(b []byte, name string) (*File, error) {
size := len(b)
if size == 0 {
return nil, errors.New("cannot create an empty file")
}
f := &File{}
f.Reader = &BytesReader{b}
f.Size = int64(size)
f.OriginalName = name
f.Name = normalizeName(f.Reader, f.OriginalName)
return f, nil
}
// NewFileFromMultipart creates a new File instace from the provided multipart header.
func NewFileFromMultipart(mh *multipart.FileHeader) (*File, error) {
f := &File{}
@@ -62,7 +81,7 @@ func NewFileFromMultipart(mh *multipart.FileHeader) (*File, error) {
var _ FileReader = (*MultipartReader)(nil)
// MultipartReader defines a [multipart.FileHeader] reader.
// MultipartReader defines a FileReader from [multipart.FileHeader].
type MultipartReader struct {
Header *multipart.FileHeader
}
@@ -76,6 +95,7 @@ func (r *MultipartReader) Open() (io.ReadSeekCloser, error) {
var _ FileReader = (*PathReader)(nil)
// PathReader defines a FileReader from a local file path.
type PathReader struct {
Path string
}
@@ -87,6 +107,29 @@ func (r *PathReader) Open() (io.ReadSeekCloser, error) {
// -------------------------------------------------------------------
var _ FileReader = (*BytesReader)(nil)
// BytesReader defines a FileReader from bytes content.
type BytesReader struct {
Bytes []byte
}
// Open implements the [filesystem.FileReader] interface.
func (r *BytesReader) Open() (io.ReadSeekCloser, error) {
return &bytesReadSeekCloser{bytes.NewReader(r.Bytes)}, nil
}
type bytesReadSeekCloser struct {
*bytes.Reader
}
// Close implements the [io.ReadSeekCloser] interface.
func (r *bytesReadSeekCloser) Close() error {
return nil
}
// -------------------------------------------------------------------
var extInvalidCharsRegex = regexp.MustCompile(`[^\w\.\*\-\+\=\#]+`)
func normalizeName(fr FileReader, name string) string {
+30 -2
View File
@@ -12,7 +12,7 @@ import (
"github.com/pocketbase/pocketbase/tools/filesystem"
)
func TestNewFileFromFromPath(t *testing.T) {
func TestNewFileFromPath(t *testing.T) {
testDir := createTestDir(t)
defer os.RemoveAll(testDir)
@@ -30,7 +30,7 @@ func TestNewFileFromFromPath(t *testing.T) {
t.Fatalf("Expected nil error, got %v", err)
}
if f.OriginalName != originalName {
t.Fatalf("Expected originalName %q, got %q", originalName, f.OriginalName)
t.Fatalf("Expected OriginalName %q, got %q", originalName, f.OriginalName)
}
if match, _ := regexp.Match(normalizedNamePattern, []byte(f.Name)); !match {
t.Fatalf("Expected Name to match %v, got %q (%v)", normalizedNamePattern, f.Name, err)
@@ -43,6 +43,34 @@ func TestNewFileFromFromPath(t *testing.T) {
}
}
func TestNewFileFromBytes(t *testing.T) {
// nil bytes
if _, err := filesystem.NewFileFromBytes(nil, "photo.jpg"); err == nil {
t.Fatal("Expected error, got nil")
}
// zero bytes
if _, err := filesystem.NewFileFromBytes([]byte{}, "photo.jpg"); err == nil {
t.Fatal("Expected error, got nil")
}
originalName := "image_! noext"
normalizedNamePattern := regexp.QuoteMeta("image_noext_") + `\w{10}` + regexp.QuoteMeta(".txt")
f, err := filesystem.NewFileFromBytes([]byte("text\n"), originalName)
if err != nil {
t.Fatal(err)
}
if f.Size != 5 {
t.Fatalf("Expected Size %v, got %v", 5, f.Size)
}
if f.OriginalName != originalName {
t.Fatalf("Expected OriginalName %q, got %q", originalName, f.OriginalName)
}
if match, _ := regexp.Match(normalizedNamePattern, []byte(f.Name)); !match {
t.Fatalf("Expected Name to match %v, got %q (%v)", normalizedNamePattern, f.Name, err)
}
}
func TestNewFileFromMultipart(t *testing.T) {
formData, mp, err := tests.MockMultipartData(nil, "test")
if err != nil {
+6 -8
View File
@@ -142,7 +142,7 @@ func (s *System) UploadFile(file *File, fileKey string) error {
opts := &blob.WriterOptions{
ContentType: mt.String(),
Metadata: map[string]string{
"original_filename": originalName,
"original-filename": originalName,
},
}
@@ -184,7 +184,7 @@ func (s *System) UploadMultipart(fh *multipart.FileHeader, fileKey string) error
opts := &blob.WriterOptions{
ContentType: mt.String(),
Metadata: map[string]string{
"original_filename": originalName,
"original-filename": originalName,
},
}
@@ -225,13 +225,11 @@ func (s *System) DeletePrefix(prefix string) []error {
})
for {
obj, err := iter.Next(s.ctx)
if err == io.EOF {
break
}
if err != nil {
failed = append(failed, err)
continue
if err != io.EOF {
failed = append(failed, err)
}
break
}
if err := s.Delete(obj.Key); err != nil {
+45 -6
View File
@@ -170,15 +170,52 @@ func TestFileSystemUploadMultipart(t *testing.T) {
}
if exists, _ := fs.Exists(fileKey); !exists {
t.Fatalf("Expected newdir/newkey.txt to exist")
t.Fatalf("Expected %q to exist", fileKey)
}
attrs, err := fs.Attributes(fileKey)
if err != nil {
t.Fatalf("Failed to fetch file attributes: %v", err)
}
if name, ok := attrs.Metadata["original_filename"]; !ok || name != "test" {
t.Fatalf("Expected original_filename to be %q, got %q", "test", name)
if name, ok := attrs.Metadata["original-filename"]; !ok || name != "test" {
t.Fatalf("Expected original-filename to be %q, got %q", "test", name)
}
}
func TestFileSystemUploadFile(t *testing.T) {
dir := createTestDir(t)
defer os.RemoveAll(dir)
fs, err := filesystem.NewLocal(dir)
if err != nil {
t.Fatal(err)
}
defer fs.Close()
fileKey := "newdir/newkey.txt"
file, err := filesystem.NewFileFromPath(filepath.Join(dir, "image.svg"))
if err != nil {
t.Fatalf("Failed to load test file: %v", err)
}
file.OriginalName = "test.txt"
uploadErr := fs.UploadFile(file, fileKey)
if uploadErr != nil {
t.Fatal(uploadErr)
}
if exists, _ := fs.Exists(fileKey); !exists {
t.Fatalf("Expected %q to exist", fileKey)
}
attrs, err := fs.Attributes(fileKey)
if err != nil {
t.Fatalf("Failed to fetch file attributes: %v", err)
}
if name, ok := attrs.Metadata["original-filename"]; !ok || name != file.OriginalName {
t.Fatalf("Expected original-filename to be %q, got %q", file.OriginalName, name)
}
}
@@ -192,13 +229,15 @@ func TestFileSystemUpload(t *testing.T) {
}
defer fs.Close()
uploadErr := fs.Upload([]byte("demo"), "newdir/newkey.txt")
fileKey := "newdir/newkey.txt"
uploadErr := fs.Upload([]byte("demo"), fileKey)
if uploadErr != nil {
t.Fatal(uploadErr)
}
if exists, _ := fs.Exists("newdir/newkey.txt"); !exists {
t.Fatalf("Expected newdir/newkey.txt to exist")
if exists, _ := fs.Exists(fileKey); !exists {
t.Fatalf("Expected %s to exist", fileKey)
}
}
+14
View File
@@ -10,6 +10,20 @@ import (
var cachedPatterns = map[string]*regexp.Regexp{}
// SubtractSlice returns a new slice with only the "base" elements
// that don't exist in "subtract".
func SubtractSlice[T comparable](base []T, subtract []T) []T {
var result = make([]T, 0, len(base))
for _, b := range base {
if !ExistInSlice(b, subtract) {
result = append(result, b)
}
}
return result
}
// ExistInSlice checks whether a comparable element exists in a slice of the same type.
func ExistInSlice[T comparable](item T, list []T) bool {
if len(list) == 0 {
+99
View File
@@ -1,12 +1,111 @@
package list_test
import (
"encoding/json"
"testing"
"github.com/pocketbase/pocketbase/tools/list"
"github.com/pocketbase/pocketbase/tools/types"
)
func TestSubtractSliceString(t *testing.T) {
scenarios := []struct {
base []string
subtract []string
expected string
}{
{
[]string{},
[]string{},
`[]`,
},
{
[]string{},
[]string{"1", "2", "3", "4"},
`[]`,
},
{
[]string{"1", "2", "3", "4"},
[]string{},
`["1","2","3","4"]`,
},
{
[]string{"1", "2", "3", "4"},
[]string{"1", "2", "3", "4"},
`[]`,
},
{
[]string{"1", "2", "3", "4", "7"},
[]string{"2", "4", "5", "6"},
`["1","3","7"]`,
},
}
for i, s := range scenarios {
result := list.SubtractSlice(s.base, s.subtract)
raw, err := json.Marshal(result)
if err != nil {
t.Fatalf("(%d) Failed to serialize: %v", i, err)
}
strResult := string(raw)
if strResult != s.expected {
t.Fatalf("(%d) Expected %v, got %v", i, s.expected, strResult)
}
}
}
func TestSubtractSliceInt(t *testing.T) {
scenarios := []struct {
base []int
subtract []int
expected string
}{
{
[]int{},
[]int{},
`[]`,
},
{
[]int{},
[]int{1, 2, 3, 4},
`[]`,
},
{
[]int{1, 2, 3, 4},
[]int{},
`[1,2,3,4]`,
},
{
[]int{1, 2, 3, 4},
[]int{1, 2, 3, 4},
`[]`,
},
{
[]int{1, 2, 3, 4, 7},
[]int{2, 4, 5, 6},
`[1,3,7]`,
},
}
for i, s := range scenarios {
result := list.SubtractSlice(s.base, s.subtract)
raw, err := json.Marshal(result)
if err != nil {
t.Fatalf("(%d) Failed to serialize: %v", i, err)
}
strResult := string(raw)
if strResult != s.expected {
t.Fatalf("(%d) Expected %v, got %v", i, s.expected, strResult)
}
}
}
func TestExistInSliceString(t *testing.T) {
scenarios := []struct {
item string
+59 -25
View File
@@ -2,6 +2,7 @@ package migrate
import (
"fmt"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
@@ -72,9 +73,19 @@ func (r *Runner) Run(args ...string) error {
}
}
names, err := r.lastAppliedMigrations(toRevertCount)
if err != nil {
color.Red(err.Error())
return err
}
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Do you really want to revert the last %d applied migration(s)?", toRevertCount),
Message: fmt.Sprintf(
"\n%v\nDo you really want to revert the last %d applied migration(s)?",
strings.Join(names, "\n"),
toRevertCount,
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
@@ -138,38 +149,43 @@ func (r *Runner) Up() ([]string, error) {
return applied, nil
}
// Down reverts the last `toRevertCount` applied migrations.
// Down reverts the last `toRevertCount` applied migrations
// (in the order they were applied).
//
// On success returns list with the reverted migrations file names.
func (r *Runner) Down(toRevertCount int) ([]string, error) {
reverted := make([]string, 0, toRevertCount)
names, appliedErr := r.lastAppliedMigrations(toRevertCount)
if appliedErr != nil {
return nil, appliedErr
}
err := r.db.Transactional(func(tx *dbx.Tx) error {
for i := len(r.migrationsList.Items()) - 1; i >= 0; i-- {
m := r.migrationsList.Item(i)
// skip unapplied
if !r.isMigrationApplied(tx, m.File) {
continue
}
// revert limit reached
if toRevertCount-len(reverted) <= 0 {
break
}
// ignore empty Down action
if m.Down != nil {
if err := m.Down(tx); err != nil {
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
for _, name := range names {
for _, m := range r.migrationsList.Items() {
if m.File != name {
continue
}
}
if err := r.saveRevertedMigration(tx, m.File); err != nil {
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
}
// revert limit reached
if toRevertCount-len(reverted) <= 0 {
return nil
}
reverted = append(reverted, m.File)
// ignore empty Down action
if m.Down != nil {
if err := m.Down(tx); err != nil {
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
}
}
if err := r.saveRevertedMigration(tx, m.File); err != nil {
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
}
reverted = append(reverted, m.File)
}
}
return nil
@@ -178,6 +194,7 @@ func (r *Runner) Down(toRevertCount int) ([]string, error) {
if err != nil {
return nil, err
}
return reverted, nil
}
@@ -207,7 +224,7 @@ func (r *Runner) isMigrationApplied(tx dbx.Builder, file string) bool {
func (r *Runner) saveAppliedMigration(tx dbx.Builder, file string) error {
_, err := tx.Insert(r.tableName, dbx.Params{
"file": file,
"applied": time.Now().Unix(),
"applied": time.Now().UnixMicro(),
}).Execute()
return err
@@ -218,3 +235,20 @@ func (r *Runner) saveRevertedMigration(tx dbx.Builder, file string) error {
return err
}
func (r *Runner) lastAppliedMigrations(limit int) ([]string, error) {
var files = make([]string, 0, limit)
err := r.db.Select("file").
From(r.tableName).
Where(dbx.Not(dbx.HashExp{"applied": nil})).
OrderBy("applied DESC", "file DESC").
Limit(int64(limit)).
Column(&files)
if err != nil {
return nil, err
}
return files, nil
}
+56 -40
View File
@@ -3,6 +3,7 @@ package migrate
import (
"context"
"database/sql"
"encoding/json"
"testing"
"time"
@@ -52,73 +53,88 @@ func TestRunnerUpAndDown(t *testing.T) {
}
defer testDB.Close()
var test1UpCalled bool
var test1DownCalled bool
var test2UpCalled bool
var test2DownCalled bool
callsOrder := []string{}
l := MigrationsList{}
l.Register(func(db dbx.Builder) error {
test1UpCalled = true
callsOrder = append(callsOrder, "up2")
return nil
}, func(db dbx.Builder) error {
test1DownCalled = true
return nil
}, "1_test")
l.Register(func(db dbx.Builder) error {
test2UpCalled = true
return nil
}, func(db dbx.Builder) error {
test2DownCalled = true
callsOrder = append(callsOrder, "down2")
return nil
}, "2_test")
l.Register(func(db dbx.Builder) error {
callsOrder = append(callsOrder, "up3")
return nil
}, func(db dbx.Builder) error {
callsOrder = append(callsOrder, "down3")
return nil
}, "3_test")
l.Register(func(db dbx.Builder) error {
callsOrder = append(callsOrder, "up1")
return nil
}, func(db dbx.Builder) error {
callsOrder = append(callsOrder, "down1")
return nil
}, "1_test")
r, err := NewRunner(testDB.DB, l)
if err != nil {
t.Fatal(err)
}
// simulate partially run migration
r.saveAppliedMigration(testDB, r.migrationsList.Item(0).File)
// simulate partially out-of-order run migration
r.saveAppliedMigration(testDB, "2_test")
// ---------------------------------------------------------------
// Up()
// ---
// ---------------------------------------------------------------
if _, err := r.Up(); err != nil {
t.Fatal(err)
}
if test1UpCalled {
t.Fatalf("Didn't expect 1_test to be called")
}
expectedUpCallsOrder := `["up1","up3"]` // skip up2 since it was applied previously
if !test2UpCalled {
t.Fatalf("Expected 2_test to be called")
}
// simulate unrun migration
var test3DownCalled bool
r.migrationsList.Register(nil, func(db dbx.Builder) error {
test3DownCalled = true
return nil
}, "3_test")
// Down()
// ---
// revert one migration
if _, err := r.Down(1); err != nil {
upCallsOrder, err := json.Marshal(callsOrder)
if err != nil {
t.Fatal(err)
}
if test3DownCalled {
t.Fatal("Didn't expect 3_test to be reverted.")
if v := string(upCallsOrder); v != expectedUpCallsOrder {
t.Fatalf("Expected Up() calls order %s, got %s", expectedUpCallsOrder, upCallsOrder)
}
if !test2DownCalled {
t.Fatal("Expected 2_test to be reverted.")
// ---------------------------------------------------------------
// reset callsOrder
callsOrder = []string{}
// simulate unrun migration
r.migrationsList.Register(nil, func(db dbx.Builder) error {
callsOrder = append(callsOrder, "down4")
return nil
}, "4_test")
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// Down()
// ---------------------------------------------------------------
if _, err := r.Down(2); err != nil {
t.Fatal(err)
}
if test1DownCalled {
t.Fatal("Didn't expect 1_test to be reverted.")
expectedDownCallsOrder := `["down3","down1"]` // revert in the applied order
downCallsOrder, err := json.Marshal(callsOrder)
if err != nil {
t.Fatal(err)
}
if v := string(downCallsOrder); v != expectedDownCallsOrder {
t.Fatalf("Expected Down() calls order %s, got %s", expectedDownCallsOrder, downCallsOrder)
}
}
+275 -66
View File
@@ -85,72 +85,119 @@ func (f FilterData) build(data []fexpr.ExprGroup, fieldResolver FieldResolver) (
}
func (f FilterData) resolveTokenizedExpr(expr fexpr.Expr, fieldResolver FieldResolver) (dbx.Expression, error) {
lName, lParams, lErr := f.resolveToken(expr.Left, fieldResolver)
if lName == "" || lErr != nil {
return nil, fmt.Errorf("Invalid left operand %q - %v.", expr.Left.Literal, lErr)
lResult, lErr := resolveToken(expr.Left, fieldResolver)
if lErr != nil || lResult.Identifier == "" {
return nil, fmt.Errorf("invalid left operand %q - %v", expr.Left.Literal, lErr)
}
rName, rParams, rErr := f.resolveToken(expr.Right, fieldResolver)
if rName == "" || rErr != nil {
return nil, fmt.Errorf("Invalid right operand %q - %v.", expr.Right.Literal, rErr)
rResult, rErr := resolveToken(expr.Right, fieldResolver)
if rErr != nil || rResult.Identifier == "" {
return nil, fmt.Errorf("invalid right operand %q - %v", expr.Right.Literal, rErr)
}
switch expr.Op {
case fexpr.SignEq:
return dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') = COALESCE(%s, '')", lName, rName), mergeParams(lParams, rParams)), nil
case fexpr.SignNeq:
return dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') != COALESCE(%s, '')", lName, rName), mergeParams(lParams, rParams)), nil
case fexpr.SignLike:
// the right side is a column and therefor wrap it with "%" for contains like behavior
if len(rParams) == 0 {
return dbx.NewExp(fmt.Sprintf("%s LIKE ('%%' || %s || '%%') ESCAPE '\\'", lName, rName), lParams), nil
}
return dbx.NewExp(fmt.Sprintf("%s LIKE %s ESCAPE '\\'", lName, rName), mergeParams(lParams, wrapLikeParams(rParams))), nil
case fexpr.SignNlike:
// the right side is a column and therefor wrap it with "%" for not-contains like behavior
if len(rParams) == 0 {
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE ('%%' || %s || '%%') ESCAPE '\\'", lName, rName), lParams), nil
}
// normalize operands and switch sides if the left operand is a number/text, but the right one is a column
// (usually this shouldn't be needed, but it's kept for backward compatibility)
if len(lParams) > 0 && len(rParams) == 0 {
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s ESCAPE '\\'", rName, lName), wrapLikeParams(lParams)), nil
}
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s ESCAPE '\\'", lName, rName), mergeParams(lParams, wrapLikeParams(rParams))), nil
case fexpr.SignLt:
return dbx.NewExp(fmt.Sprintf("%s < %s", lName, rName), mergeParams(lParams, rParams)), nil
case fexpr.SignLte:
return dbx.NewExp(fmt.Sprintf("%s <= %s", lName, rName), mergeParams(lParams, rParams)), nil
case fexpr.SignGt:
return dbx.NewExp(fmt.Sprintf("%s > %s", lName, rName), mergeParams(lParams, rParams)), nil
case fexpr.SignGte:
return dbx.NewExp(fmt.Sprintf("%s >= %s", lName, rName), mergeParams(lParams, rParams)), nil
}
return nil, fmt.Errorf("Unknown expression operator %q", expr.Op)
return buildExpr(lResult, expr.Op, rResult)
}
func (f FilterData) resolveToken(token fexpr.Token, fieldResolver FieldResolver) (name string, params dbx.Params, err error) {
func buildExpr(
left *ResolverResult,
op fexpr.SignOp,
right *ResolverResult,
) (dbx.Expression, error) {
var expr dbx.Expression
switch op {
case fexpr.SignEq, fexpr.SignAnyEq:
expr = dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') = COALESCE(%s, '')", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
case fexpr.SignNeq, fexpr.SignAnyNeq:
expr = dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') != COALESCE(%s, '')", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
case fexpr.SignLike, fexpr.SignAnyLike:
// the right side is a column and therefor wrap it with "%" for contains like behavior
if len(right.Params) == 0 {
expr = dbx.NewExp(fmt.Sprintf("%s LIKE ('%%' || %s || '%%') ESCAPE '\\'", left.Identifier, right.Identifier), left.Params)
} else {
expr = dbx.NewExp(fmt.Sprintf("%s LIKE %s ESCAPE '\\'", left.Identifier, right.Identifier), mergeParams(left.Params, wrapLikeParams(right.Params)))
}
case fexpr.SignNlike, fexpr.SignAnyNlike:
// the right side is a column and therefor wrap it with "%" for not-contains like behavior
if len(right.Params) == 0 {
expr = dbx.NewExp(fmt.Sprintf("%s NOT LIKE ('%%' || %s || '%%') ESCAPE '\\'", left.Identifier, right.Identifier), left.Params)
} else {
expr = dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s ESCAPE '\\'", left.Identifier, right.Identifier), mergeParams(left.Params, wrapLikeParams(right.Params)))
}
case fexpr.SignLt, fexpr.SignAnyLt:
expr = dbx.NewExp(fmt.Sprintf("%s < %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
case fexpr.SignLte, fexpr.SignAnyLte:
expr = dbx.NewExp(fmt.Sprintf("%s <= %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
case fexpr.SignGt, fexpr.SignAnyGt:
expr = dbx.NewExp(fmt.Sprintf("%s > %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
case fexpr.SignGte, fexpr.SignAnyGte:
expr = dbx.NewExp(fmt.Sprintf("%s >= %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
}
if expr == nil {
return nil, fmt.Errorf("unknown expression operator %q", op)
}
// multi-match expressions
if !isAnyMatchOp(op) {
if left.MultiMatchSubQuery != nil && right.MultiMatchSubQuery != nil {
mm := &manyVsManyExpr{
leftSubQuery: left.MultiMatchSubQuery,
rightSubQuery: right.MultiMatchSubQuery,
op: op,
}
expr = dbx.And(expr, mm)
} else if left.MultiMatchSubQuery != nil {
mm := &manyVsOneExpr{
subQuery: left.MultiMatchSubQuery,
op: op,
otherOperand: right,
}
expr = dbx.And(expr, mm)
} else if right.MultiMatchSubQuery != nil {
mm := &manyVsOneExpr{
subQuery: right.MultiMatchSubQuery,
op: op,
otherOperand: left,
inverse: true,
}
expr = dbx.And(expr, mm)
}
}
if left.AfterBuild != nil {
expr = left.AfterBuild(expr)
}
if right.AfterBuild != nil {
expr = right.AfterBuild(expr)
}
return expr, nil
}
func resolveToken(token fexpr.Token, fieldResolver FieldResolver) (*ResolverResult, error) {
switch token.Type {
case fexpr.TokenIdentifier:
// current datetime constant
// ---
if token.Literal == "@now" {
placeholder := "t" + security.PseudorandomString(8)
name := fmt.Sprintf("{:%s}", placeholder)
params := dbx.Params{placeholder: types.NowDateTime().String()}
placeholder := "t" + security.PseudorandomString(5)
return name, params, nil
return &ResolverResult{
Identifier: "{:" + placeholder + "}",
Params: dbx.Params{placeholder: types.NowDateTime().String()},
}, nil
}
// custom resolver
// ---
name, params, err := fieldResolver.Resolve(token.Literal)
result, err := fieldResolver.Resolve(token.Literal)
if name == "" || err != nil {
if err != nil || result.Identifier == "" {
m := map[string]string{
// if `null` field is missing, treat `null` identifier as NULL token
"null": "NULL",
@@ -160,27 +207,46 @@ func (f FilterData) resolveToken(token fexpr.Token, fieldResolver FieldResolver)
"false": "0",
}
if v, ok := m[strings.ToLower(token.Literal)]; ok {
return v, nil, nil
return &ResolverResult{Identifier: v}, nil
}
return "", nil, err
return nil, err
}
return name, params, err
return result, err
case fexpr.TokenText:
placeholder := "t" + security.PseudorandomString(8)
name := fmt.Sprintf("{:%s}", placeholder)
params := dbx.Params{placeholder: token.Literal}
placeholder := "t" + security.PseudorandomString(5)
return name, params, nil
return &ResolverResult{
Identifier: "{:" + placeholder + "}",
Params: dbx.Params{placeholder: token.Literal},
}, nil
case fexpr.TokenNumber:
placeholder := "t" + security.PseudorandomString(8)
name := fmt.Sprintf("{:%s}", placeholder)
params := dbx.Params{placeholder: cast.ToFloat64(token.Literal)}
placeholder := "t" + security.PseudorandomString(5)
return name, params, nil
return &ResolverResult{
Identifier: "{:" + placeholder + "}",
Params: dbx.Params{placeholder: cast.ToFloat64(token.Literal)},
}, nil
}
return "", nil, errors.New("Unresolvable token type.")
return nil, errors.New("unresolvable token type")
}
func isAnyMatchOp(op fexpr.SignOp) bool {
switch op {
case
fexpr.SignAnyEq,
fexpr.SignAnyNeq,
fexpr.SignAnyLike,
fexpr.SignAnyNlike,
fexpr.SignAnyLt,
fexpr.SignAnyLte,
fexpr.SignAnyGt,
fexpr.SignAnyGte:
return true
}
return false
}
// mergeParams returns new dbx.Params where each provided params item
@@ -218,18 +284,24 @@ func wrapLikeParams(params dbx.Params) dbx.Params {
// -------------------------------------------------------------------
var _ dbx.Expression = (*opExpr)(nil)
// opExpr defines an expression that contains a raw sql operator string.
type opExpr struct {
op string
}
// Build converts an expression into a SQL fragment.
// Build converts the expression into a SQL fragment.
//
// Implements [dbx.Expression] interface.
func (e *opExpr) Build(db *dbx.DB, params dbx.Params) string {
return e.op
}
// -------------------------------------------------------------------
var _ dbx.Expression = (*concatExpr)(nil)
// concatExpr defines an expression that concatenates multiple
// other expressions with a specified separator.
type concatExpr struct {
@@ -237,7 +309,7 @@ type concatExpr struct {
separator string
}
// Build converts an expression into a SQL fragment.
// Build converts the expression into a SQL fragment.
//
// Implements [dbx.Expression] interface.
func (e *concatExpr) Build(db *dbx.DB, params dbx.Params) string {
@@ -247,12 +319,12 @@ func (e *concatExpr) Build(db *dbx.DB, params dbx.Params) string {
stringParts := make([]string, 0, len(e.parts))
for _, a := range e.parts {
if a == nil {
for _, p := range e.parts {
if p == nil {
continue
}
if sql := a.Build(db, params); sql != "" {
if sql := p.Build(db, params); sql != "" {
stringParts = append(stringParts, sql)
}
}
@@ -267,3 +339,140 @@ func (e *concatExpr) Build(db *dbx.DB, params dbx.Params) string {
return "(" + strings.Join(stringParts, e.separator) + ")"
}
// -------------------------------------------------------------------
var _ dbx.Expression = (*manyVsManyExpr)(nil)
// manyVsManyExpr constructs a multi-match many<->many db where expression.
//
// Expects leftSubQuery and rightSubQuery to return a subquery with a
// single "multiMatchValue" column.
type manyVsManyExpr struct {
leftSubQuery dbx.Expression
rightSubQuery dbx.Expression
op fexpr.SignOp
}
// Build converts the expression into a SQL fragment.
//
// Implements [dbx.Expression] interface.
func (e *manyVsManyExpr) Build(db *dbx.DB, params dbx.Params) string {
if e.leftSubQuery == nil || e.rightSubQuery == nil {
return "0=1"
}
lAlias := "__ml" + security.PseudorandomString(5)
rAlias := "__mr" + security.PseudorandomString(5)
whereExpr, buildErr := buildExpr(
&ResolverResult{
Identifier: "[[" + lAlias + ".multiMatchValue]]",
},
e.op,
&ResolverResult{
Identifier: "[[" + rAlias + ".multiMatchValue]]",
// note: the AfterBuild needs to be handled only once and it
// doesn't matter whether it is applied on the left or right subquery operand
AfterBuild: multiMatchAfterBuildFunc(e.op, lAlias, rAlias),
},
)
if buildErr != nil {
return "0=1"
}
return fmt.Sprintf(
"NOT EXISTS (SELECT 1 FROM (%s) {{%s}} LEFT JOIN (%s) {{%s}} WHERE %s)",
e.leftSubQuery.Build(db, params),
lAlias,
e.rightSubQuery.Build(db, params),
rAlias,
whereExpr.Build(db, params),
)
}
// -------------------------------------------------------------------
var _ dbx.Expression = (*manyVsOneExpr)(nil)
// manyVsManyExpr constructs a multi-match many<->one db where expression.
//
// Expects subQuery to return a subquery with a single "multiMatchValue" column.
//
// You can set inverse=false to reverse the condition sides (aka. one<->many).
type manyVsOneExpr struct {
subQuery dbx.Expression
op fexpr.SignOp
otherOperand *ResolverResult
inverse bool
}
// Build converts the expression into a SQL fragment.
//
// Implements [dbx.Expression] interface.
func (e *manyVsOneExpr) Build(db *dbx.DB, params dbx.Params) string {
if e.subQuery == nil {
return "0=1"
}
alias := "__sm" + security.PseudorandomString(5)
r1 := &ResolverResult{
Identifier: "[[" + alias + ".multiMatchValue]]",
AfterBuild: multiMatchAfterBuildFunc(e.op, alias),
}
r2 := &ResolverResult{
Identifier: e.otherOperand.Identifier,
Params: e.otherOperand.Params,
}
var whereExpr dbx.Expression
var buildErr error
if e.inverse {
whereExpr, buildErr = buildExpr(r2, e.op, r1)
} else {
whereExpr, buildErr = buildExpr(r1, e.op, r2)
}
if buildErr != nil {
return "0=1"
}
return fmt.Sprintf(
"NOT EXISTS (SELECT 1 FROM (%s) {{%s}} WHERE %s)",
e.subQuery.Build(db, params),
alias,
whereExpr.Build(db, params),
)
}
func multiMatchAfterBuildFunc(op fexpr.SignOp, multiMatchAliases ...string) func(dbx.Expression) dbx.Expression {
return func(expr dbx.Expression) dbx.Expression {
expr = dbx.Not(expr) // inverse for the not-exist expression
if op == fexpr.SignEq {
return expr
}
orExprs := make([]dbx.Expression, len(multiMatchAliases)+1)
orExprs[0] = expr
// Add an optional "IS NULL" condition(s) to handle the empty rows result.
//
// For example, let's assume that some "rel" field is [nonemptyRel1, nonemptyRel2, emptyRel3],
// The filter "rel.total > 0" will ensures that the above will return true only if all relations
// are existing and match the condition.
//
// The "=" operator is excluded because it will never equal directly with NULL anyway
// and also because we want in case "rel.id = ''" is specified to allow
// matching the empty relations (they will match due to the applied COALESCE).
for i, mAlias := range multiMatchAliases {
orExprs[i+1] = dbx.NewExp("[[" + mAlias + ".multiMatchValue]] IS NULL")
}
return dbx.Or(orExprs...)
}
}
+3 -3
View File
@@ -495,12 +495,12 @@ func (t *testFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
return nil
}
func (t *testFieldResolver) Resolve(field string) (name string, placeholderParams dbx.Params, err error) {
func (t *testFieldResolver) Resolve(field string) (*ResolverResult, error) {
t.ResolveCalls++
if field == "unknown" {
return "", nil, errors.New("test error")
return nil, errors.New("test error")
}
return field, nil, nil
return &ResolverResult{Identifier: field}, nil
}
+25 -4
View File
@@ -8,6 +8,25 @@ import (
"github.com/pocketbase/pocketbase/tools/list"
)
// ResolverResult defines a single FieldResolver.Resolve() successfully parsed result.
type ResolverResult struct {
// Identifier is the plain SQL identifier/column that will be used
// in the final db expression as left or right operand.
Identifier string
// Params is a map with db placeholder->value pairs that will be added
// to the query when building both resolved operands/sides in a single expression.
Params dbx.Params
// MultiMatchSubQuery is an optional sub query expression that will be added
// in addition to the combined ResolverResult expression during build.
MultiMatchSubQuery dbx.Expression
// AfterBuild is an optional function that will be called after building
// and combining the result of both resolved operands/sides in a single expression.
AfterBuild func(expr dbx.Expression) dbx.Expression
}
// FieldResolver defines an interface for managing search fields.
type FieldResolver interface {
// UpdateQuery allows to updated the provided db query based on the
@@ -18,7 +37,7 @@ type FieldResolver interface {
// Resolve parses the provided field and returns a properly
// formatted db identifier (eg. NULL, quoted column, placeholder parameter, etc.).
Resolve(field string) (name string, placeholderParams dbx.Params, err error)
Resolve(field string) (*ResolverResult, error)
}
// NewSimpleFieldResolver creates a new `SimpleFieldResolver` with the
@@ -49,10 +68,12 @@ func (r *SimpleFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
// Resolve implements `search.Resolve` interface.
//
// Returns error if `field` is not in `r.allowedFields`.
func (r *SimpleFieldResolver) Resolve(field string) (resultName string, placeholderParams dbx.Params, err error) {
func (r *SimpleFieldResolver) Resolve(field string) (*ResolverResult, error) {
if !list.ExistInSliceWithRegex(field, r.allowedFields) {
return "", nil, fmt.Errorf("Failed to resolve field %q.", field)
return nil, fmt.Errorf("Failed to resolve field %q.", field)
}
return fmt.Sprintf("[[%s]]", inflector.Columnify(field)), nil, nil
return &ResolverResult{
Identifier: "[[" + inflector.Columnify(field) + "]]",
}, nil
}
+9 -5
View File
@@ -61,7 +61,7 @@ func TestSimpleFieldResolverResolve(t *testing.T) {
}
for i, s := range scenarios {
name, params, err := r.Resolve(s.fieldName)
r, err := r.Resolve(s.fieldName)
hasErr := err != nil
if hasErr != s.expectError {
@@ -69,13 +69,17 @@ func TestSimpleFieldResolverResolve(t *testing.T) {
continue
}
if name != s.expectName {
t.Errorf("(%d) Expected name %q, got %q", i, s.expectName, name)
if hasErr {
continue
}
if r.Identifier != s.expectName {
t.Errorf("(%d) Expected r.Identifier %q, got %q", i, s.expectName, r.Identifier)
}
// params should be empty
if len(params) != 0 {
t.Errorf("(%d) Expected 0 params, got %v", i, params)
if len(r.Params) != 0 {
t.Errorf("(%d) Expected 0 r.Params, got %v", i, r.Params)
}
}
}
+13 -6
View File
@@ -5,6 +5,8 @@ import (
"strings"
)
const randomSortKey string = "@random"
// sort field directions
const (
SortAsc string = "ASC"
@@ -19,14 +21,19 @@ type SortField struct {
// BuildExpr resolves the sort field into a valid db sort expression.
func (s *SortField) BuildExpr(fieldResolver FieldResolver) (string, error) {
name, params, err := fieldResolver.Resolve(s.Name)
// invalidate empty fields and non-column identifiers
if err != nil || len(params) > 0 || name == "" || strings.ToLower(name) == "null" {
return "", fmt.Errorf("Invalid sort field %q.", s.Name)
// special case for random sort
if s.Name == randomSortKey {
return "RANDOM()", nil
}
return fmt.Sprintf("%s %s", name, s.Direction), nil
result, err := fieldResolver.Resolve(s.Name)
// invalidate empty fields and non-column identifiers
if err != nil || len(result.Params) > 0 || result.Identifier == "" || strings.ToLower(result.Identifier) == "null" {
return "", fmt.Errorf("invalid sort field %q", s.Name)
}
return fmt.Sprintf("%s %s", result.Identifier, s.Direction), nil
}
// ParseSortFromString parses the provided string expression
+3
View File
@@ -27,6 +27,8 @@ func TestSortFieldBuildExpr(t *testing.T) {
{search.SortField{"test1", search.SortAsc}, false, "[[test1]] ASC"},
// allowed field - desc
{search.SortField{"test1", search.SortDesc}, false, "[[test1]] DESC"},
// special @random field (ignore direction)
{search.SortField{"@random", search.SortDesc}, false, "RANDOM()"},
}
for i, s := range scenarios {
@@ -54,6 +56,7 @@ func TestParseSortFromString(t *testing.T) {
{"+test", `[{"name":"test","direction":"ASC"}]`},
{"-test", `[{"name":"test","direction":"DESC"}]`},
{"test1,-test2,+test3", `[{"name":"test1","direction":"ASC"},{"name":"test2","direction":"DESC"},{"name":"test3","direction":"ASC"}]`},
{"@random,-test", `[{"name":"@random","direction":"ASC"},{"name":"test","direction":"DESC"}]`},
}
for i, s := range scenarios {
+2 -1
View File
@@ -4,7 +4,8 @@ PB_INSTALLER_PARAM = "installer"
PB_OAUTH2_EXAMPLE = "https://pocketbase.io/docs/authentication/#web-oauth2-integration"
PB_RULES_SYNTAX_DOCS = "https://pocketbase.io/docs/api-rules-and-filters/"
PB_FILE_UPLOAD_DOCS = "https://pocketbase.io/docs/files-handling/"
PB_DOCS_URL = "https://pocketbase.io/docs/"
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
PB_VERSION = "v0.10.2"
PB_VERSION = "v0.11.2"
@@ -1,4 +1,4 @@
import{S as ke,i as be,s as ge,e as r,w as b,b as g,c as _e,f as k,g as h,h as n,m as me,x as G,N as re,O as we,k as ve,P as Ce,n as Pe,t as L,a as Y,o as _,d as pe,Q as Me,C as Se,p as $e,r as H,u as je,M as Ae}from"./index.2a48c906.js";import{S as Be}from"./SdkTabs.9dbb2ca3.js";function ue(a,l,o){const s=a.slice();return s[5]=l[o],s}function de(a,l,o){const s=a.slice();return s[5]=l[o],s}function fe(a,l){let o,s=l[5].code+"",m,f,i,u;function d(){return l[4](l[5])}return{key:a,first:null,c(){o=r("button"),m=b(s),f=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(v,C){h(v,o,C),n(o,m),n(o,f),i||(u=je(o,"click",d),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&G(m,s),C&6&&H(o,"active",l[1]===l[5].code)},d(v){v&&_(o),i=!1,u()}}}function he(a,l){let o,s,m,f;return s=new Ae({props:{content:l[5].body}}),{key:a,first:null,c(){o=r("div"),_e(s.$$.fragment),m=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(i,u){h(i,o,u),me(s,o,null),n(o,m),f=!0},p(i,u){l=i;const d={};u&4&&(d.content=l[5].body),s.$set(d),(!f||u&6)&&H(o,"active",l[1]===l[5].code)},i(i){f||(L(s.$$.fragment,i),f=!0)},o(i){Y(s.$$.fragment,i),f=!1},d(i){i&&_(o),pe(s)}}}function Oe(a){var ae,ne;let l,o,s=a[0].name+"",m,f,i,u,d,v,C,F=a[0].name+"",U,X,q,P,D,j,W,M,K,R,Q,A,Z,V,y=a[0].name+"",I,x,E,B,J,S,O,w=[],ee=new Map,te,T,p=[],le=new Map,$;P=new Be({props:{js:`
import{S as ke,i as be,s as ge,e as r,w as b,b as g,c as _e,f as k,g as h,h as n,m as me,x as G,O as re,P as we,k as ve,Q as Ce,n as Pe,t as L,a as Y,o as _,d as pe,R as Me,C as Se,p as $e,r as H,u as je,N as Ae}from"./index.536fddcb.js";import{S as Be}from"./SdkTabs.3571c26b.js";function ue(a,l,o){const s=a.slice();return s[5]=l[o],s}function de(a,l,o){const s=a.slice();return s[5]=l[o],s}function fe(a,l){let o,s=l[5].code+"",m,f,i,u;function d(){return l[4](l[5])}return{key:a,first:null,c(){o=r("button"),m=b(s),f=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(v,C){h(v,o,C),n(o,m),n(o,f),i||(u=je(o,"click",d),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&G(m,s),C&6&&H(o,"active",l[1]===l[5].code)},d(v){v&&_(o),i=!1,u()}}}function he(a,l){let o,s,m,f;return s=new Ae({props:{content:l[5].body}}),{key:a,first:null,c(){o=r("div"),_e(s.$$.fragment),m=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(i,u){h(i,o,u),me(s,o,null),n(o,m),f=!0},p(i,u){l=i;const d={};u&4&&(d.content=l[5].body),s.$set(d),(!f||u&6)&&H(o,"active",l[1]===l[5].code)},i(i){f||(L(s.$$.fragment,i),f=!0)},o(i){Y(s.$$.fragment,i),f=!1},d(i){i&&_(o),pe(s)}}}function Oe(a){var ae,ne;let l,o,s=a[0].name+"",m,f,i,u,d,v,C,F=a[0].name+"",U,R,q,P,D,j,W,M,K,X,Q,A,Z,V,y=a[0].name+"",I,x,E,B,J,S,O,w=[],ee=new Map,te,T,p=[],le=new Map,$;P=new Be({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${a[3]}');
@@ -14,7 +14,7 @@ import{S as ke,i as be,s as ge,e as r,w as b,b as g,c as _e,f as k,g as h,h as n
...
final result = await pb.collection('${(ne=a[0])==null?void 0:ne.name}').listAuthMethods();
`}});let z=a[2];const oe=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=de(a,z,e),c=oe(t);ee.set(c,w[e]=fe(c,t))}let N=a[2];const se=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=ue(a,N,e),c=se(t);le.set(c,p[e]=he(c,t))}return{c(){l=r("h3"),o=b("List auth methods ("),m=b(s),f=b(")"),i=g(),u=r("div"),d=r("p"),v=b("Returns a public list with all allowed "),C=r("strong"),U=b(F),X=b(" authentication methods."),q=g(),_e(P.$$.fragment),D=g(),j=r("h6"),j.textContent="API details",W=g(),M=r("div"),K=r("strong"),K.textContent="GET",R=g(),Q=r("div"),A=r("p"),Z=b("/api/collections/"),V=r("strong"),I=b(y),x=b("/auth-methods"),E=g(),B=r("div"),B.textContent="Responses",J=g(),S=r("div"),O=r("div");for(let e=0;e<w.length;e+=1)w[e].c();te=g(),T=r("div");for(let e=0;e<p.length;e+=1)p[e].c();k(l,"class","m-b-sm"),k(u,"class","content txt-lg m-b-sm"),k(j,"class","m-b-xs"),k(K,"class","label label-primary"),k(Q,"class","content"),k(M,"class","alert alert-info"),k(B,"class","section-title"),k(O,"class","tabs-header compact left"),k(T,"class","tabs-content"),k(S,"class","tabs")},m(e,t){h(e,l,t),n(l,o),n(l,m),n(l,f),h(e,i,t),h(e,u,t),n(u,d),n(d,v),n(d,C),n(C,U),n(d,X),h(e,q,t),me(P,e,t),h(e,D,t),h(e,j,t),h(e,W,t),h(e,M,t),n(M,K),n(M,R),n(M,Q),n(Q,A),n(A,Z),n(A,V),n(V,I),n(A,x),h(e,E,t),h(e,B,t),h(e,J,t),h(e,S,t),n(S,O);for(let c=0;c<w.length;c+=1)w[c].m(O,null);n(S,te),n(S,T);for(let c=0;c<p.length;c+=1)p[c].m(T,null);$=!0},p(e,[t]){var ie,ce;(!$||t&1)&&s!==(s=e[0].name+"")&&G(m,s),(!$||t&1)&&F!==(F=e[0].name+"")&&G(U,F);const c={};t&9&&(c.js=`
`}});let z=a[2];const oe=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=de(a,z,e),c=oe(t);ee.set(c,w[e]=fe(c,t))}let N=a[2];const se=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=ue(a,N,e),c=se(t);le.set(c,p[e]=he(c,t))}return{c(){l=r("h3"),o=b("List auth methods ("),m=b(s),f=b(")"),i=g(),u=r("div"),d=r("p"),v=b("Returns a public list with all allowed "),C=r("strong"),U=b(F),R=b(" authentication methods."),q=g(),_e(P.$$.fragment),D=g(),j=r("h6"),j.textContent="API details",W=g(),M=r("div"),K=r("strong"),K.textContent="GET",X=g(),Q=r("div"),A=r("p"),Z=b("/api/collections/"),V=r("strong"),I=b(y),x=b("/auth-methods"),E=g(),B=r("div"),B.textContent="Responses",J=g(),S=r("div"),O=r("div");for(let e=0;e<w.length;e+=1)w[e].c();te=g(),T=r("div");for(let e=0;e<p.length;e+=1)p[e].c();k(l,"class","m-b-sm"),k(u,"class","content txt-lg m-b-sm"),k(j,"class","m-b-xs"),k(K,"class","label label-primary"),k(Q,"class","content"),k(M,"class","alert alert-info"),k(B,"class","section-title"),k(O,"class","tabs-header compact left"),k(T,"class","tabs-content"),k(S,"class","tabs")},m(e,t){h(e,l,t),n(l,o),n(l,m),n(l,f),h(e,i,t),h(e,u,t),n(u,d),n(d,v),n(d,C),n(C,U),n(d,R),h(e,q,t),me(P,e,t),h(e,D,t),h(e,j,t),h(e,W,t),h(e,M,t),n(M,K),n(M,X),n(M,Q),n(Q,A),n(A,Z),n(A,V),n(V,I),n(A,x),h(e,E,t),h(e,B,t),h(e,J,t),h(e,S,t),n(S,O);for(let c=0;c<w.length;c+=1)w[c].m(O,null);n(S,te),n(S,T);for(let c=0;c<p.length;c+=1)p[c].m(T,null);$=!0},p(e,[t]){var ie,ce;(!$||t&1)&&s!==(s=e[0].name+"")&&G(m,s),(!$||t&1)&&F!==(F=e[0].name+"")&&G(U,F);const c={};t&9&&(c.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -1,11 +1,11 @@
import{S as ze,i as Ue,s as je,M as Ve,e as a,w as k,b as p,c as ae,f as b,g as c,h as o,m as ne,x as re,N as qe,O as xe,k as Ie,P as Je,n as Ke,t as U,a as j,o as d,d as ie,Q as Qe,C as He,p as We,r as x,u as Ge}from"./index.2a48c906.js";import{S as Xe}from"./SdkTabs.9dbb2ca3.js";function Ee(r,l,s){const n=r.slice();return n[5]=l[s],n}function Fe(r,l,s){const n=r.slice();return n[5]=l[s],n}function Le(r,l){let s,n=l[5].code+"",m,_,i,f;function v(){return l[4](l[5])}return{key:r,first:null,c(){s=a("button"),m=k(n),_=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(g,w){c(g,s,w),o(s,m),o(s,_),i||(f=Ge(s,"click",v),i=!0)},p(g,w){l=g,w&4&&n!==(n=l[5].code+"")&&re(m,n),w&6&&x(s,"active",l[1]===l[5].code)},d(g){g&&d(s),i=!1,f()}}}function Ne(r,l){let s,n,m,_;return n=new Ve({props:{content:l[5].body}}),{key:r,first:null,c(){s=a("div"),ae(n.$$.fragment),m=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(i,f){c(i,s,f),ne(n,s,null),o(s,m),_=!0},p(i,f){l=i;const v={};f&4&&(v.content=l[5].body),n.$set(v),(!_||f&6)&&x(s,"active",l[1]===l[5].code)},i(i){_||(U(n.$$.fragment,i),_=!0)},o(i){j(n.$$.fragment,i),_=!1},d(i){i&&d(s),ie(n)}}}function Ye(r){var Ae,Be;let l,s,n=r[0].name+"",m,_,i,f,v,g,w,A,I,S,F,ce,L,B,de,J,N=r[0].name+"",K,ue,pe,V,Q,D,W,T,G,fe,X,C,Y,he,Z,be,h,me,R,_e,ke,ve,ee,ge,te,ye,Se,$e,oe,we,le,O,se,P,q,$=[],Te=new Map,Ce,H,y=[],Pe=new Map,M;g=new Xe({props:{js:`
import{S as ze,i as Ue,s as je,N as Ve,e as a,w as k,b as p,c as ae,f as b,g as c,h as o,m as ne,x as re,O as qe,P as xe,k as Ie,Q as Je,n as Ke,t as U,a as j,o as d,d as ie,R as Qe,C as He,p as We,r as x,u as Ge}from"./index.536fddcb.js";import{S as Xe}from"./SdkTabs.3571c26b.js";function Ee(r,l,s){const n=r.slice();return n[5]=l[s],n}function Fe(r,l,s){const n=r.slice();return n[5]=l[s],n}function Le(r,l){let s,n=l[5].code+"",m,_,i,f;function v(){return l[4](l[5])}return{key:r,first:null,c(){s=a("button"),m=k(n),_=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(g,w){c(g,s,w),o(s,m),o(s,_),i||(f=Ge(s,"click",v),i=!0)},p(g,w){l=g,w&4&&n!==(n=l[5].code+"")&&re(m,n),w&6&&x(s,"active",l[1]===l[5].code)},d(g){g&&d(s),i=!1,f()}}}function Ne(r,l){let s,n,m,_;return n=new Ve({props:{content:l[5].body}}),{key:r,first:null,c(){s=a("div"),ae(n.$$.fragment),m=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(i,f){c(i,s,f),ne(n,s,null),o(s,m),_=!0},p(i,f){l=i;const v={};f&4&&(v.content=l[5].body),n.$set(v),(!_||f&6)&&x(s,"active",l[1]===l[5].code)},i(i){_||(U(n.$$.fragment,i),_=!0)},o(i){j(n.$$.fragment,i),_=!1},d(i){i&&d(s),ie(n)}}}function Ye(r){var Be,Me;let l,s,n=r[0].name+"",m,_,i,f,v,g,w,B,I,S,F,ce,L,M,de,J,N=r[0].name+"",K,ue,pe,V,Q,D,W,T,G,fe,X,C,Y,he,Z,be,h,me,P,_e,ke,ve,ee,ge,te,ye,Se,$e,oe,we,le,O,se,R,q,$=[],Te=new Map,Ce,H,y=[],Re=new Map,A;g=new Xe({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${r[3]}');
...
const authData = await pb.collection('${(Ae=r[0])==null?void 0:Ae.name}').authRefresh();
const authData = await pb.collection('${(Be=r[0])==null?void 0:Be.name}').authRefresh();
// after the above you can also access the refreshed auth data from the authStore
console.log(pb.authStore.isValid);
@@ -18,24 +18,24 @@ import{S as ze,i as Ue,s as je,M as Ve,e as a,w as k,b as p,c as ae,f as b,g as
...
final authData = await pb.collection('${(Be=r[0])==null?void 0:Be.name}').authRefresh();
final authData = await pb.collection('${(Me=r[0])==null?void 0:Me.name}').authRefresh();
// after the above you can also access the refreshed auth data from the authStore
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
`}}),R=new Ve({props:{content:"?expand=relField1,relField2.subRelField"}});let z=r[2];const Re=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=Fe(r,z,e),u=Re(t);Te.set(u,$[e]=Le(u,t))}let E=r[2];const Me=e=>e[5].code;for(let e=0;e<E.length;e+=1){let t=Ee(r,E,e),u=Me(t);Pe.set(u,y[e]=Ne(u,t))}return{c(){l=a("h3"),s=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),f=a("div"),f.innerHTML=`<p>Returns a new auth response (token and record data) for an
`}}),P=new Ve({props:{content:"?expand=relField1,relField2.subRelField"}});let z=r[2];const Pe=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=Fe(r,z,e),u=Pe(t);Te.set(u,$[e]=Le(u,t))}let E=r[2];const Ae=e=>e[5].code;for(let e=0;e<E.length;e+=1){let t=Ee(r,E,e),u=Ae(t);Re.set(u,y[e]=Ne(u,t))}return{c(){l=a("h3"),s=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),f=a("div"),f.innerHTML=`<p>Returns a new auth response (token and record data) for an
<strong>already authenticated record</strong>.</p>
<p><em>This method is usually called by users on page/screen reload to ensure that the previously stored
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,v=p(),ae(g.$$.fragment),w=p(),A=a("h6"),A.textContent="API details",I=p(),S=a("div"),F=a("strong"),F.textContent="POST",ce=p(),L=a("div"),B=a("p"),de=k("/api/collections/"),J=a("strong"),K=k(N),ue=k("/auth-refresh"),pe=p(),V=a("p"),V.innerHTML="Requires record <code>Authorization:TOKEN</code> header",Q=p(),D=a("div"),D.textContent="Query parameters",W=p(),T=a("table"),G=a("thead"),G.innerHTML=`<tr><th>Param</th>
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,v=p(),ae(g.$$.fragment),w=p(),B=a("h6"),B.textContent="API details",I=p(),S=a("div"),F=a("strong"),F.textContent="POST",ce=p(),L=a("div"),M=a("p"),de=k("/api/collections/"),J=a("strong"),K=k(N),ue=k("/auth-refresh"),pe=p(),V=a("p"),V.innerHTML="Requires record <code>Authorization:TOKEN</code> header",Q=p(),D=a("div"),D.textContent="Query parameters",W=p(),T=a("table"),G=a("thead"),G.innerHTML=`<tr><th>Param</th>
<th>Type</th>
<th width="60%">Description</th></tr>`,fe=p(),X=a("tbody"),C=a("tr"),Y=a("td"),Y.textContent="expand",he=p(),Z=a("td"),Z.innerHTML='<span class="label">String</span>',be=p(),h=a("td"),me=k(`Auto expand record relations. Ex.:
`),ae(R.$$.fragment),_e=k(`
`),ae(P.$$.fragment),_e=k(`
Supports up to 6-levels depth nested relations expansion. `),ke=a("br"),ve=k(`
The expanded relations will be appended to the record under the
`),ee=a("code"),ee.textContent="expand",ge=k(" property (eg. "),te=a("code"),te.textContent='"expand": {"relField1": {...}, ...}',ye=k(`).
`),Se=a("br"),$e=k(`
Only the relations to which the request user has permissions to `),oe=a("strong"),oe.textContent="view",we=k(" will be expanded."),le=p(),O=a("div"),O.textContent="Responses",se=p(),P=a("div"),q=a("div");for(let e=0;e<$.length;e+=1)$[e].c();Ce=p(),H=a("div");for(let e=0;e<y.length;e+=1)y[e].c();b(l,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(A,"class","m-b-xs"),b(F,"class","label label-primary"),b(L,"class","content"),b(V,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(O,"class","section-title"),b(q,"class","tabs-header compact left"),b(H,"class","tabs-content"),b(P,"class","tabs")},m(e,t){c(e,l,t),o(l,s),o(l,m),o(l,_),c(e,i,t),c(e,f,t),c(e,v,t),ne(g,e,t),c(e,w,t),c(e,A,t),c(e,I,t),c(e,S,t),o(S,F),o(S,ce),o(S,L),o(L,B),o(B,de),o(B,J),o(J,K),o(B,ue),o(S,pe),o(S,V),c(e,Q,t),c(e,D,t),c(e,W,t),c(e,T,t),o(T,G),o(T,fe),o(T,X),o(X,C),o(C,Y),o(C,he),o(C,Z),o(C,be),o(C,h),o(h,me),ne(R,h,null),o(h,_e),o(h,ke),o(h,ve),o(h,ee),o(h,ge),o(h,te),o(h,ye),o(h,Se),o(h,$e),o(h,oe),o(h,we),c(e,le,t),c(e,O,t),c(e,se,t),c(e,P,t),o(P,q);for(let u=0;u<$.length;u+=1)$[u].m(q,null);o(P,Ce),o(P,H);for(let u=0;u<y.length;u+=1)y[u].m(H,null);M=!0},p(e,[t]){var De,Oe;(!M||t&1)&&n!==(n=e[0].name+"")&&re(m,n);const u={};t&9&&(u.js=`
Only the relations to which the request user has permissions to `),oe=a("strong"),oe.textContent="view",we=k(" will be expanded."),le=p(),O=a("div"),O.textContent="Responses",se=p(),R=a("div"),q=a("div");for(let e=0;e<$.length;e+=1)$[e].c();Ce=p(),H=a("div");for(let e=0;e<y.length;e+=1)y[e].c();b(l,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(F,"class","label label-primary"),b(L,"class","content"),b(V,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(O,"class","section-title"),b(q,"class","tabs-header compact left"),b(H,"class","tabs-content"),b(R,"class","tabs")},m(e,t){c(e,l,t),o(l,s),o(l,m),o(l,_),c(e,i,t),c(e,f,t),c(e,v,t),ne(g,e,t),c(e,w,t),c(e,B,t),c(e,I,t),c(e,S,t),o(S,F),o(S,ce),o(S,L),o(L,M),o(M,de),o(M,J),o(J,K),o(M,ue),o(S,pe),o(S,V),c(e,Q,t),c(e,D,t),c(e,W,t),c(e,T,t),o(T,G),o(T,fe),o(T,X),o(X,C),o(C,Y),o(C,he),o(C,Z),o(C,be),o(C,h),o(h,me),ne(P,h,null),o(h,_e),o(h,ke),o(h,ve),o(h,ee),o(h,ge),o(h,te),o(h,ye),o(h,Se),o(h,$e),o(h,oe),o(h,we),c(e,le,t),c(e,O,t),c(e,se,t),c(e,R,t),o(R,q);for(let u=0;u<$.length;u+=1)$[u].m(q,null);o(R,Ce),o(R,H);for(let u=0;u<y.length;u+=1)y[u].m(H,null);A=!0},p(e,[t]){var De,Oe;(!A||t&1)&&n!==(n=e[0].name+"")&&re(m,n);const u={};t&9&&(u.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -61,7 +61,7 @@ import{S as ze,i as Ue,s as je,M as Ve,e as a,w as k,b as p,c as ae,f as b,g as
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
`),g.$set(u),(!M||t&1)&&N!==(N=e[0].name+"")&&re(K,N),t&6&&(z=e[2],$=qe($,t,Re,1,e,z,Te,q,xe,Le,null,Fe)),t&6&&(E=e[2],Ie(),y=qe(y,t,Me,1,e,E,Pe,H,Je,Ne,null,Ee),Ke())},i(e){if(!M){U(g.$$.fragment,e),U(R.$$.fragment,e);for(let t=0;t<E.length;t+=1)U(y[t]);M=!0}},o(e){j(g.$$.fragment,e),j(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)j(y[t]);M=!1},d(e){e&&d(l),e&&d(i),e&&d(f),e&&d(v),ie(g,e),e&&d(w),e&&d(A),e&&d(I),e&&d(S),e&&d(Q),e&&d(D),e&&d(W),e&&d(T),ie(R),e&&d(le),e&&d(O),e&&d(se),e&&d(P);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<y.length;t+=1)y[t].d()}}}function Ze(r,l,s){let n,{collection:m=new Qe}=l,_=200,i=[];const f=v=>s(1,_=v.code);return r.$$set=v=>{"collection"in v&&s(0,m=v.collection)},r.$$.update=()=>{r.$$.dirty&1&&s(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:He.dummyCollectionRecord(m)},null,2)},{code:401,body:`
`),g.$set(u),(!A||t&1)&&N!==(N=e[0].name+"")&&re(K,N),t&6&&(z=e[2],$=qe($,t,Pe,1,e,z,Te,q,xe,Le,null,Fe)),t&6&&(E=e[2],Ie(),y=qe(y,t,Ae,1,e,E,Re,H,Je,Ne,null,Ee),Ke())},i(e){if(!A){U(g.$$.fragment,e),U(P.$$.fragment,e);for(let t=0;t<E.length;t+=1)U(y[t]);A=!0}},o(e){j(g.$$.fragment,e),j(P.$$.fragment,e);for(let t=0;t<y.length;t+=1)j(y[t]);A=!1},d(e){e&&d(l),e&&d(i),e&&d(f),e&&d(v),ie(g,e),e&&d(w),e&&d(B),e&&d(I),e&&d(S),e&&d(Q),e&&d(D),e&&d(W),e&&d(T),ie(P),e&&d(le),e&&d(O),e&&d(se),e&&d(R);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<y.length;t+=1)y[t].d()}}}function Ze(r,l,s){let n,{collection:m=new Qe}=l,_=200,i=[];const f=v=>s(1,_=v.code);return r.$$set=v=>{"collection"in v&&s(0,m=v.collection)},r.$$.update=()=>{r.$$.dirty&1&&s(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:He.dummyCollectionRecord(m)},null,2)},{code:401,body:`
{
"code": 401,
"message": "The request requires valid record authorization token to be set.",
@@ -1,4 +1,4 @@
import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as r,h as a,m as ce,x as de,N as Ve,O as Ne,k as Qe,P as ze,n as Ke,t as j,a as H,o as c,d as ue,Q as Ye,C as Be,p as Ge,r as J,u as Xe}from"./index.2a48c906.js";import{S as Ze}from"./SdkTabs.9dbb2ca3.js";function Fe(i,l,o){const n=i.slice();return n[5]=l[o],n}function Le(i,l,o){const n=i.slice();return n[5]=l[o],n}function Me(i,l){let o,n=l[5].code+"",m,_,d,b;function g(){return l[4](l[5])}return{key:i,first:null,c(){o=s("button"),m=v(n),_=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(k,y){r(k,o,y),a(o,m),a(o,_),d||(b=Xe(o,"click",g),d=!0)},p(k,y){l=k,y&4&&n!==(n=l[5].code+"")&&de(m,n),y&6&&J(o,"active",l[1]===l[5].code)},d(k){k&&c(o),d=!1,b()}}}function xe(i,l){let o,n,m,_;return n=new We({props:{content:l[5].body}}),{key:i,first:null,c(){o=s("div"),re(n.$$.fragment),m=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(d,b){r(d,o,b),ce(n,o,null),a(o,m),_=!0},p(d,b){l=d;const g={};b&4&&(g.content=l[5].body),n.$set(g),(!_||b&6)&&J(o,"active",l[1]===l[5].code)},i(d){_||(j(n.$$.fragment,d),_=!0)},o(d){H(n.$$.fragment,d),_=!1},d(d){d&&c(o),ue(n)}}}function et(i){var qe,Ie;let l,o,n=i[0].name+"",m,_,d,b,g,k,y,C,N,O,L,pe,M,D,he,Q,x=i[0].name+"",z,be,K,q,Y,I,G,P,X,R,Z,fe,ee,$,te,me,ae,_e,f,ve,E,ge,ke,we,le,Se,oe,ye,Oe,Re,se,$e,ne,U,ie,A,V,S=[],Ae=new Map,Ee,B,w=[],Te=new Map,T;k=new Ze({props:{js:`
import{S as je,i as He,s as Je,N as We,e as s,w as v,b as p,c as re,f as h,g as r,h as a,m as ce,x as de,O as Ve,P as Ne,k as Qe,Q as ze,n as Ke,t as j,a as H,o as c,d as ue,R as Ye,C as Be,p as Ge,r as J,u as Xe}from"./index.536fddcb.js";import{S as Ze}from"./SdkTabs.3571c26b.js";function Fe(i,l,o){const n=i.slice();return n[5]=l[o],n}function Le(i,l,o){const n=i.slice();return n[5]=l[o],n}function xe(i,l){let o,n=l[5].code+"",m,_,d,b;function g(){return l[4](l[5])}return{key:i,first:null,c(){o=s("button"),m=v(n),_=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(k,R){r(k,o,R),a(o,m),a(o,_),d||(b=Xe(o,"click",g),d=!0)},p(k,R){l=k,R&4&&n!==(n=l[5].code+"")&&de(m,n),R&6&&J(o,"active",l[1]===l[5].code)},d(k){k&&c(o),d=!1,b()}}}function Me(i,l){let o,n,m,_;return n=new We({props:{content:l[5].body}}),{key:i,first:null,c(){o=s("div"),re(n.$$.fragment),m=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(d,b){r(d,o,b),ce(n,o,null),a(o,m),_=!0},p(d,b){l=d;const g={};b&4&&(g.content=l[5].body),n.$set(g),(!_||b&6)&&J(o,"active",l[1]===l[5].code)},i(d){_||(j(n.$$.fragment,d),_=!0)},o(d){H(n.$$.fragment,d),_=!1},d(d){d&&c(o),ue(n)}}}function et(i){var qe,Ie;let l,o,n=i[0].name+"",m,_,d,b,g,k,R,C,N,y,L,pe,x,D,he,Q,M=i[0].name+"",z,be,K,q,Y,I,G,P,X,O,Z,fe,ee,$,te,me,ae,_e,f,ve,E,ge,ke,we,le,Se,oe,Re,ye,Oe,se,$e,ne,U,ie,A,V,S=[],Ae=new Map,Ee,B,w=[],Te=new Map,T;k=new Ze({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${i[3]}');
@@ -48,11 +48,11 @@ import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as
// "logout" the last authenticated account
pb.authStore.clear();
`}}),E=new We({props:{content:"?expand=relField1,relField2.subRelField"}});let W=i[2];const Ce=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=Le(i,W,e),u=Ce(t);Ae.set(u,S[e]=Me(u,t))}let F=i[2];const De=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=Fe(i,F,e),u=De(t);Te.set(u,w[e]=xe(u,t))}return{c(){l=s("h3"),o=v("Auth with OAuth2 ("),m=v(n),_=v(")"),d=p(),b=s("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p>
`}}),E=new We({props:{content:"?expand=relField1,relField2.subRelField"}});let W=i[2];const Ce=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=Le(i,W,e),u=Ce(t);Ae.set(u,S[e]=xe(u,t))}let F=i[2];const De=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=Fe(i,F,e),u=De(t);Te.set(u,w[e]=Me(u,t))}return{c(){l=s("h3"),o=v("Auth with OAuth2 ("),m=v(n),_=v(")"),d=p(),b=s("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p>
<p>This action usually should be called right after the provider login page redirect.</p>
<p>You could also check the
<a href="https://pocketbase.io/docs/authentication/#web-oauth2-integration" target="_blank" rel="noopener noreferrer">OAuth2 web integration example
</a>.</p>`,g=p(),re(k.$$.fragment),y=p(),C=s("h6"),C.textContent="API details",N=p(),O=s("div"),L=s("strong"),L.textContent="POST",pe=p(),M=s("div"),D=s("p"),he=v("/api/collections/"),Q=s("strong"),z=v(x),be=v("/auth-with-oauth2"),K=p(),q=s("div"),q.textContent="Body Parameters",Y=p(),I=s("table"),I.innerHTML=`<thead><tr><th>Param</th>
</a>.</p>`,g=p(),re(k.$$.fragment),R=p(),C=s("h6"),C.textContent="API details",N=p(),y=s("div"),L=s("strong"),L.textContent="POST",pe=p(),x=s("div"),D=s("p"),he=v("/api/collections/"),Q=s("strong"),z=v(M),be=v("/auth-with-oauth2"),K=p(),q=s("div"),q.textContent="Body Parameters",Y=p(),I=s("table"),I.innerHTML=`<thead><tr><th>Param</th>
<th>Type</th>
<th width="50%">Description</th></tr></thead>
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
@@ -79,15 +79,15 @@ import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as
regular <strong>create</strong> action.
<br/>
<em>The data can only be in <code>json</code>, aka. <code>multipart/form-data</code> and files
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,G=p(),P=s("div"),P.textContent="Query parameters",X=p(),R=s("table"),Z=s("thead"),Z.innerHTML=`<tr><th>Param</th>
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,G=p(),P=s("div"),P.textContent="Query parameters",X=p(),O=s("table"),Z=s("thead"),Z.innerHTML=`<tr><th>Param</th>
<th>Type</th>
<th width="60%">Description</th></tr>`,fe=p(),ee=s("tbody"),$=s("tr"),te=s("td"),te.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ve=v(`Auto expand record relations. Ex.:
`),re(E.$$.fragment),ge=v(`
Supports up to 6-levels depth nested relations expansion. `),ke=s("br"),we=v(`
The expanded relations will be appended to the record under the
`),le=s("code"),le.textContent="expand",Se=v(" property (eg. "),oe=s("code"),oe.textContent='"expand": {"relField1": {...}, ...}',ye=v(`).
`),Oe=s("br"),Re=v(`
Only the relations to which the request user has permissions to `),se=s("strong"),se.textContent="view",$e=v(" will be expanded."),ne=p(),U=s("div"),U.textContent="Responses",ie=p(),A=s("div"),V=s("div");for(let e=0;e<S.length;e+=1)S[e].c();Ee=p(),B=s("div");for(let e=0;e<w.length;e+=1)w[e].c();h(l,"class","m-b-sm"),h(b,"class","content txt-lg m-b-sm"),h(C,"class","m-b-xs"),h(L,"class","label label-primary"),h(M,"class","content"),h(O,"class","alert alert-success"),h(q,"class","section-title"),h(I,"class","table-compact table-border m-b-base"),h(P,"class","section-title"),h(R,"class","table-compact table-border m-b-base"),h(U,"class","section-title"),h(V,"class","tabs-header compact left"),h(B,"class","tabs-content"),h(A,"class","tabs")},m(e,t){r(e,l,t),a(l,o),a(l,m),a(l,_),r(e,d,t),r(e,b,t),r(e,g,t),ce(k,e,t),r(e,y,t),r(e,C,t),r(e,N,t),r(e,O,t),a(O,L),a(O,pe),a(O,M),a(M,D),a(D,he),a(D,Q),a(Q,z),a(D,be),r(e,K,t),r(e,q,t),r(e,Y,t),r(e,I,t),r(e,G,t),r(e,P,t),r(e,X,t),r(e,R,t),a(R,Z),a(R,fe),a(R,ee),a(ee,$),a($,te),a($,me),a($,ae),a($,_e),a($,f),a(f,ve),ce(E,f,null),a(f,ge),a(f,ke),a(f,we),a(f,le),a(f,Se),a(f,oe),a(f,ye),a(f,Oe),a(f,Re),a(f,se),a(f,$e),r(e,ne,t),r(e,U,t),r(e,ie,t),r(e,A,t),a(A,V);for(let u=0;u<S.length;u+=1)S[u].m(V,null);a(A,Ee),a(A,B);for(let u=0;u<w.length;u+=1)w[u].m(B,null);T=!0},p(e,[t]){var Pe,Ue;(!T||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const u={};t&9&&(u.js=`
`),le=s("code"),le.textContent="expand",Se=v(" property (eg. "),oe=s("code"),oe.textContent='"expand": {"relField1": {...}, ...}',Re=v(`).
`),ye=s("br"),Oe=v(`
Only the relations to which the request user has permissions to `),se=s("strong"),se.textContent="view",$e=v(" will be expanded."),ne=p(),U=s("div"),U.textContent="Responses",ie=p(),A=s("div"),V=s("div");for(let e=0;e<S.length;e+=1)S[e].c();Ee=p(),B=s("div");for(let e=0;e<w.length;e+=1)w[e].c();h(l,"class","m-b-sm"),h(b,"class","content txt-lg m-b-sm"),h(C,"class","m-b-xs"),h(L,"class","label label-primary"),h(x,"class","content"),h(y,"class","alert alert-success"),h(q,"class","section-title"),h(I,"class","table-compact table-border m-b-base"),h(P,"class","section-title"),h(O,"class","table-compact table-border m-b-base"),h(U,"class","section-title"),h(V,"class","tabs-header compact left"),h(B,"class","tabs-content"),h(A,"class","tabs")},m(e,t){r(e,l,t),a(l,o),a(l,m),a(l,_),r(e,d,t),r(e,b,t),r(e,g,t),ce(k,e,t),r(e,R,t),r(e,C,t),r(e,N,t),r(e,y,t),a(y,L),a(y,pe),a(y,x),a(x,D),a(D,he),a(D,Q),a(Q,z),a(D,be),r(e,K,t),r(e,q,t),r(e,Y,t),r(e,I,t),r(e,G,t),r(e,P,t),r(e,X,t),r(e,O,t),a(O,Z),a(O,fe),a(O,ee),a(ee,$),a($,te),a($,me),a($,ae),a($,_e),a($,f),a(f,ve),ce(E,f,null),a(f,ge),a(f,ke),a(f,we),a(f,le),a(f,Se),a(f,oe),a(f,Re),a(f,ye),a(f,Oe),a(f,se),a(f,$e),r(e,ne,t),r(e,U,t),r(e,ie,t),r(e,A,t),a(A,V);for(let u=0;u<S.length;u+=1)S[u].m(V,null);a(A,Ee),a(A,B);for(let u=0;u<w.length;u+=1)w[u].m(B,null);T=!0},p(e,[t]){var Pe,Ue;(!T||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const u={};t&9&&(u.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -137,7 +137,7 @@ import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as
// "logout" the last authenticated account
pb.authStore.clear();
`),k.$set(u),(!T||t&1)&&x!==(x=e[0].name+"")&&de(z,x),t&6&&(W=e[2],S=Ve(S,t,Ce,1,e,W,Ae,V,Ne,Me,null,Le)),t&6&&(F=e[2],Qe(),w=Ve(w,t,De,1,e,F,Te,B,ze,xe,null,Fe),Ke())},i(e){if(!T){j(k.$$.fragment,e),j(E.$$.fragment,e);for(let t=0;t<F.length;t+=1)j(w[t]);T=!0}},o(e){H(k.$$.fragment,e),H(E.$$.fragment,e);for(let t=0;t<w.length;t+=1)H(w[t]);T=!1},d(e){e&&c(l),e&&c(d),e&&c(b),e&&c(g),ue(k,e),e&&c(y),e&&c(C),e&&c(N),e&&c(O),e&&c(K),e&&c(q),e&&c(Y),e&&c(I),e&&c(G),e&&c(P),e&&c(X),e&&c(R),ue(E),e&&c(ne),e&&c(U),e&&c(ie),e&&c(A);for(let t=0;t<S.length;t+=1)S[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function tt(i,l,o){let n,{collection:m=new Ye}=l,_=200,d=[];const b=g=>o(1,_=g.code);return i.$$set=g=>{"collection"in g&&o(0,m=g.collection)},i.$$.update=()=>{i.$$.dirty&1&&o(2,d=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Be.dummyCollectionRecord(m),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png"}},null,2)},{code:400,body:`
`),k.$set(u),(!T||t&1)&&M!==(M=e[0].name+"")&&de(z,M),t&6&&(W=e[2],S=Ve(S,t,Ce,1,e,W,Ae,V,Ne,xe,null,Le)),t&6&&(F=e[2],Qe(),w=Ve(w,t,De,1,e,F,Te,B,ze,Me,null,Fe),Ke())},i(e){if(!T){j(k.$$.fragment,e),j(E.$$.fragment,e);for(let t=0;t<F.length;t+=1)j(w[t]);T=!0}},o(e){H(k.$$.fragment,e),H(E.$$.fragment,e);for(let t=0;t<w.length;t+=1)H(w[t]);T=!1},d(e){e&&c(l),e&&c(d),e&&c(b),e&&c(g),ue(k,e),e&&c(R),e&&c(C),e&&c(N),e&&c(y),e&&c(K),e&&c(q),e&&c(Y),e&&c(I),e&&c(G),e&&c(P),e&&c(X),e&&c(O),ue(E),e&&c(ne),e&&c(U),e&&c(ie),e&&c(A);for(let t=0;t<S.length;t+=1)S[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function tt(i,l,o){let n,{collection:m=new Ye}=l,_=200,d=[];const b=g=>o(1,_=g.code);return i.$$set=g=>{"collection"in g&&o(0,m=g.collection)},i.$$.update=()=>{i.$$.dirty&1&&o(2,d=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Be.dummyCollectionRecord(m),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png"}},null,2)},{code:400,body:`
{
"code": 400,
"message": "An error occurred while submitting the form.",
@@ -1,4 +1,4 @@
import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as r,h as o,m as At,x as Tt,N as ce,O as ye,k as ge,P as Pe,n as $e,t as tt,a as et,o as c,d as Mt,Q as Re,C as de,p as Ce,r as lt,u as Oe}from"./index.2a48c906.js";import{S as Ae}from"./SdkTabs.9dbb2ca3.js";function ue(n,e,l){const i=n.slice();return i[8]=e[l],i}function fe(n,e,l){const i=n.slice();return i[8]=e[l],i}function Te(n){let e;return{c(){e=f("email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Me(n){let e;return{c(){e=f("username")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Ue(n){let e;return{c(){e=f("username/email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function pe(n){let e;return{c(){e=s("strong"),e.textContent="username"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function be(n){let e;return{c(){e=f("or")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function me(n){let e;return{c(){e=s("strong"),e.textContent="email"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function he(n,e){let l,i=e[8].code+"",S,m,p,d;function _(){return e[7](e[8])}return{key:n,first:null,c(){l=s("button"),S=f(i),m=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m(R,C){r(R,l,C),o(l,S),o(l,m),p||(d=Oe(l,"click",_),p=!0)},p(R,C){e=R,C&16&&i!==(i=e[8].code+"")&&Tt(S,i),C&24&&lt(l,"active",e[3]===e[8].code)},d(R){R&&c(l),p=!1,d()}}}function _e(n,e){let l,i,S,m;return i=new ke({props:{content:e[8].body}}),{key:n,first:null,c(){l=s("div"),Ot(i.$$.fragment),S=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m(p,d){r(p,l,d),At(i,l,null),o(l,S),m=!0},p(p,d){e=p;const _={};d&16&&(_.content=e[8].body),i.$set(_),(!m||d&24)&&lt(l,"active",e[3]===e[8].code)},i(p){m||(tt(i.$$.fragment,p),m=!0)},o(p){et(i.$$.fragment,p),m=!1},d(p){p&&c(l),Mt(i)}}}function De(n){var se,ne;let e,l,i=n[0].name+"",S,m,p,d,_,R,C,O,B,Ut,ot,T,at,F,st,M,G,Dt,X,I,Et,nt,Z=n[0].name+"",it,Wt,rt,N,ct,U,dt,Lt,V,D,ut,Bt,ft,Ht,g,Yt,pt,bt,mt,qt,ht,_t,j,kt,E,St,Ft,vt,W,wt,It,yt,Nt,k,Vt,H,jt,Jt,Qt,gt,Kt,Pt,zt,Gt,Xt,$t,Zt,Rt,J,Ct,L,Q,A=[],xt=new Map,te,K,P=[],ee=new Map,Y;function le(t,a){if(t[1]&&t[2])return Ue;if(t[1])return Me;if(t[2])return Te}let q=le(n),$=q&&q(n);T=new Ae({props:{js:`
import{S as Se,i as ve,s as we,N as ke,e as s,w as f,b as u,c as Ot,f as h,g as r,h as o,m as At,x as Tt,O as ce,P as ye,k as ge,Q as Pe,n as Re,t as tt,a as et,o as c,d as Ut,R as $e,C as de,p as Ce,r as lt,u as Oe}from"./index.536fddcb.js";import{S as Ae}from"./SdkTabs.3571c26b.js";function ue(n,e,l){const i=n.slice();return i[8]=e[l],i}function fe(n,e,l){const i=n.slice();return i[8]=e[l],i}function Te(n){let e;return{c(){e=f("email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Ue(n){let e;return{c(){e=f("username")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Me(n){let e;return{c(){e=f("username/email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function pe(n){let e;return{c(){e=s("strong"),e.textContent="username"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function be(n){let e;return{c(){e=f("or")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function me(n){let e;return{c(){e=s("strong"),e.textContent="email"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function he(n,e){let l,i=e[8].code+"",S,m,p,d;function _(){return e[7](e[8])}return{key:n,first:null,c(){l=s("button"),S=f(i),m=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m($,C){r($,l,C),o(l,S),o(l,m),p||(d=Oe(l,"click",_),p=!0)},p($,C){e=$,C&16&&i!==(i=e[8].code+"")&&Tt(S,i),C&24&&lt(l,"active",e[3]===e[8].code)},d($){$&&c(l),p=!1,d()}}}function _e(n,e){let l,i,S,m;return i=new ke({props:{content:e[8].body}}),{key:n,first:null,c(){l=s("div"),Ot(i.$$.fragment),S=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m(p,d){r(p,l,d),At(i,l,null),o(l,S),m=!0},p(p,d){e=p;const _={};d&16&&(_.content=e[8].body),i.$set(_),(!m||d&24)&&lt(l,"active",e[3]===e[8].code)},i(p){m||(tt(i.$$.fragment,p),m=!0)},o(p){et(i.$$.fragment,p),m=!1},d(p){p&&c(l),Ut(i)}}}function De(n){var se,ne;let e,l,i=n[0].name+"",S,m,p,d,_,$,C,O,B,Mt,ot,T,at,F,st,U,G,Dt,X,I,Et,nt,Z=n[0].name+"",it,Wt,rt,N,ct,M,dt,Lt,V,D,ut,Bt,ft,Ht,g,Yt,pt,bt,mt,qt,ht,_t,j,kt,E,St,Ft,vt,W,wt,It,yt,Nt,k,Vt,H,jt,Jt,Qt,gt,Kt,Pt,zt,Gt,Xt,Rt,Zt,$t,J,Ct,L,Q,A=[],xt=new Map,te,K,P=[],ee=new Map,Y;function le(t,a){if(t[1]&&t[2])return Me;if(t[1])return Ue;if(t[2])return Te}let q=le(n),R=q&&q(n);T=new Ae({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${n[6]}');
@@ -36,9 +36,9 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
// "logout" the last authenticated account
pb.authStore.clear();
`}});let v=n[1]&&pe(),w=n[1]&&n[2]&&be(),y=n[2]&&me();H=new ke({props:{content:"?expand=relField1,relField2.subRelField"}});let x=n[4];const oe=t=>t[8].code;for(let t=0;t<x.length;t+=1){let a=fe(n,x,t),b=oe(a);xt.set(b,A[t]=he(b,a))}let z=n[4];const ae=t=>t[8].code;for(let t=0;t<z.length;t+=1){let a=ue(n,z,t),b=ae(a);ee.set(b,P[t]=_e(b,a))}return{c(){e=s("h3"),l=f("Auth with password ("),S=f(i),m=f(")"),p=u(),d=s("div"),_=s("p"),R=f(`Returns new auth token and account data by a combination of
`),C=s("strong"),$&&$.c(),O=f(`
and `),B=s("strong"),B.textContent="password",Ut=f("."),ot=u(),Ot(T.$$.fragment),at=u(),F=s("h6"),F.textContent="API details",st=u(),M=s("div"),G=s("strong"),G.textContent="POST",Dt=u(),X=s("div"),I=s("p"),Et=f("/api/collections/"),nt=s("strong"),it=f(Z),Wt=f("/auth-with-password"),rt=u(),N=s("div"),N.textContent="Body Parameters",ct=u(),U=s("table"),dt=s("thead"),dt.innerHTML=`<tr><th>Param</th>
`}});let v=n[1]&&pe(),w=n[1]&&n[2]&&be(),y=n[2]&&me();H=new ke({props:{content:"?expand=relField1,relField2.subRelField"}});let x=n[4];const oe=t=>t[8].code;for(let t=0;t<x.length;t+=1){let a=fe(n,x,t),b=oe(a);xt.set(b,A[t]=he(b,a))}let z=n[4];const ae=t=>t[8].code;for(let t=0;t<z.length;t+=1){let a=ue(n,z,t),b=ae(a);ee.set(b,P[t]=_e(b,a))}return{c(){e=s("h3"),l=f("Auth with password ("),S=f(i),m=f(")"),p=u(),d=s("div"),_=s("p"),$=f(`Returns new auth token and account data by a combination of
`),C=s("strong"),R&&R.c(),O=f(`
and `),B=s("strong"),B.textContent="password",Mt=f("."),ot=u(),Ot(T.$$.fragment),at=u(),F=s("h6"),F.textContent="API details",st=u(),U=s("div"),G=s("strong"),G.textContent="POST",Dt=u(),X=s("div"),I=s("p"),Et=f("/api/collections/"),nt=s("strong"),it=f(Z),Wt=f("/auth-with-password"),rt=u(),N=s("div"),N.textContent="Body Parameters",ct=u(),M=s("table"),dt=s("thead"),dt.innerHTML=`<tr><th>Param</th>
<th>Type</th>
<th width="50%">Description</th></tr>`,Lt=u(),V=s("tbody"),D=s("tr"),ut=s("td"),ut.innerHTML=`<div class="inline-flex"><span class="label label-success">Required</span>
<span>identity</span></div>`,Bt=u(),ft=s("td"),ft.innerHTML='<span class="label">String</span>',Ht=u(),g=s("td"),Yt=f(`The
@@ -54,7 +54,7 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
The expanded relations will be appended to the record under the
`),gt=s("code"),gt.textContent="expand",Kt=f(" property (eg. "),Pt=s("code"),Pt.textContent='"expand": {"relField1": {...}, ...}',zt=f(`).
`),Gt=s("br"),Xt=f(`
Only the relations to which the request user has permissions to `),$t=s("strong"),$t.textContent="view",Zt=f(" will be expanded."),Rt=u(),J=s("div"),J.textContent="Responses",Ct=u(),L=s("div"),Q=s("div");for(let t=0;t<A.length;t+=1)A[t].c();te=u(),K=s("div");for(let t=0;t<P.length;t+=1)P[t].c();h(e,"class","m-b-sm"),h(d,"class","content txt-lg m-b-sm"),h(F,"class","m-b-xs"),h(G,"class","label label-primary"),h(X,"class","content"),h(M,"class","alert alert-success"),h(N,"class","section-title"),h(U,"class","table-compact table-border m-b-base"),h(j,"class","section-title"),h(E,"class","table-compact table-border m-b-base"),h(J,"class","section-title"),h(Q,"class","tabs-header compact left"),h(K,"class","tabs-content"),h(L,"class","tabs")},m(t,a){r(t,e,a),o(e,l),o(e,S),o(e,m),r(t,p,a),r(t,d,a),o(d,_),o(_,R),o(_,C),$&&$.m(C,null),o(_,O),o(_,B),o(_,Ut),r(t,ot,a),At(T,t,a),r(t,at,a),r(t,F,a),r(t,st,a),r(t,M,a),o(M,G),o(M,Dt),o(M,X),o(X,I),o(I,Et),o(I,nt),o(nt,it),o(I,Wt),r(t,rt,a),r(t,N,a),r(t,ct,a),r(t,U,a),o(U,dt),o(U,Lt),o(U,V),o(V,D),o(D,ut),o(D,Bt),o(D,ft),o(D,Ht),o(D,g),o(g,Yt),v&&v.m(g,null),o(g,pt),w&&w.m(g,null),o(g,bt),y&&y.m(g,null),o(g,mt),o(V,qt),o(V,ht),r(t,_t,a),r(t,j,a),r(t,kt,a),r(t,E,a),o(E,St),o(E,Ft),o(E,vt),o(vt,W),o(W,wt),o(W,It),o(W,yt),o(W,Nt),o(W,k),o(k,Vt),At(H,k,null),o(k,jt),o(k,Jt),o(k,Qt),o(k,gt),o(k,Kt),o(k,Pt),o(k,zt),o(k,Gt),o(k,Xt),o(k,$t),o(k,Zt),r(t,Rt,a),r(t,J,a),r(t,Ct,a),r(t,L,a),o(L,Q);for(let b=0;b<A.length;b+=1)A[b].m(Q,null);o(L,te),o(L,K);for(let b=0;b<P.length;b+=1)P[b].m(K,null);Y=!0},p(t,[a]){var ie,re;(!Y||a&1)&&i!==(i=t[0].name+"")&&Tt(S,i),q!==(q=le(t))&&($&&$.d(1),$=q&&q(t),$&&($.c(),$.m(C,null)));const b={};a&97&&(b.js=`
Only the relations to which the request user has permissions to `),Rt=s("strong"),Rt.textContent="view",Zt=f(" will be expanded."),$t=u(),J=s("div"),J.textContent="Responses",Ct=u(),L=s("div"),Q=s("div");for(let t=0;t<A.length;t+=1)A[t].c();te=u(),K=s("div");for(let t=0;t<P.length;t+=1)P[t].c();h(e,"class","m-b-sm"),h(d,"class","content txt-lg m-b-sm"),h(F,"class","m-b-xs"),h(G,"class","label label-primary"),h(X,"class","content"),h(U,"class","alert alert-success"),h(N,"class","section-title"),h(M,"class","table-compact table-border m-b-base"),h(j,"class","section-title"),h(E,"class","table-compact table-border m-b-base"),h(J,"class","section-title"),h(Q,"class","tabs-header compact left"),h(K,"class","tabs-content"),h(L,"class","tabs")},m(t,a){r(t,e,a),o(e,l),o(e,S),o(e,m),r(t,p,a),r(t,d,a),o(d,_),o(_,$),o(_,C),R&&R.m(C,null),o(_,O),o(_,B),o(_,Mt),r(t,ot,a),At(T,t,a),r(t,at,a),r(t,F,a),r(t,st,a),r(t,U,a),o(U,G),o(U,Dt),o(U,X),o(X,I),o(I,Et),o(I,nt),o(nt,it),o(I,Wt),r(t,rt,a),r(t,N,a),r(t,ct,a),r(t,M,a),o(M,dt),o(M,Lt),o(M,V),o(V,D),o(D,ut),o(D,Bt),o(D,ft),o(D,Ht),o(D,g),o(g,Yt),v&&v.m(g,null),o(g,pt),w&&w.m(g,null),o(g,bt),y&&y.m(g,null),o(g,mt),o(V,qt),o(V,ht),r(t,_t,a),r(t,j,a),r(t,kt,a),r(t,E,a),o(E,St),o(E,Ft),o(E,vt),o(vt,W),o(W,wt),o(W,It),o(W,yt),o(W,Nt),o(W,k),o(k,Vt),At(H,k,null),o(k,jt),o(k,Jt),o(k,Qt),o(k,gt),o(k,Kt),o(k,Pt),o(k,zt),o(k,Gt),o(k,Xt),o(k,Rt),o(k,Zt),r(t,$t,a),r(t,J,a),r(t,Ct,a),r(t,L,a),o(L,Q);for(let b=0;b<A.length;b+=1)A[b].m(Q,null);o(L,te),o(L,K);for(let b=0;b<P.length;b+=1)P[b].m(K,null);Y=!0},p(t,[a]){var ie,re;(!Y||a&1)&&i!==(i=t[0].name+"")&&Tt(S,i),q!==(q=le(t))&&(R&&R.d(1),R=q&&q(t),R&&(R.c(),R.m(C,null)));const b={};a&97&&(b.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${t[6]}');
@@ -92,7 +92,7 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
// "logout" the last authenticated account
pb.authStore.clear();
`),T.$set(b),(!Y||a&1)&&Z!==(Z=t[0].name+"")&&Tt(it,Z),t[1]?v||(v=pe(),v.c(),v.m(g,pt)):v&&(v.d(1),v=null),t[1]&&t[2]?w||(w=be(),w.c(),w.m(g,bt)):w&&(w.d(1),w=null),t[2]?y||(y=me(),y.c(),y.m(g,mt)):y&&(y.d(1),y=null),a&24&&(x=t[4],A=ce(A,a,oe,1,t,x,xt,Q,ye,he,null,fe)),a&24&&(z=t[4],ge(),P=ce(P,a,ae,1,t,z,ee,K,Pe,_e,null,ue),$e())},i(t){if(!Y){tt(T.$$.fragment,t),tt(H.$$.fragment,t);for(let a=0;a<z.length;a+=1)tt(P[a]);Y=!0}},o(t){et(T.$$.fragment,t),et(H.$$.fragment,t);for(let a=0;a<P.length;a+=1)et(P[a]);Y=!1},d(t){t&&c(e),t&&c(p),t&&c(d),$&&$.d(),t&&c(ot),Mt(T,t),t&&c(at),t&&c(F),t&&c(st),t&&c(M),t&&c(rt),t&&c(N),t&&c(ct),t&&c(U),v&&v.d(),w&&w.d(),y&&y.d(),t&&c(_t),t&&c(j),t&&c(kt),t&&c(E),Mt(H),t&&c(Rt),t&&c(J),t&&c(Ct),t&&c(L);for(let a=0;a<A.length;a+=1)A[a].d();for(let a=0;a<P.length;a+=1)P[a].d()}}}function Ee(n,e,l){let i,S,m,p,{collection:d=new Re}=e,_=200,R=[];const C=O=>l(3,_=O.code);return n.$$set=O=>{"collection"in O&&l(0,d=O.collection)},n.$$.update=()=>{var O,B;n.$$.dirty&1&&l(2,S=(O=d==null?void 0:d.options)==null?void 0:O.allowEmailAuth),n.$$.dirty&1&&l(1,m=(B=d==null?void 0:d.options)==null?void 0:B.allowUsernameAuth),n.$$.dirty&6&&l(5,p=m&&S?"YOUR_USERNAME_OR_EMAIL":m?"YOUR_USERNAME":"YOUR_EMAIL"),n.$$.dirty&1&&l(4,R=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:de.dummyCollectionRecord(d)},null,2)},{code:400,body:`
`),T.$set(b),(!Y||a&1)&&Z!==(Z=t[0].name+"")&&Tt(it,Z),t[1]?v||(v=pe(),v.c(),v.m(g,pt)):v&&(v.d(1),v=null),t[1]&&t[2]?w||(w=be(),w.c(),w.m(g,bt)):w&&(w.d(1),w=null),t[2]?y||(y=me(),y.c(),y.m(g,mt)):y&&(y.d(1),y=null),a&24&&(x=t[4],A=ce(A,a,oe,1,t,x,xt,Q,ye,he,null,fe)),a&24&&(z=t[4],ge(),P=ce(P,a,ae,1,t,z,ee,K,Pe,_e,null,ue),Re())},i(t){if(!Y){tt(T.$$.fragment,t),tt(H.$$.fragment,t);for(let a=0;a<z.length;a+=1)tt(P[a]);Y=!0}},o(t){et(T.$$.fragment,t),et(H.$$.fragment,t);for(let a=0;a<P.length;a+=1)et(P[a]);Y=!1},d(t){t&&c(e),t&&c(p),t&&c(d),R&&R.d(),t&&c(ot),Ut(T,t),t&&c(at),t&&c(F),t&&c(st),t&&c(U),t&&c(rt),t&&c(N),t&&c(ct),t&&c(M),v&&v.d(),w&&w.d(),y&&y.d(),t&&c(_t),t&&c(j),t&&c(kt),t&&c(E),Ut(H),t&&c($t),t&&c(J),t&&c(Ct),t&&c(L);for(let a=0;a<A.length;a+=1)A[a].d();for(let a=0;a<P.length;a+=1)P[a].d()}}}function Ee(n,e,l){let i,S,m,p,{collection:d=new $e}=e,_=200,$=[];const C=O=>l(3,_=O.code);return n.$$set=O=>{"collection"in O&&l(0,d=O.collection)},n.$$.update=()=>{var O,B;n.$$.dirty&1&&l(2,S=(O=d==null?void 0:d.options)==null?void 0:O.allowEmailAuth),n.$$.dirty&1&&l(1,m=(B=d==null?void 0:d.options)==null?void 0:B.allowUsernameAuth),n.$$.dirty&6&&l(5,p=m&&S?"YOUR_USERNAME_OR_EMAIL":m?"YOUR_USERNAME":"YOUR_EMAIL"),n.$$.dirty&1&&l(4,$=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:de.dummyCollectionRecord(d)},null,2)},{code:400,body:`
{
"code": 400,
"message": "Failed to authenticate.",
@@ -103,4 +103,4 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
}
}
}
`}])},l(6,i=de.getApiExampleUrl(Ce.baseUrl)),[d,m,S,_,R,p,i,C]}class Be extends Se{constructor(e){super(),ve(this,e,Ee,De,we,{collection:0})}}export{Be as default};
`}])},l(6,i=de.getApiExampleUrl(Ce.baseUrl)),[d,m,S,_,$,p,i,C]}class Be extends Se{constructor(e){super(),ve(this,e,Ee,De,we,{collection:0})}}export{Be as default};
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n,m as ve,x as Y,N as pe,O as Pe,k as Se,P as Oe,n as Re,t as Z,a as x,o as f,d as ge,Q as Te,C as Ee,p as ye,r as j,u as Be,M as qe}from"./index.2a48c906.js";import{S as Ae}from"./SdkTabs.9dbb2ca3.js";function ue(o,l,s){const a=o.slice();return a[5]=l[s],a}function be(o,l,s){const a=o.slice();return a[5]=l[s],a}function _e(o,l){let s,a=l[5].code+"",_,u,i,d;function p(){return l[4](l[5])}return{key:o,first:null,c(){s=c("button"),_=v(a),u=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(C,$){r(C,s,$),n(s,_),n(s,u),i||(d=Be(s,"click",p),i=!0)},p(C,$){l=C,$&4&&a!==(a=l[5].code+"")&&Y(_,a),$&6&&j(s,"active",l[1]===l[5].code)},d(C){C&&f(s),i=!1,d()}}}function ke(o,l){let s,a,_,u;return a=new qe({props:{content:l[5].body}}),{key:o,first:null,c(){s=c("div"),he(a.$$.fragment),_=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(i,d){r(i,s,d),ve(a,s,null),n(s,_),u=!0},p(i,d){l=i;const p={};d&4&&(p.content=l[5].body),a.$set(p),(!u||d&6)&&j(s,"active",l[1]===l[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&f(s),ge(a)}}}function Ue(o){var re,fe;let l,s,a=o[0].name+"",_,u,i,d,p,C,$,D=o[0].name+"",H,ee,I,w,F,R,L,P,M,te,N,T,le,Q,K=o[0].name+"",z,se,G,E,J,y,V,B,X,S,q,g=[],ae=new Map,oe,A,k=[],ne=new Map,O;w=new Ae({props:{js:`
import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n,m as ve,x as Y,O as pe,P as Pe,k as Se,Q as Oe,n as Re,t as Z,a as x,o as f,d as ge,R as Te,C as Ee,p as ye,r as j,u as Be,N as qe}from"./index.536fddcb.js";import{S as Ae}from"./SdkTabs.3571c26b.js";function ue(o,l,s){const a=o.slice();return a[5]=l[s],a}function be(o,l,s){const a=o.slice();return a[5]=l[s],a}function _e(o,l){let s,a=l[5].code+"",_,u,i,d;function p(){return l[4](l[5])}return{key:o,first:null,c(){s=c("button"),_=v(a),u=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(C,$){r(C,s,$),n(s,_),n(s,u),i||(d=Be(s,"click",p),i=!0)},p(C,$){l=C,$&4&&a!==(a=l[5].code+"")&&Y(_,a),$&6&&j(s,"active",l[1]===l[5].code)},d(C){C&&f(s),i=!1,d()}}}function ke(o,l){let s,a,_,u;return a=new qe({props:{content:l[5].body}}),{key:o,first:null,c(){s=c("div"),he(a.$$.fragment),_=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(i,d){r(i,s,d),ve(a,s,null),n(s,_),u=!0},p(i,d){l=i;const p={};d&4&&(p.content=l[5].body),a.$set(p),(!u||d&6)&&j(s,"active",l[1]===l[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&f(s),ge(a)}}}function Ue(o){var re,fe;let l,s,a=o[0].name+"",_,u,i,d,p,C,$,D=o[0].name+"",H,ee,I,w,F,R,L,P,N,te,K,T,le,Q,M=o[0].name+"",z,se,G,E,J,y,V,B,X,S,q,g=[],ae=new Map,oe,A,k=[],ne=new Map,O;w=new Ae({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${o[3]}');
@@ -20,7 +20,7 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
'TOKEN',
'YOUR_PASSWORD',
);
`}});let W=o[2];const ie=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=be(o,W,e),m=ie(t);ae.set(m,g[e]=_e(m,t))}let U=o[2];const ce=e=>e[5].code;for(let e=0;e<U.length;e+=1){let t=ue(o,U,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=v("Confirm email change ("),_=v(a),u=v(")"),i=h(),d=c("div"),p=c("p"),C=v("Confirms "),$=c("strong"),H=v(D),ee=v(" email change request."),I=h(),he(w.$$.fragment),F=h(),R=c("h6"),R.textContent="API details",L=h(),P=c("div"),M=c("strong"),M.textContent="POST",te=h(),N=c("div"),T=c("p"),le=v("/api/collections/"),Q=c("strong"),z=v(K),se=v("/confirm-email-change"),G=h(),E=c("div"),E.textContent="Body Parameters",J=h(),y=c("table"),y.innerHTML=`<thead><tr><th>Param</th>
`}});let W=o[2];const ie=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=be(o,W,e),m=ie(t);ae.set(m,g[e]=_e(m,t))}let U=o[2];const ce=e=>e[5].code;for(let e=0;e<U.length;e+=1){let t=ue(o,U,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=v("Confirm email change ("),_=v(a),u=v(")"),i=h(),d=c("div"),p=c("p"),C=v("Confirms "),$=c("strong"),H=v(D),ee=v(" email change request."),I=h(),he(w.$$.fragment),F=h(),R=c("h6"),R.textContent="API details",L=h(),P=c("div"),N=c("strong"),N.textContent="POST",te=h(),K=c("div"),T=c("p"),le=v("/api/collections/"),Q=c("strong"),z=v(M),se=v("/confirm-email-change"),G=h(),E=c("div"),E.textContent="Body Parameters",J=h(),y=c("table"),y.innerHTML=`<thead><tr><th>Param</th>
<th>Type</th>
<th width="50%">Description</th></tr></thead>
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
@@ -30,7 +30,7 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
<tr><td><div class="inline-flex"><span class="label label-success">Required</span>
<span>password</span></div></td>
<td><span class="label">String</span></td>
<td>The account password to confirm the email change.</td></tr></tbody>`,V=h(),B=c("div"),B.textContent="Responses",X=h(),S=c("div"),q=c("div");for(let e=0;e<g.length;e+=1)g[e].c();oe=h(),A=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(d,"class","content txt-lg m-b-sm"),b(R,"class","m-b-xs"),b(M,"class","label label-primary"),b(N,"class","content"),b(P,"class","alert alert-success"),b(E,"class","section-title"),b(y,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(A,"class","tabs-content"),b(S,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,i,t),r(e,d,t),n(d,p),n(p,C),n(p,$),n($,H),n(p,ee),r(e,I,t),ve(w,e,t),r(e,F,t),r(e,R,t),r(e,L,t),r(e,P,t),n(P,M),n(P,te),n(P,N),n(N,T),n(T,le),n(T,Q),n(Q,z),n(T,se),r(e,G,t),r(e,E,t),r(e,J,t),r(e,y,t),r(e,V,t),r(e,B,t),r(e,X,t),r(e,S,t),n(S,q);for(let m=0;m<g.length;m+=1)g[m].m(q,null);n(S,oe),n(S,A);for(let m=0;m<k.length;m+=1)k[m].m(A,null);O=!0},p(e,[t]){var me,de;(!O||t&1)&&a!==(a=e[0].name+"")&&Y(_,a),(!O||t&1)&&D!==(D=e[0].name+"")&&Y(H,D);const m={};t&9&&(m.js=`
<td>The account password to confirm the email change.</td></tr></tbody>`,V=h(),B=c("div"),B.textContent="Responses",X=h(),S=c("div"),q=c("div");for(let e=0;e<g.length;e+=1)g[e].c();oe=h(),A=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(d,"class","content txt-lg m-b-sm"),b(R,"class","m-b-xs"),b(N,"class","label label-primary"),b(K,"class","content"),b(P,"class","alert alert-success"),b(E,"class","section-title"),b(y,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(A,"class","tabs-content"),b(S,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,i,t),r(e,d,t),n(d,p),n(p,C),n(p,$),n($,H),n(p,ee),r(e,I,t),ve(w,e,t),r(e,F,t),r(e,R,t),r(e,L,t),r(e,P,t),n(P,N),n(P,te),n(P,K),n(K,T),n(T,le),n(T,Q),n(Q,z),n(T,se),r(e,G,t),r(e,E,t),r(e,J,t),r(e,y,t),r(e,V,t),r(e,B,t),r(e,X,t),r(e,S,t),n(S,q);for(let m=0;m<g.length;m+=1)g[m].m(q,null);n(S,oe),n(S,A);for(let m=0;m<k.length;m+=1)k[m].m(A,null);O=!0},p(e,[t]){var me,de;(!O||t&1)&&a!==(a=e[0].name+"")&&Y(_,a),(!O||t&1)&&D!==(D=e[0].name+"")&&Y(H,D);const m={};t&9&&(m.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -52,7 +52,7 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
'TOKEN',
'YOUR_PASSWORD',
);
`),w.$set(m),(!O||t&1)&&K!==(K=e[0].name+"")&&Y(z,K),t&6&&(W=e[2],g=pe(g,t,ie,1,e,W,ae,q,Pe,_e,null,be)),t&6&&(U=e[2],Se(),k=pe(k,t,ce,1,e,U,ne,A,Oe,ke,null,ue),Re())},i(e){if(!O){Z(w.$$.fragment,e);for(let t=0;t<U.length;t+=1)Z(k[t]);O=!0}},o(e){x(w.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);O=!1},d(e){e&&f(l),e&&f(i),e&&f(d),e&&f(I),ge(w,e),e&&f(F),e&&f(R),e&&f(L),e&&f(P),e&&f(G),e&&f(E),e&&f(J),e&&f(y),e&&f(V),e&&f(B),e&&f(X),e&&f(S);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function De(o,l,s){let a,{collection:_=new Te}=l,u=204,i=[];const d=p=>s(1,u=p.code);return o.$$set=p=>{"collection"in p&&s(0,_=p.collection)},s(3,a=Ee.getApiExampleUrl(ye.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
`),w.$set(m),(!O||t&1)&&M!==(M=e[0].name+"")&&Y(z,M),t&6&&(W=e[2],g=pe(g,t,ie,1,e,W,ae,q,Pe,_e,null,be)),t&6&&(U=e[2],Se(),k=pe(k,t,ce,1,e,U,ne,A,Oe,ke,null,ue),Re())},i(e){if(!O){Z(w.$$.fragment,e);for(let t=0;t<U.length;t+=1)Z(k[t]);O=!0}},o(e){x(w.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);O=!1},d(e){e&&f(l),e&&f(i),e&&f(d),e&&f(I),ge(w,e),e&&f(F),e&&f(R),e&&f(L),e&&f(P),e&&f(G),e&&f(E),e&&f(J),e&&f(y),e&&f(V),e&&f(B),e&&f(X),e&&f(S);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function De(o,l,s){let a,{collection:_=new Te}=l,u=204,i=[];const d=p=>s(1,u=p.code);return o.$$set=p=>{"collection"in p&&s(0,_=p.collection)},s(3,a=Ee.getApiExampleUrl(ye.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
{
"code": 400,
"message": "Failed to authenticate.",
@@ -63,4 +63,4 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
}
}
}
`}]),[_,u,i,a,d]}class Ke extends Ce{constructor(l){super(),$e(this,l,De,Ue,we,{collection:0})}}export{Ke as default};
`}]),[_,u,i,a,d]}class Me extends Ce{constructor(l){super(),$e(this,l,De,Ue,we,{collection:0})}}export{Me as default};
@@ -1,4 +1,4 @@
import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as we,x as K,N as me,O as Oe,k as Ne,P as Ce,n as We,t as Z,a as x,o as d,d as Pe,Q as $e,C as Ee,p as Te,r as U,u as ge,M as Ae}from"./index.2a48c906.js";import{S as De}from"./SdkTabs.9dbb2ca3.js";function ue(o,s,l){const a=o.slice();return a[5]=s[l],a}function be(o,s,l){const a=o.slice();return a[5]=s[l],a}function _e(o,s){let l,a=s[5].code+"",_,u,i,p;function m(){return s[4](s[5])}return{key:o,first:null,c(){l=c("button"),_=w(a),u=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(S,h){r(S,l,h),n(l,_),n(l,u),i||(p=ge(l,"click",m),i=!0)},p(S,h){s=S,h&4&&a!==(a=s[5].code+"")&&K(_,a),h&6&&U(l,"active",s[1]===s[5].code)},d(S){S&&d(l),i=!1,p()}}}function ke(o,s){let l,a,_,u;return a=new Ae({props:{content:s[5].body}}),{key:o,first:null,c(){l=c("div"),ve(a.$$.fragment),_=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(i,p){r(i,l,p),we(a,l,null),n(l,_),u=!0},p(i,p){s=i;const m={};p&4&&(m.content=s[5].body),a.$set(m),(!u||p&6)&&U(l,"active",s[1]===s[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&d(l),Pe(a)}}}function ye(o){var re,de;let s,l,a=o[0].name+"",_,u,i,p,m,S,h,M=o[0].name+"",j,ee,H,R,L,W,Q,O,q,te,B,$,se,z,I=o[0].name+"",G,le,J,E,V,T,X,g,Y,N,A,P=[],ae=new Map,oe,D,k=[],ne=new Map,C;R=new De({props:{js:`
import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as we,x as K,O as me,P as Oe,k as Ne,Q as Ce,n as We,t as Z,a as x,o as d,d as Pe,R as $e,C as Ee,p as Te,r as U,u as ge,N as Ae}from"./index.536fddcb.js";import{S as De}from"./SdkTabs.3571c26b.js";function ue(o,s,l){const a=o.slice();return a[5]=s[l],a}function be(o,s,l){const a=o.slice();return a[5]=s[l],a}function _e(o,s){let l,a=s[5].code+"",_,u,i,p;function m(){return s[4](s[5])}return{key:o,first:null,c(){l=c("button"),_=w(a),u=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(S,h){r(S,l,h),n(l,_),n(l,u),i||(p=ge(l,"click",m),i=!0)},p(S,h){s=S,h&4&&a!==(a=s[5].code+"")&&K(_,a),h&6&&U(l,"active",s[1]===s[5].code)},d(S){S&&d(l),i=!1,p()}}}function ke(o,s){let l,a,_,u;return a=new Ae({props:{content:s[5].body}}),{key:o,first:null,c(){l=c("div"),ve(a.$$.fragment),_=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(i,p){r(i,l,p),we(a,l,null),n(l,_),u=!0},p(i,p){s=i;const m={};p&4&&(m.content=s[5].body),a.$set(m),(!u||p&6)&&U(l,"active",s[1]===s[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&d(l),Pe(a)}}}function ye(o){var re,de;let s,l,a=o[0].name+"",_,u,i,p,m,S,h,q=o[0].name+"",j,ee,H,R,L,W,Q,O,B,te,M,$,se,z,I=o[0].name+"",G,le,J,E,V,T,X,g,Y,N,A,P=[],ae=new Map,oe,D,k=[],ne=new Map,C;R=new De({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${o[3]}');
@@ -22,7 +22,7 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
'NEW_PASSWORD',
'NEW_PASSWORD_CONFIRM',
);
`}});let F=o[2];const ie=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=be(o,F,e),f=ie(t);ae.set(f,P[e]=_e(f,t))}let y=o[2];const ce=e=>e[5].code;for(let e=0;e<y.length;e+=1){let t=ue(o,y,e),f=ce(t);ne.set(f,k[e]=ke(f,t))}return{c(){s=c("h3"),l=w("Confirm password reset ("),_=w(a),u=w(")"),i=v(),p=c("div"),m=c("p"),S=w("Confirms "),h=c("strong"),j=w(M),ee=w(" password reset request and sets a new password."),H=v(),ve(R.$$.fragment),L=v(),W=c("h6"),W.textContent="API details",Q=v(),O=c("div"),q=c("strong"),q.textContent="POST",te=v(),B=c("div"),$=c("p"),se=w("/api/collections/"),z=c("strong"),G=w(I),le=w("/confirm-password-reset"),J=v(),E=c("div"),E.textContent="Body Parameters",V=v(),T=c("table"),T.innerHTML=`<thead><tr><th>Param</th>
`}});let F=o[2];const ie=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=be(o,F,e),f=ie(t);ae.set(f,P[e]=_e(f,t))}let y=o[2];const ce=e=>e[5].code;for(let e=0;e<y.length;e+=1){let t=ue(o,y,e),f=ce(t);ne.set(f,k[e]=ke(f,t))}return{c(){s=c("h3"),l=w("Confirm password reset ("),_=w(a),u=w(")"),i=v(),p=c("div"),m=c("p"),S=w("Confirms "),h=c("strong"),j=w(q),ee=w(" password reset request and sets a new password."),H=v(),ve(R.$$.fragment),L=v(),W=c("h6"),W.textContent="API details",Q=v(),O=c("div"),B=c("strong"),B.textContent="POST",te=v(),M=c("div"),$=c("p"),se=w("/api/collections/"),z=c("strong"),G=w(I),le=w("/confirm-password-reset"),J=v(),E=c("div"),E.textContent="Body Parameters",V=v(),T=c("table"),T.innerHTML=`<thead><tr><th>Param</th>
<th>Type</th>
<th width="50%">Description</th></tr></thead>
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
@@ -36,7 +36,7 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
<tr><td><div class="inline-flex"><span class="label label-success">Required</span>
<span>passwordConfirm</span></div></td>
<td><span class="label">String</span></td>
<td>The new password confirmation.</td></tr></tbody>`,X=v(),g=c("div"),g.textContent="Responses",Y=v(),N=c("div"),A=c("div");for(let e=0;e<P.length;e+=1)P[e].c();oe=v(),D=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(s,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(W,"class","m-b-xs"),b(q,"class","label label-primary"),b(B,"class","content"),b(O,"class","alert alert-success"),b(E,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(g,"class","section-title"),b(A,"class","tabs-header compact left"),b(D,"class","tabs-content"),b(N,"class","tabs")},m(e,t){r(e,s,t),n(s,l),n(s,_),n(s,u),r(e,i,t),r(e,p,t),n(p,m),n(m,S),n(m,h),n(h,j),n(m,ee),r(e,H,t),we(R,e,t),r(e,L,t),r(e,W,t),r(e,Q,t),r(e,O,t),n(O,q),n(O,te),n(O,B),n(B,$),n($,se),n($,z),n(z,G),n($,le),r(e,J,t),r(e,E,t),r(e,V,t),r(e,T,t),r(e,X,t),r(e,g,t),r(e,Y,t),r(e,N,t),n(N,A);for(let f=0;f<P.length;f+=1)P[f].m(A,null);n(N,oe),n(N,D);for(let f=0;f<k.length;f+=1)k[f].m(D,null);C=!0},p(e,[t]){var fe,pe;(!C||t&1)&&a!==(a=e[0].name+"")&&K(_,a),(!C||t&1)&&M!==(M=e[0].name+"")&&K(j,M);const f={};t&9&&(f.js=`
<td>The new password confirmation.</td></tr></tbody>`,X=v(),g=c("div"),g.textContent="Responses",Y=v(),N=c("div"),A=c("div");for(let e=0;e<P.length;e+=1)P[e].c();oe=v(),D=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(s,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(W,"class","m-b-xs"),b(B,"class","label label-primary"),b(M,"class","content"),b(O,"class","alert alert-success"),b(E,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(g,"class","section-title"),b(A,"class","tabs-header compact left"),b(D,"class","tabs-content"),b(N,"class","tabs")},m(e,t){r(e,s,t),n(s,l),n(s,_),n(s,u),r(e,i,t),r(e,p,t),n(p,m),n(m,S),n(m,h),n(h,j),n(m,ee),r(e,H,t),we(R,e,t),r(e,L,t),r(e,W,t),r(e,Q,t),r(e,O,t),n(O,B),n(O,te),n(O,M),n(M,$),n($,se),n($,z),n(z,G),n($,le),r(e,J,t),r(e,E,t),r(e,V,t),r(e,T,t),r(e,X,t),r(e,g,t),r(e,Y,t),r(e,N,t),n(N,A);for(let f=0;f<P.length;f+=1)P[f].m(A,null);n(N,oe),n(N,D);for(let f=0;f<k.length;f+=1)k[f].m(D,null);C=!0},p(e,[t]){var fe,pe;(!C||t&1)&&a!==(a=e[0].name+"")&&K(_,a),(!C||t&1)&&q!==(q=e[0].name+"")&&K(j,q);const f={};t&9&&(f.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -60,7 +60,7 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
'NEW_PASSWORD',
'NEW_PASSWORD_CONFIRM',
);
`),R.$set(f),(!C||t&1)&&I!==(I=e[0].name+"")&&K(G,I),t&6&&(F=e[2],P=me(P,t,ie,1,e,F,ae,A,Oe,_e,null,be)),t&6&&(y=e[2],Ne(),k=me(k,t,ce,1,e,y,ne,D,Ce,ke,null,ue),We())},i(e){if(!C){Z(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)Z(k[t]);C=!0}},o(e){x(R.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);C=!1},d(e){e&&d(s),e&&d(i),e&&d(p),e&&d(H),Pe(R,e),e&&d(L),e&&d(W),e&&d(Q),e&&d(O),e&&d(J),e&&d(E),e&&d(V),e&&d(T),e&&d(X),e&&d(g),e&&d(Y),e&&d(N);for(let t=0;t<P.length;t+=1)P[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Me(o,s,l){let a,{collection:_=new $e}=s,u=204,i=[];const p=m=>l(1,u=m.code);return o.$$set=m=>{"collection"in m&&l(0,_=m.collection)},l(3,a=Ee.getApiExampleUrl(Te.baseUrl)),l(2,i=[{code:204,body:"null"},{code:400,body:`
`),R.$set(f),(!C||t&1)&&I!==(I=e[0].name+"")&&K(G,I),t&6&&(F=e[2],P=me(P,t,ie,1,e,F,ae,A,Oe,_e,null,be)),t&6&&(y=e[2],Ne(),k=me(k,t,ce,1,e,y,ne,D,Ce,ke,null,ue),We())},i(e){if(!C){Z(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)Z(k[t]);C=!0}},o(e){x(R.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);C=!1},d(e){e&&d(s),e&&d(i),e&&d(p),e&&d(H),Pe(R,e),e&&d(L),e&&d(W),e&&d(Q),e&&d(O),e&&d(J),e&&d(E),e&&d(V),e&&d(T),e&&d(X),e&&d(g),e&&d(Y),e&&d(N);for(let t=0;t<P.length;t+=1)P[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function qe(o,s,l){let a,{collection:_=new $e}=s,u=204,i=[];const p=m=>l(1,u=m.code);return o.$$set=m=>{"collection"in m&&l(0,_=m.collection)},l(3,a=Ee.getApiExampleUrl(Te.baseUrl)),l(2,i=[{code:204,body:"null"},{code:400,body:`
{
"code": 400,
"message": "Failed to authenticate.",
@@ -71,4 +71,4 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
}
}
}
`}]),[_,u,i,a,p]}class Ie extends Se{constructor(s){super(),he(this,s,Me,ye,Re,{collection:0})}}export{Ie as default};
`}]),[_,u,i,a,p]}class Ie extends Se{constructor(s){super(),he(this,s,qe,ye,Re,{collection:0})}}export{Ie as default};
@@ -1,4 +1,4 @@
import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n,m as he,x as H,N as de,O as Te,k as ge,P as ye,n as Be,t as Z,a as x,o as f,d as $e,Q as qe,C as Oe,p as Se,r as I,u as Ee,M as Me}from"./index.2a48c906.js";import{S as Ne}from"./SdkTabs.9dbb2ca3.js";function ue(i,l,s){const o=i.slice();return o[5]=l[s],o}function be(i,l,s){const o=i.slice();return o[5]=l[s],o}function _e(i,l){let s,o=l[5].code+"",_,u,a,p;function d(){return l[4](l[5])}return{key:i,first:null,c(){s=c("button"),_=h(o),u=v(),b(s,"class","tab-item"),I(s,"active",l[1]===l[5].code),this.first=s},m(w,C){r(w,s,C),n(s,_),n(s,u),a||(p=Ee(s,"click",d),a=!0)},p(w,C){l=w,C&4&&o!==(o=l[5].code+"")&&H(_,o),C&6&&I(s,"active",l[1]===l[5].code)},d(w){w&&f(s),a=!1,p()}}}function ke(i,l){let s,o,_,u;return o=new Me({props:{content:l[5].body}}),{key:i,first:null,c(){s=c("div"),ve(o.$$.fragment),_=v(),b(s,"class","tab-item"),I(s,"active",l[1]===l[5].code),this.first=s},m(a,p){r(a,s,p),he(o,s,null),n(s,_),u=!0},p(a,p){l=a;const d={};p&4&&(d.content=l[5].body),o.$set(d),(!u||p&6)&&I(s,"active",l[1]===l[5].code)},i(a){u||(Z(o.$$.fragment,a),u=!0)},o(a){x(o.$$.fragment,a),u=!1},d(a){a&&f(s),$e(o)}}}function Ve(i){var re,fe;let l,s,o=i[0].name+"",_,u,a,p,d,w,C,K=i[0].name+"",R,ee,F,P,L,B,Q,T,A,te,U,q,le,z,j=i[0].name+"",G,se,J,O,W,S,X,E,Y,g,M,$=[],oe=new Map,ie,N,k=[],ne=new Map,y;P=new Ne({props:{js:`
import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n,m as he,x as D,O as de,P as Te,k as ge,Q as ye,n as Be,t as Z,a as x,o as f,d as $e,R as qe,C as Oe,p as Se,r as H,u as Ee,N as Ne}from"./index.536fddcb.js";import{S as Ve}from"./SdkTabs.3571c26b.js";function ue(i,l,s){const o=i.slice();return o[5]=l[s],o}function be(i,l,s){const o=i.slice();return o[5]=l[s],o}function _e(i,l){let s,o=l[5].code+"",_,u,a,p;function d(){return l[4](l[5])}return{key:i,first:null,c(){s=c("button"),_=h(o),u=v(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(w,C){r(w,s,C),n(s,_),n(s,u),a||(p=Ee(s,"click",d),a=!0)},p(w,C){l=w,C&4&&o!==(o=l[5].code+"")&&D(_,o),C&6&&H(s,"active",l[1]===l[5].code)},d(w){w&&f(s),a=!1,p()}}}function ke(i,l){let s,o,_,u;return o=new Ne({props:{content:l[5].body}}),{key:i,first:null,c(){s=c("div"),ve(o.$$.fragment),_=v(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(a,p){r(a,s,p),he(o,s,null),n(s,_),u=!0},p(a,p){l=a;const d={};p&4&&(d.content=l[5].body),o.$set(d),(!u||p&6)&&H(s,"active",l[1]===l[5].code)},i(a){u||(Z(o.$$.fragment,a),u=!0)},o(a){x(o.$$.fragment,a),u=!1},d(a){a&&f(s),$e(o)}}}function Ke(i){var re,fe;let l,s,o=i[0].name+"",_,u,a,p,d,w,C,M=i[0].name+"",I,ee,F,P,L,B,Q,T,A,te,R,q,le,z,U=i[0].name+"",G,se,J,O,W,S,X,E,Y,g,N,$=[],oe=new Map,ie,V,k=[],ne=new Map,y;P=new Ve({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${i[3]}');
@@ -14,13 +14,13 @@ import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n
...
await pb.collection('${(fe=i[0])==null?void 0:fe.name}').confirmVerification('TOKEN');
`}});let D=i[2];const ae=e=>e[5].code;for(let e=0;e<D.length;e+=1){let t=be(i,D,e),m=ae(t);oe.set(m,$[e]=_e(m,t))}let V=i[2];const ce=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=ue(i,V,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=h("Confirm verification ("),_=h(o),u=h(")"),a=v(),p=c("div"),d=c("p"),w=h("Confirms "),C=c("strong"),R=h(K),ee=h(" account verification request."),F=v(),ve(P.$$.fragment),L=v(),B=c("h6"),B.textContent="API details",Q=v(),T=c("div"),A=c("strong"),A.textContent="POST",te=v(),U=c("div"),q=c("p"),le=h("/api/collections/"),z=c("strong"),G=h(j),se=h("/confirm-verification"),J=v(),O=c("div"),O.textContent="Body Parameters",W=v(),S=c("table"),S.innerHTML=`<thead><tr><th>Param</th>
`}});let j=i[2];const ae=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=be(i,j,e),m=ae(t);oe.set(m,$[e]=_e(m,t))}let K=i[2];const ce=e=>e[5].code;for(let e=0;e<K.length;e+=1){let t=ue(i,K,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=h("Confirm verification ("),_=h(o),u=h(")"),a=v(),p=c("div"),d=c("p"),w=h("Confirms "),C=c("strong"),I=h(M),ee=h(" account verification request."),F=v(),ve(P.$$.fragment),L=v(),B=c("h6"),B.textContent="API details",Q=v(),T=c("div"),A=c("strong"),A.textContent="POST",te=v(),R=c("div"),q=c("p"),le=h("/api/collections/"),z=c("strong"),G=h(U),se=h("/confirm-verification"),J=v(),O=c("div"),O.textContent="Body Parameters",W=v(),S=c("table"),S.innerHTML=`<thead><tr><th>Param</th>
<th>Type</th>
<th width="50%">Description</th></tr></thead>
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
<span>token</span></div></td>
<td><span class="label">String</span></td>
<td>The token from the verification request email.</td></tr></tbody>`,X=v(),E=c("div"),E.textContent="Responses",Y=v(),g=c("div"),M=c("div");for(let e=0;e<$.length;e+=1)$[e].c();ie=v(),N=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(A,"class","label label-primary"),b(U,"class","content"),b(T,"class","alert alert-success"),b(O,"class","section-title"),b(S,"class","table-compact table-border m-b-base"),b(E,"class","section-title"),b(M,"class","tabs-header compact left"),b(N,"class","tabs-content"),b(g,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,a,t),r(e,p,t),n(p,d),n(d,w),n(d,C),n(C,R),n(d,ee),r(e,F,t),he(P,e,t),r(e,L,t),r(e,B,t),r(e,Q,t),r(e,T,t),n(T,A),n(T,te),n(T,U),n(U,q),n(q,le),n(q,z),n(z,G),n(q,se),r(e,J,t),r(e,O,t),r(e,W,t),r(e,S,t),r(e,X,t),r(e,E,t),r(e,Y,t),r(e,g,t),n(g,M);for(let m=0;m<$.length;m+=1)$[m].m(M,null);n(g,ie),n(g,N);for(let m=0;m<k.length;m+=1)k[m].m(N,null);y=!0},p(e,[t]){var me,pe;(!y||t&1)&&o!==(o=e[0].name+"")&&H(_,o),(!y||t&1)&&K!==(K=e[0].name+"")&&H(R,K);const m={};t&9&&(m.js=`
<td>The token from the verification request email.</td></tr></tbody>`,X=v(),E=c("div"),E.textContent="Responses",Y=v(),g=c("div"),N=c("div");for(let e=0;e<$.length;e+=1)$[e].c();ie=v(),V=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(A,"class","label label-primary"),b(R,"class","content"),b(T,"class","alert alert-success"),b(O,"class","section-title"),b(S,"class","table-compact table-border m-b-base"),b(E,"class","section-title"),b(N,"class","tabs-header compact left"),b(V,"class","tabs-content"),b(g,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,a,t),r(e,p,t),n(p,d),n(d,w),n(d,C),n(C,I),n(d,ee),r(e,F,t),he(P,e,t),r(e,L,t),r(e,B,t),r(e,Q,t),r(e,T,t),n(T,A),n(T,te),n(T,R),n(R,q),n(q,le),n(q,z),n(z,G),n(q,se),r(e,J,t),r(e,O,t),r(e,W,t),r(e,S,t),r(e,X,t),r(e,E,t),r(e,Y,t),r(e,g,t),n(g,N);for(let m=0;m<$.length;m+=1)$[m].m(N,null);n(g,ie),n(g,V);for(let m=0;m<k.length;m+=1)k[m].m(V,null);y=!0},p(e,[t]){var me,pe;(!y||t&1)&&o!==(o=e[0].name+"")&&D(_,o),(!y||t&1)&&M!==(M=e[0].name+"")&&D(I,M);const m={};t&9&&(m.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -36,7 +36,7 @@ import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n
...
await pb.collection('${(pe=e[0])==null?void 0:pe.name}').confirmVerification('TOKEN');
`),P.$set(m),(!y||t&1)&&j!==(j=e[0].name+"")&&H(G,j),t&6&&(D=e[2],$=de($,t,ae,1,e,D,oe,M,Te,_e,null,be)),t&6&&(V=e[2],ge(),k=de(k,t,ce,1,e,V,ne,N,ye,ke,null,ue),Be())},i(e){if(!y){Z(P.$$.fragment,e);for(let t=0;t<V.length;t+=1)Z(k[t]);y=!0}},o(e){x(P.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);y=!1},d(e){e&&f(l),e&&f(a),e&&f(p),e&&f(F),$e(P,e),e&&f(L),e&&f(B),e&&f(Q),e&&f(T),e&&f(J),e&&f(O),e&&f(W),e&&f(S),e&&f(X),e&&f(E),e&&f(Y),e&&f(g);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Ke(i,l,s){let o,{collection:_=new qe}=l,u=204,a=[];const p=d=>s(1,u=d.code);return i.$$set=d=>{"collection"in d&&s(0,_=d.collection)},s(3,o=Oe.getApiExampleUrl(Se.baseUrl)),s(2,a=[{code:204,body:"null"},{code:400,body:`
`),P.$set(m),(!y||t&1)&&U!==(U=e[0].name+"")&&D(G,U),t&6&&(j=e[2],$=de($,t,ae,1,e,j,oe,N,Te,_e,null,be)),t&6&&(K=e[2],ge(),k=de(k,t,ce,1,e,K,ne,V,ye,ke,null,ue),Be())},i(e){if(!y){Z(P.$$.fragment,e);for(let t=0;t<K.length;t+=1)Z(k[t]);y=!0}},o(e){x(P.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);y=!1},d(e){e&&f(l),e&&f(a),e&&f(p),e&&f(F),$e(P,e),e&&f(L),e&&f(B),e&&f(Q),e&&f(T),e&&f(J),e&&f(O),e&&f(W),e&&f(S),e&&f(X),e&&f(E),e&&f(Y),e&&f(g);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Me(i,l,s){let o,{collection:_=new qe}=l,u=204,a=[];const p=d=>s(1,u=d.code);return i.$$set=d=>{"collection"in d&&s(0,_=d.collection)},s(3,o=Oe.getApiExampleUrl(Se.baseUrl)),s(2,a=[{code:204,body:"null"},{code:400,body:`
{
"code": 400,
"message": "Failed to authenticate.",
@@ -47,4 +47,4 @@ import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n
}
}
}
`}]),[_,u,a,o,p]}class je extends we{constructor(l){super(),Ce(this,l,Ke,Ve,Pe,{collection:0})}}export{je as default};
`}]),[_,u,a,o,p]}class Ue extends we{constructor(l){super(),Ce(this,l,Me,Ke,Pe,{collection:0})}}export{Ue as default};
@@ -1,15 +1,15 @@
import{S as Ht,i as Lt,s as Pt,C as Q,M as At,e as a,w as k,b as m,c as Pe,f as h,g as r,h as n,m as Be,x,N as Le,O as ht,k as Bt,P as Ft,n as Rt,t as fe,a as pe,o as d,d as Fe,Q as gt,p as jt,r as ue,u as Dt,y as le}from"./index.2a48c906.js";import{S as Nt}from"./SdkTabs.9dbb2ca3.js";function wt(o,e,l){const s=o.slice();return s[7]=e[l],s}function Ct(o,e,l){const s=o.slice();return s[7]=e[l],s}function St(o,e,l){const s=o.slice();return s[12]=e[l],s}function $t(o){let e;return{c(){e=a("p"),e.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",h(e,"class","txt-hint txt-sm txt-right")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Tt(o){let e,l,s,b,p,c,f,y,T,w,M,g,D,V,L,I,j,F,S,N,q,C,_;function O(u,$){var ee,K;return(K=(ee=u[0])==null?void 0:ee.options)!=null&&K.requireEmail?It:Vt}let z=O(o),P=z(o);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint">Auth fields</td>',l=m(),s=a("tr"),s.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
import{S as Ht,i as Lt,s as Pt,C as Q,N as At,e as a,w as k,b as m,c as Pe,f as h,g as r,h as n,m as Re,x,O as Le,P as ht,k as Rt,Q as Bt,n as Ft,t as fe,a as pe,o as d,d as Be,R as gt,p as jt,r as ue,u as Dt,y as le}from"./index.536fddcb.js";import{S as Nt}from"./SdkTabs.3571c26b.js";function wt(o,e,l){const s=o.slice();return s[7]=e[l],s}function Ct(o,e,l){const s=o.slice();return s[7]=e[l],s}function St(o,e,l){const s=o.slice();return s[12]=e[l],s}function $t(o){let e;return{c(){e=a("p"),e.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",h(e,"class","txt-hint txt-sm txt-right")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Tt(o){let e,l,s,b,p,c,f,y,T,w,O,g,D,V,L,I,j,B,S,N,q,C,_;function M(u,$){var ee,K;return(K=(ee=u[0])==null?void 0:ee.options)!=null&&K.requireEmail?It:Vt}let z=M(o),P=z(o);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint">Auth fields</td>',l=m(),s=a("tr"),s.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
<span>username</span></div></td>
<td><span class="label">String</span></td>
<td>The username of the auth record.
<br/>
If not set, it will be auto generated.</td>`,b=m(),p=a("tr"),c=a("td"),f=a("div"),P.c(),y=m(),T=a("span"),T.textContent="email",w=m(),M=a("td"),M.innerHTML='<span class="label">String</span>',g=m(),D=a("td"),D.textContent="Auth record email address.",V=m(),L=a("tr"),L.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
If not set, it will be auto generated.</td>`,b=m(),p=a("tr"),c=a("td"),f=a("div"),P.c(),y=m(),T=a("span"),T.textContent="email",w=m(),O=a("td"),O.innerHTML='<span class="label">String</span>',g=m(),D=a("td"),D.textContent="Auth record email address.",V=m(),L=a("tr"),L.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
<span>emailVisibility</span></div></td>
<td><span class="label">Boolean</span></td>
<td>Whether to show/hide the auth record email when fetching the record data.</td>`,I=m(),j=a("tr"),j.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
<span>password</span></div></td>
<td><span class="label">String</span></td>
<td>Auth record password.</td>`,F=m(),S=a("tr"),S.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
<td>Auth record password.</td>`,B=m(),S=a("tr"),S.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
<span>passwordConfirm</span></div></td>
<td><span class="label">String</span></td>
<td>Auth record password confirmation.</td>`,N=m(),q=a("tr"),q.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
@@ -17,8 +17,8 @@ import{S as Ht,i as Lt,s as Pt,C as Q,M as At,e as a,w as k,b as m,c as Pe,f as
<td><span class="label">Boolean</span></td>
<td>Indicates whether the auth record is verified or not.
<br/>
This field can be set only by admins or auth records with &quot;Manage&quot; access.</td>`,C=m(),_=a("tr"),_.innerHTML='<td colspan="3" class="txt-hint">Schema fields</td>',h(f,"class","inline-flex")},m(u,$){r(u,e,$),r(u,l,$),r(u,s,$),r(u,b,$),r(u,p,$),n(p,c),n(c,f),P.m(f,null),n(f,y),n(f,T),n(p,w),n(p,M),n(p,g),n(p,D),r(u,V,$),r(u,L,$),r(u,I,$),r(u,j,$),r(u,F,$),r(u,S,$),r(u,N,$),r(u,q,$),r(u,C,$),r(u,_,$)},p(u,$){z!==(z=O(u))&&(P.d(1),P=z(u),P&&(P.c(),P.m(f,y)))},d(u){u&&d(e),u&&d(l),u&&d(s),u&&d(b),u&&d(p),P.d(),u&&d(V),u&&d(L),u&&d(I),u&&d(j),u&&d(F),u&&d(S),u&&d(N),u&&d(q),u&&d(C),u&&d(_)}}}function Vt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function It(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Jt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Et(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Ut(o){var p;let e,l=((p=o[12].options)==null?void 0:p.maxSelect)===1?"id":"ids",s,b;return{c(){e=k("Relation record "),s=k(l),b=k(".")},m(c,f){r(c,e,f),r(c,s,f),r(c,b,f)},p(c,f){var y;f&1&&l!==(l=((y=c[12].options)==null?void 0:y.maxSelect)===1?"id":"ids")&&x(s,l)},d(c){c&&d(e),c&&d(s),c&&d(b)}}}function Qt(o){let e,l,s,b,p;return{c(){e=k("File object."),l=a("br"),s=k(`
Set to `),b=a("code"),b.textContent="null",p=k(" to delete already uploaded file(s).")},m(c,f){r(c,e,f),r(c,l,f),r(c,s,f),r(c,b,f),r(c,p,f)},p:le,d(c){c&&d(e),c&&d(l),c&&d(s),c&&d(b),c&&d(p)}}}function zt(o){let e;return{c(){e=k("URL address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Kt(o){let e;return{c(){e=k("Email address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Wt(o){let e;return{c(){e=k("JSON array or object.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Yt(o){let e;return{c(){e=k("Number value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Gt(o){let e;return{c(){e=k("Plain text value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function qt(o,e){let l,s,b,p,c,f=e[12].name+"",y,T,w,M,g=Q.getFieldValueType(e[12])+"",D,V,L,I;function j(_,O){return _[12].required?Et:Jt}let F=j(e),S=F(e);function N(_,O){if(_[12].type==="text")return Gt;if(_[12].type==="number")return Yt;if(_[12].type==="json")return Wt;if(_[12].type==="email")return Kt;if(_[12].type==="url")return zt;if(_[12].type==="file")return Qt;if(_[12].type==="relation")return Ut}let q=N(e),C=q&&q(e);return{key:o,first:null,c(){l=a("tr"),s=a("td"),b=a("div"),S.c(),p=m(),c=a("span"),y=k(f),T=m(),w=a("td"),M=a("span"),D=k(g),V=m(),L=a("td"),C&&C.c(),I=m(),h(b,"class","inline-flex"),h(M,"class","label"),this.first=l},m(_,O){r(_,l,O),n(l,s),n(s,b),S.m(b,null),n(b,p),n(b,c),n(c,y),n(l,T),n(l,w),n(w,M),n(M,D),n(l,V),n(l,L),C&&C.m(L,null),n(l,I)},p(_,O){e=_,F!==(F=j(e))&&(S.d(1),S=F(e),S&&(S.c(),S.m(b,p))),O&1&&f!==(f=e[12].name+"")&&x(y,f),O&1&&g!==(g=Q.getFieldValueType(e[12])+"")&&x(D,g),q===(q=N(e))&&C?C.p(e,O):(C&&C.d(1),C=q&&q(e),C&&(C.c(),C.m(L,null)))},d(_){_&&d(l),S.d(),C&&C.d()}}}function Mt(o,e){let l,s=e[7].code+"",b,p,c,f;function y(){return e[6](e[7])}return{key:o,first:null,c(){l=a("button"),b=k(s),p=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(T,w){r(T,l,w),n(l,b),n(l,p),c||(f=Dt(l,"click",y),c=!0)},p(T,w){e=T,w&4&&s!==(s=e[7].code+"")&&x(b,s),w&6&&ue(l,"active",e[1]===e[7].code)},d(T){T&&d(l),c=!1,f()}}}function Ot(o,e){let l,s,b,p;return s=new At({props:{content:e[7].body}}),{key:o,first:null,c(){l=a("div"),Pe(s.$$.fragment),b=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(c,f){r(c,l,f),Be(s,l,null),n(l,b),p=!0},p(c,f){e=c;const y={};f&4&&(y.content=e[7].body),s.$set(y),(!p||f&6)&&ue(l,"active",e[1]===e[7].code)},i(c){p||(fe(s.$$.fragment,c),p=!0)},o(c){pe(s.$$.fragment,c),p=!1},d(c){c&&d(l),Fe(s)}}}function Xt(o){var st,it,at,ot,rt,dt,ct,ft;let e,l,s=o[0].name+"",b,p,c,f,y,T,w,M=o[0].name+"",g,D,V,L,I,j,F,S,N,q,C,_,O,z,P,u,$,ee,K=o[0].name+"",me,Re,ge,be,ne,_e,W,ke,je,J,ye,De,ve,E=[],Ne=new Map,he,se,we,Y,Ce,Ve,Se,G,$e,Ie,Te,Je,A,Ee,te,Ue,Qe,ze,qe,Ke,Me,We,Ye,Ge,Oe,Xe,Ae,ie,He,X,ae,U=[],Ze=new Map,xe,oe,R=[],et=new Map,Z;S=new Nt({props:{js:`
This field can be set only by admins or auth records with &quot;Manage&quot; access.</td>`,C=m(),_=a("tr"),_.innerHTML='<td colspan="3" class="txt-hint">Schema fields</td>',h(f,"class","inline-flex")},m(u,$){r(u,e,$),r(u,l,$),r(u,s,$),r(u,b,$),r(u,p,$),n(p,c),n(c,f),P.m(f,null),n(f,y),n(f,T),n(p,w),n(p,O),n(p,g),n(p,D),r(u,V,$),r(u,L,$),r(u,I,$),r(u,j,$),r(u,B,$),r(u,S,$),r(u,N,$),r(u,q,$),r(u,C,$),r(u,_,$)},p(u,$){z!==(z=M(u))&&(P.d(1),P=z(u),P&&(P.c(),P.m(f,y)))},d(u){u&&d(e),u&&d(l),u&&d(s),u&&d(b),u&&d(p),P.d(),u&&d(V),u&&d(L),u&&d(I),u&&d(j),u&&d(B),u&&d(S),u&&d(N),u&&d(q),u&&d(C),u&&d(_)}}}function Vt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function It(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Jt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Et(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Ut(o){var p;let e,l=((p=o[12].options)==null?void 0:p.maxSelect)===1?"id":"ids",s,b;return{c(){e=k("Relation record "),s=k(l),b=k(".")},m(c,f){r(c,e,f),r(c,s,f),r(c,b,f)},p(c,f){var y;f&1&&l!==(l=((y=c[12].options)==null?void 0:y.maxSelect)===1?"id":"ids")&&x(s,l)},d(c){c&&d(e),c&&d(s),c&&d(b)}}}function Qt(o){let e,l,s,b,p;return{c(){e=k("File object."),l=a("br"),s=k(`
Set to `),b=a("code"),b.textContent="null",p=k(" to delete already uploaded file(s).")},m(c,f){r(c,e,f),r(c,l,f),r(c,s,f),r(c,b,f),r(c,p,f)},p:le,d(c){c&&d(e),c&&d(l),c&&d(s),c&&d(b),c&&d(p)}}}function zt(o){let e;return{c(){e=k("URL address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Kt(o){let e;return{c(){e=k("Email address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Wt(o){let e;return{c(){e=k("JSON array or object.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Yt(o){let e;return{c(){e=k("Number value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Gt(o){let e;return{c(){e=k("Plain text value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function qt(o,e){let l,s,b,p,c,f=e[12].name+"",y,T,w,O,g=Q.getFieldValueType(e[12])+"",D,V,L,I;function j(_,M){return _[12].required?Et:Jt}let B=j(e),S=B(e);function N(_,M){if(_[12].type==="text")return Gt;if(_[12].type==="number")return Yt;if(_[12].type==="json")return Wt;if(_[12].type==="email")return Kt;if(_[12].type==="url")return zt;if(_[12].type==="file")return Qt;if(_[12].type==="relation")return Ut}let q=N(e),C=q&&q(e);return{key:o,first:null,c(){l=a("tr"),s=a("td"),b=a("div"),S.c(),p=m(),c=a("span"),y=k(f),T=m(),w=a("td"),O=a("span"),D=k(g),V=m(),L=a("td"),C&&C.c(),I=m(),h(b,"class","inline-flex"),h(O,"class","label"),this.first=l},m(_,M){r(_,l,M),n(l,s),n(s,b),S.m(b,null),n(b,p),n(b,c),n(c,y),n(l,T),n(l,w),n(w,O),n(O,D),n(l,V),n(l,L),C&&C.m(L,null),n(l,I)},p(_,M){e=_,B!==(B=j(e))&&(S.d(1),S=B(e),S&&(S.c(),S.m(b,p))),M&1&&f!==(f=e[12].name+"")&&x(y,f),M&1&&g!==(g=Q.getFieldValueType(e[12])+"")&&x(D,g),q===(q=N(e))&&C?C.p(e,M):(C&&C.d(1),C=q&&q(e),C&&(C.c(),C.m(L,null)))},d(_){_&&d(l),S.d(),C&&C.d()}}}function Ot(o,e){let l,s=e[7].code+"",b,p,c,f;function y(){return e[6](e[7])}return{key:o,first:null,c(){l=a("button"),b=k(s),p=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(T,w){r(T,l,w),n(l,b),n(l,p),c||(f=Dt(l,"click",y),c=!0)},p(T,w){e=T,w&4&&s!==(s=e[7].code+"")&&x(b,s),w&6&&ue(l,"active",e[1]===e[7].code)},d(T){T&&d(l),c=!1,f()}}}function Mt(o,e){let l,s,b,p;return s=new At({props:{content:e[7].body}}),{key:o,first:null,c(){l=a("div"),Pe(s.$$.fragment),b=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(c,f){r(c,l,f),Re(s,l,null),n(l,b),p=!0},p(c,f){e=c;const y={};f&4&&(y.content=e[7].body),s.$set(y),(!p||f&6)&&ue(l,"active",e[1]===e[7].code)},i(c){p||(fe(s.$$.fragment,c),p=!0)},o(c){pe(s.$$.fragment,c),p=!1},d(c){c&&d(l),Be(s)}}}function Xt(o){var st,it,at,ot,rt,dt,ct,ft;let e,l,s=o[0].name+"",b,p,c,f,y,T,w,O=o[0].name+"",g,D,V,L,I,j,B,S,N,q,C,_,M,z,P,u,$,ee,K=o[0].name+"",me,Fe,ge,be,ne,_e,W,ke,je,J,ye,De,ve,E=[],Ne=new Map,he,se,we,Y,Ce,Ve,Se,G,$e,Ie,Te,Je,A,Ee,te,Ue,Qe,ze,qe,Ke,Oe,We,Ye,Ge,Me,Xe,Ae,ie,He,X,ae,U=[],Ze=new Map,xe,oe,F=[],et=new Map,Z;S=new Nt({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${o[4]}');
@@ -46,12 +46,12 @@ final record = await pb.collection('${(ot=o[0])==null?void 0:ot.name}').create(b
`+((rt=o[0])!=null&&rt.isAuth?`
// (optional) send an email verification request
await pb.collection('${(dt=o[0])==null?void 0:dt.name}').requestVerification('test@example.com');
`:"")}});let B=o[5]&&$t(),H=((ct=o[0])==null?void 0:ct.isAuth)&&Tt(o),de=(ft=o[0])==null?void 0:ft.schema;const tt=t=>t[12].name;for(let t=0;t<de.length;t+=1){let i=St(o,de,t),v=tt(i);Ne.set(v,E[t]=qt(v,i))}te=new At({props:{content:"?expand=relField1,relField2.subRelField"}});let ce=o[2];const lt=t=>t[7].code;for(let t=0;t<ce.length;t+=1){let i=Ct(o,ce,t),v=lt(i);Ze.set(v,U[t]=Mt(v,i))}let re=o[2];const nt=t=>t[7].code;for(let t=0;t<re.length;t+=1){let i=wt(o,re,t),v=nt(i);et.set(v,R[t]=Ot(v,i))}return{c(){e=a("h3"),l=k("Create ("),b=k(s),p=k(")"),c=m(),f=a("div"),y=a("p"),T=k("Create a new "),w=a("strong"),g=k(M),D=k(" record."),V=m(),L=a("p"),L.innerHTML=`Body parameters could be sent as <code>application/json</code> or
`:"")}});let R=o[5]&&$t(),H=((ct=o[0])==null?void 0:ct.isAuth)&&Tt(o),de=(ft=o[0])==null?void 0:ft.schema;const tt=t=>t[12].name;for(let t=0;t<de.length;t+=1){let i=St(o,de,t),v=tt(i);Ne.set(v,E[t]=qt(v,i))}te=new At({props:{content:"?expand=relField1,relField2.subRelField"}});let ce=o[2];const lt=t=>t[7].code;for(let t=0;t<ce.length;t+=1){let i=Ct(o,ce,t),v=lt(i);Ze.set(v,U[t]=Ot(v,i))}let re=o[2];const nt=t=>t[7].code;for(let t=0;t<re.length;t+=1){let i=wt(o,re,t),v=nt(i);et.set(v,F[t]=Mt(v,i))}return{c(){e=a("h3"),l=k("Create ("),b=k(s),p=k(")"),c=m(),f=a("div"),y=a("p"),T=k("Create a new "),w=a("strong"),g=k(O),D=k(" record."),V=m(),L=a("p"),L.innerHTML=`Body parameters could be sent as <code>application/json</code> or
<code>multipart/form-data</code>.`,I=m(),j=a("p"),j.innerHTML=`File upload is supported only via <code>multipart/form-data</code>.
<br/>
For more info and examples you could check the detailed
<a href="https://pocketbase.io/docs/files-handling/" target="_blank" rel="noopener noreferrer">Files upload and handling docs
</a>.`,F=m(),Pe(S.$$.fragment),N=m(),q=a("h6"),q.textContent="API details",C=m(),_=a("div"),O=a("strong"),O.textContent="POST",z=m(),P=a("div"),u=a("p"),$=k("/api/collections/"),ee=a("strong"),me=k(K),Re=k("/records"),ge=m(),B&&B.c(),be=m(),ne=a("div"),ne.textContent="Body Parameters",_e=m(),W=a("table"),ke=a("thead"),ke.innerHTML=`<tr><th>Param</th>
</a>.`,B=m(),Pe(S.$$.fragment),N=m(),q=a("h6"),q.textContent="API details",C=m(),_=a("div"),M=a("strong"),M.textContent="POST",z=m(),P=a("div"),u=a("p"),$=k("/api/collections/"),ee=a("strong"),me=k(K),Fe=k("/records"),ge=m(),R&&R.c(),be=m(),ne=a("div"),ne.textContent="Body Parameters",_e=m(),W=a("table"),ke=a("thead"),ke.innerHTML=`<tr><th>Param</th>
<th>Type</th>
<th width="50%">Description</th></tr>`,je=m(),J=a("tbody"),ye=a("tr"),ye.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
<span>id</span></div></td>
@@ -64,9 +64,9 @@ await pb.collection('${(dt=o[0])==null?void 0:dt.name}').requestVerification('te
`),Pe(te.$$.fragment),Ue=k(`
Supports up to 6-levels depth nested relations expansion. `),Qe=a("br"),ze=k(`
The expanded relations will be appended to the record under the
`),qe=a("code"),qe.textContent="expand",Ke=k(" property (eg. "),Me=a("code"),Me.textContent='"expand": {"relField1": {...}, ...}',We=k(`).
`),qe=a("code"),qe.textContent="expand",Ke=k(" property (eg. "),Oe=a("code"),Oe.textContent='"expand": {"relField1": {...}, ...}',We=k(`).
`),Ye=a("br"),Ge=k(`
Only the relations to which the request user has permissions to `),Oe=a("strong"),Oe.textContent="view",Xe=k(" will be expanded."),Ae=m(),ie=a("div"),ie.textContent="Responses",He=m(),X=a("div"),ae=a("div");for(let t=0;t<U.length;t+=1)U[t].c();xe=m(),oe=a("div");for(let t=0;t<R.length;t+=1)R[t].c();h(e,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(q,"class","m-b-xs"),h(O,"class","label label-primary"),h(P,"class","content"),h(_,"class","alert alert-success"),h(ne,"class","section-title"),h(W,"class","table-compact table-border m-b-base"),h(se,"class","section-title"),h(Y,"class","table-compact table-border m-b-base"),h(ie,"class","section-title"),h(ae,"class","tabs-header compact left"),h(oe,"class","tabs-content"),h(X,"class","tabs")},m(t,i){r(t,e,i),n(e,l),n(e,b),n(e,p),r(t,c,i),r(t,f,i),n(f,y),n(y,T),n(y,w),n(w,g),n(y,D),n(f,V),n(f,L),n(f,I),n(f,j),r(t,F,i),Be(S,t,i),r(t,N,i),r(t,q,i),r(t,C,i),r(t,_,i),n(_,O),n(_,z),n(_,P),n(P,u),n(u,$),n(u,ee),n(ee,me),n(u,Re),n(_,ge),B&&B.m(_,null),r(t,be,i),r(t,ne,i),r(t,_e,i),r(t,W,i),n(W,ke),n(W,je),n(W,J),n(J,ye),n(J,De),H&&H.m(J,null),n(J,ve);for(let v=0;v<E.length;v+=1)E[v].m(J,null);r(t,he,i),r(t,se,i),r(t,we,i),r(t,Y,i),n(Y,Ce),n(Y,Ve),n(Y,Se),n(Se,G),n(G,$e),n(G,Ie),n(G,Te),n(G,Je),n(G,A),n(A,Ee),Be(te,A,null),n(A,Ue),n(A,Qe),n(A,ze),n(A,qe),n(A,Ke),n(A,Me),n(A,We),n(A,Ye),n(A,Ge),n(A,Oe),n(A,Xe),r(t,Ae,i),r(t,ie,i),r(t,He,i),r(t,X,i),n(X,ae);for(let v=0;v<U.length;v+=1)U[v].m(ae,null);n(X,xe),n(X,oe);for(let v=0;v<R.length;v+=1)R[v].m(oe,null);Z=!0},p(t,[i]){var pt,ut,mt,bt,_t,kt,yt,vt;(!Z||i&1)&&s!==(s=t[0].name+"")&&x(b,s),(!Z||i&1)&&M!==(M=t[0].name+"")&&x(g,M);const v={};i&25&&(v.js=`
Only the relations to which the request user has permissions to `),Me=a("strong"),Me.textContent="view",Xe=k(" will be expanded."),Ae=m(),ie=a("div"),ie.textContent="Responses",He=m(),X=a("div"),ae=a("div");for(let t=0;t<U.length;t+=1)U[t].c();xe=m(),oe=a("div");for(let t=0;t<F.length;t+=1)F[t].c();h(e,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(q,"class","m-b-xs"),h(M,"class","label label-primary"),h(P,"class","content"),h(_,"class","alert alert-success"),h(ne,"class","section-title"),h(W,"class","table-compact table-border m-b-base"),h(se,"class","section-title"),h(Y,"class","table-compact table-border m-b-base"),h(ie,"class","section-title"),h(ae,"class","tabs-header compact left"),h(oe,"class","tabs-content"),h(X,"class","tabs")},m(t,i){r(t,e,i),n(e,l),n(e,b),n(e,p),r(t,c,i),r(t,f,i),n(f,y),n(y,T),n(y,w),n(w,g),n(y,D),n(f,V),n(f,L),n(f,I),n(f,j),r(t,B,i),Re(S,t,i),r(t,N,i),r(t,q,i),r(t,C,i),r(t,_,i),n(_,M),n(_,z),n(_,P),n(P,u),n(u,$),n(u,ee),n(ee,me),n(u,Fe),n(_,ge),R&&R.m(_,null),r(t,be,i),r(t,ne,i),r(t,_e,i),r(t,W,i),n(W,ke),n(W,je),n(W,J),n(J,ye),n(J,De),H&&H.m(J,null),n(J,ve);for(let v=0;v<E.length;v+=1)E[v].m(J,null);r(t,he,i),r(t,se,i),r(t,we,i),r(t,Y,i),n(Y,Ce),n(Y,Ve),n(Y,Se),n(Se,G),n(G,$e),n(G,Ie),n(G,Te),n(G,Je),n(G,A),n(A,Ee),Re(te,A,null),n(A,Ue),n(A,Qe),n(A,ze),n(A,qe),n(A,Ke),n(A,Oe),n(A,We),n(A,Ye),n(A,Ge),n(A,Me),n(A,Xe),r(t,Ae,i),r(t,ie,i),r(t,He,i),r(t,X,i),n(X,ae);for(let v=0;v<U.length;v+=1)U[v].m(ae,null);n(X,xe),n(X,oe);for(let v=0;v<F.length;v+=1)F[v].m(oe,null);Z=!0},p(t,[i]){var pt,ut,mt,bt,_t,kt,yt,vt;(!Z||i&1)&&s!==(s=t[0].name+"")&&x(b,s),(!Z||i&1)&&O!==(O=t[0].name+"")&&x(g,O);const v={};i&25&&(v.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${t[4]}');
@@ -94,12 +94,12 @@ final record = await pb.collection('${(bt=t[0])==null?void 0:bt.name}').create(b
`+((_t=t[0])!=null&&_t.isAuth?`
// (optional) send an email verification request
await pb.collection('${(kt=t[0])==null?void 0:kt.name}').requestVerification('test@example.com');
`:"")),S.$set(v),(!Z||i&1)&&K!==(K=t[0].name+"")&&x(me,K),t[5]?B||(B=$t(),B.c(),B.m(_,null)):B&&(B.d(1),B=null),(yt=t[0])!=null&&yt.isAuth?H?H.p(t,i):(H=Tt(t),H.c(),H.m(J,ve)):H&&(H.d(1),H=null),i&1&&(de=(vt=t[0])==null?void 0:vt.schema,E=Le(E,i,tt,1,t,de,Ne,J,ht,qt,null,St)),i&6&&(ce=t[2],U=Le(U,i,lt,1,t,ce,Ze,ae,ht,Mt,null,Ct)),i&6&&(re=t[2],Bt(),R=Le(R,i,nt,1,t,re,et,oe,Ft,Ot,null,wt),Rt())},i(t){if(!Z){fe(S.$$.fragment,t),fe(te.$$.fragment,t);for(let i=0;i<re.length;i+=1)fe(R[i]);Z=!0}},o(t){pe(S.$$.fragment,t),pe(te.$$.fragment,t);for(let i=0;i<R.length;i+=1)pe(R[i]);Z=!1},d(t){t&&d(e),t&&d(c),t&&d(f),t&&d(F),Fe(S,t),t&&d(N),t&&d(q),t&&d(C),t&&d(_),B&&B.d(),t&&d(be),t&&d(ne),t&&d(_e),t&&d(W),H&&H.d();for(let i=0;i<E.length;i+=1)E[i].d();t&&d(he),t&&d(se),t&&d(we),t&&d(Y),Fe(te),t&&d(Ae),t&&d(ie),t&&d(He),t&&d(X);for(let i=0;i<U.length;i+=1)U[i].d();for(let i=0;i<R.length;i+=1)R[i].d()}}}function Zt(o,e,l){let s,b,{collection:p=new gt}=e,c=200,f=[],y={};const T=w=>l(1,c=w.code);return o.$$set=w=>{"collection"in w&&l(0,p=w.collection)},o.$$.update=()=>{var w,M;o.$$.dirty&1&&l(5,s=(p==null?void 0:p.createRule)===null),o.$$.dirty&1&&l(2,f=[{code:200,body:JSON.stringify(Q.dummyCollectionRecord(p),null,2)},{code:400,body:`
`:"")),S.$set(v),(!Z||i&1)&&K!==(K=t[0].name+"")&&x(me,K),t[5]?R||(R=$t(),R.c(),R.m(_,null)):R&&(R.d(1),R=null),(yt=t[0])!=null&&yt.isAuth?H?H.p(t,i):(H=Tt(t),H.c(),H.m(J,ve)):H&&(H.d(1),H=null),i&1&&(de=(vt=t[0])==null?void 0:vt.schema,E=Le(E,i,tt,1,t,de,Ne,J,ht,qt,null,St)),i&6&&(ce=t[2],U=Le(U,i,lt,1,t,ce,Ze,ae,ht,Ot,null,Ct)),i&6&&(re=t[2],Rt(),F=Le(F,i,nt,1,t,re,et,oe,Bt,Mt,null,wt),Ft())},i(t){if(!Z){fe(S.$$.fragment,t),fe(te.$$.fragment,t);for(let i=0;i<re.length;i+=1)fe(F[i]);Z=!0}},o(t){pe(S.$$.fragment,t),pe(te.$$.fragment,t);for(let i=0;i<F.length;i+=1)pe(F[i]);Z=!1},d(t){t&&d(e),t&&d(c),t&&d(f),t&&d(B),Be(S,t),t&&d(N),t&&d(q),t&&d(C),t&&d(_),R&&R.d(),t&&d(be),t&&d(ne),t&&d(_e),t&&d(W),H&&H.d();for(let i=0;i<E.length;i+=1)E[i].d();t&&d(he),t&&d(se),t&&d(we),t&&d(Y),Be(te),t&&d(Ae),t&&d(ie),t&&d(He),t&&d(X);for(let i=0;i<U.length;i+=1)U[i].d();for(let i=0;i<F.length;i+=1)F[i].d()}}}function Zt(o,e,l){let s,b,{collection:p=new gt}=e,c=200,f=[],y={};const T=w=>l(1,c=w.code);return o.$$set=w=>{"collection"in w&&l(0,p=w.collection)},o.$$.update=()=>{var w,O;o.$$.dirty&1&&l(5,s=(p==null?void 0:p.createRule)===null),o.$$.dirty&1&&l(2,f=[{code:200,body:JSON.stringify(Q.dummyCollectionRecord(p),null,2)},{code:400,body:`
{
"code": 400,
"message": "Failed to create record.",
"data": {
"${(M=(w=p==null?void 0:p.schema)==null?void 0:w[0])==null?void 0:M.name}": {
"${(O=(w=p==null?void 0:p.schema)==null?void 0:w[0])==null?void 0:O.name}": {
"code": "validation_required",
"message": "Missing required value."
}
@@ -1,4 +1,4 @@
import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n,m as we,x,N as _e,O as Ee,k as Oe,P as Te,n as Be,t as ee,a as te,o as f,d as ge,Q as Ie,C as Me,p as Ae,r as N,u as Se,M as qe}from"./index.2a48c906.js";import{S as He}from"./SdkTabs.9dbb2ca3.js";function ke(o,l,s){const a=o.slice();return a[6]=l[s],a}function he(o,l,s){const a=o.slice();return a[6]=l[s],a}function ve(o){let l;return{c(){l=c("p"),l.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,a){d(s,l,a)},d(s){s&&f(l)}}}function ye(o,l){let s,a=l[6].code+"",h,i,r,u;function $(){return l[5](l[6])}return{key:o,first:null,c(){s=c("button"),h=D(a),i=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(b,g){d(b,s,g),n(s,h),n(s,i),r||(u=Se(s,"click",$),r=!0)},p(b,g){l=b,g&20&&N(s,"active",l[2]===l[6].code)},d(b){b&&f(s),r=!1,u()}}}function De(o,l){let s,a,h,i;return a=new qe({props:{content:l[6].body}}),{key:o,first:null,c(){s=c("div"),$e(a.$$.fragment),h=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(r,u){d(r,s,u),we(a,s,null),n(s,h),i=!0},p(r,u){l=r,(!i||u&20)&&N(s,"active",l[2]===l[6].code)},i(r){i||(ee(a.$$.fragment,r),i=!0)},o(r){te(a.$$.fragment,r),i=!1},d(r){r&&f(s),ge(a)}}}function Le(o){var ue,pe;let l,s,a=o[0].name+"",h,i,r,u,$,b,g,q=o[0].name+"",z,le,F,C,K,O,Q,y,H,se,L,E,oe,G,U=o[0].name+"",J,ae,V,ne,W,T,X,B,Y,I,Z,R,M,w=[],ie=new Map,re,A,v=[],ce=new Map,P;C=new He({props:{js:`
import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n,m as we,x,O as _e,P as Ee,k as Oe,Q as Te,n as Be,t as ee,a as te,o as f,d as ge,R as Ie,C as Ae,p as Me,r as N,u as Se,N as qe}from"./index.536fddcb.js";import{S as He}from"./SdkTabs.3571c26b.js";function ke(o,l,s){const a=o.slice();return a[6]=l[s],a}function he(o,l,s){const a=o.slice();return a[6]=l[s],a}function ve(o){let l;return{c(){l=c("p"),l.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,a){d(s,l,a)},d(s){s&&f(l)}}}function ye(o,l){let s,a=l[6].code+"",h,i,r,u;function $(){return l[5](l[6])}return{key:o,first:null,c(){s=c("button"),h=D(a),i=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(b,g){d(b,s,g),n(s,h),n(s,i),r||(u=Se(s,"click",$),r=!0)},p(b,g){l=b,g&20&&N(s,"active",l[2]===l[6].code)},d(b){b&&f(s),r=!1,u()}}}function De(o,l){let s,a,h,i;return a=new qe({props:{content:l[6].body}}),{key:o,first:null,c(){s=c("div"),$e(a.$$.fragment),h=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(r,u){d(r,s,u),we(a,s,null),n(s,h),i=!0},p(r,u){l=r,(!i||u&20)&&N(s,"active",l[2]===l[6].code)},i(r){i||(ee(a.$$.fragment,r),i=!0)},o(r){te(a.$$.fragment,r),i=!1},d(r){r&&f(s),ge(a)}}}function Le(o){var ue,pe;let l,s,a=o[0].name+"",h,i,r,u,$,b,g,q=o[0].name+"",z,le,F,C,K,O,Q,y,H,se,L,E,oe,G,U=o[0].name+"",J,ae,V,ne,W,T,X,B,Y,I,Z,R,A,w=[],ie=new Map,re,M,v=[],ce=new Map,P;C=new He({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${o[3]}');
@@ -19,7 +19,7 @@ import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n
<th width="60%">Description</th></tr></thead>
<tbody><tr><td>id</td>
<td><span class="label">String</span></td>
<td>ID of the record to delete.</td></tr></tbody>`,Y=k(),I=c("div"),I.textContent="Responses",Z=k(),R=c("div"),M=c("div");for(let e=0;e<w.length;e+=1)w[e].c();re=k(),A=c("div");for(let e=0;e<v.length;e+=1)v[e].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(O,"class","m-b-xs"),m(H,"class","label label-primary"),m(L,"class","content"),m(y,"class","alert alert-danger"),m(T,"class","section-title"),m(B,"class","table-compact table-border m-b-base"),m(I,"class","section-title"),m(M,"class","tabs-header compact left"),m(A,"class","tabs-content"),m(R,"class","tabs")},m(e,t){d(e,l,t),n(l,s),n(l,h),n(l,i),d(e,r,t),d(e,u,t),n(u,$),n($,b),n($,g),n(g,z),n($,le),d(e,F,t),we(C,e,t),d(e,K,t),d(e,O,t),d(e,Q,t),d(e,y,t),n(y,H),n(y,se),n(y,L),n(L,E),n(E,oe),n(E,G),n(G,J),n(E,ae),n(E,V),n(y,ne),_&&_.m(y,null),d(e,W,t),d(e,T,t),d(e,X,t),d(e,B,t),d(e,Y,t),d(e,I,t),d(e,Z,t),d(e,R,t),n(R,M);for(let p=0;p<w.length;p+=1)w[p].m(M,null);n(R,re),n(R,A);for(let p=0;p<v.length;p+=1)v[p].m(A,null);P=!0},p(e,[t]){var me,be;(!P||t&1)&&a!==(a=e[0].name+"")&&x(h,a),(!P||t&1)&&q!==(q=e[0].name+"")&&x(z,q);const p={};t&9&&(p.js=`
<td>ID of the record to delete.</td></tr></tbody>`,Y=k(),I=c("div"),I.textContent="Responses",Z=k(),R=c("div"),A=c("div");for(let e=0;e<w.length;e+=1)w[e].c();re=k(),M=c("div");for(let e=0;e<v.length;e+=1)v[e].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(O,"class","m-b-xs"),m(H,"class","label label-primary"),m(L,"class","content"),m(y,"class","alert alert-danger"),m(T,"class","section-title"),m(B,"class","table-compact table-border m-b-base"),m(I,"class","section-title"),m(A,"class","tabs-header compact left"),m(M,"class","tabs-content"),m(R,"class","tabs")},m(e,t){d(e,l,t),n(l,s),n(l,h),n(l,i),d(e,r,t),d(e,u,t),n(u,$),n($,b),n($,g),n(g,z),n($,le),d(e,F,t),we(C,e,t),d(e,K,t),d(e,O,t),d(e,Q,t),d(e,y,t),n(y,H),n(y,se),n(y,L),n(L,E),n(E,oe),n(E,G),n(G,J),n(E,ae),n(E,V),n(y,ne),_&&_.m(y,null),d(e,W,t),d(e,T,t),d(e,X,t),d(e,B,t),d(e,Y,t),d(e,I,t),d(e,Z,t),d(e,R,t),n(R,A);for(let p=0;p<w.length;p+=1)w[p].m(A,null);n(R,re),n(R,M);for(let p=0;p<v.length;p+=1)v[p].m(M,null);P=!0},p(e,[t]){var me,be;(!P||t&1)&&a!==(a=e[0].name+"")&&x(h,a),(!P||t&1)&&q!==(q=e[0].name+"")&&x(z,q);const p={};t&9&&(p.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -35,7 +35,7 @@ import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n
...
await pb.collection('${(be=e[0])==null?void 0:be.name}').delete('RECORD_ID');
`),C.$set(p),(!P||t&1)&&U!==(U=e[0].name+"")&&x(J,U),e[1]?_||(_=ve(),_.c(),_.m(y,null)):_&&(_.d(1),_=null),t&20&&(j=e[4],w=_e(w,t,de,1,e,j,ie,M,Ee,ye,null,he)),t&20&&(S=e[4],Oe(),v=_e(v,t,fe,1,e,S,ce,A,Te,De,null,ke),Be())},i(e){if(!P){ee(C.$$.fragment,e);for(let t=0;t<S.length;t+=1)ee(v[t]);P=!0}},o(e){te(C.$$.fragment,e);for(let t=0;t<v.length;t+=1)te(v[t]);P=!1},d(e){e&&f(l),e&&f(r),e&&f(u),e&&f(F),ge(C,e),e&&f(K),e&&f(O),e&&f(Q),e&&f(y),_&&_.d(),e&&f(W),e&&f(T),e&&f(X),e&&f(B),e&&f(Y),e&&f(I),e&&f(Z),e&&f(R);for(let t=0;t<w.length;t+=1)w[t].d();for(let t=0;t<v.length;t+=1)v[t].d()}}}function Ue(o,l,s){let a,h,{collection:i=new Ie}=l,r=204,u=[];const $=b=>s(2,r=b.code);return o.$$set=b=>{"collection"in b&&s(0,i=b.collection)},o.$$.update=()=>{o.$$.dirty&1&&s(1,a=(i==null?void 0:i.deleteRule)===null),o.$$.dirty&3&&i!=null&&i.id&&(u.push({code:204,body:`
`),C.$set(p),(!P||t&1)&&U!==(U=e[0].name+"")&&x(J,U),e[1]?_||(_=ve(),_.c(),_.m(y,null)):_&&(_.d(1),_=null),t&20&&(j=e[4],w=_e(w,t,de,1,e,j,ie,A,Ee,ye,null,he)),t&20&&(S=e[4],Oe(),v=_e(v,t,fe,1,e,S,ce,M,Te,De,null,ke),Be())},i(e){if(!P){ee(C.$$.fragment,e);for(let t=0;t<S.length;t+=1)ee(v[t]);P=!0}},o(e){te(C.$$.fragment,e);for(let t=0;t<v.length;t+=1)te(v[t]);P=!1},d(e){e&&f(l),e&&f(r),e&&f(u),e&&f(F),ge(C,e),e&&f(K),e&&f(O),e&&f(Q),e&&f(y),_&&_.d(),e&&f(W),e&&f(T),e&&f(X),e&&f(B),e&&f(Y),e&&f(I),e&&f(Z),e&&f(R);for(let t=0;t<w.length;t+=1)w[t].d();for(let t=0;t<v.length;t+=1)v[t].d()}}}function Ue(o,l,s){let a,h,{collection:i=new Ie}=l,r=204,u=[];const $=b=>s(2,r=b.code);return o.$$set=b=>{"collection"in b&&s(0,i=b.collection)},o.$$.update=()=>{o.$$.dirty&1&&s(1,a=(i==null?void 0:i.deleteRule)===null),o.$$.dirty&3&&i!=null&&i.id&&(u.push({code:204,body:`
null
`}),u.push({code:400,body:`
{
@@ -55,4 +55,4 @@ import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n
"message": "The requested resource wasn't found.",
"data": {}
}
`}))},s(3,h=Me.getApiExampleUrl(Ae.baseUrl)),[i,a,r,h,u,$]}class ze extends Ce{constructor(l){super(),Re(this,l,Ue,Le,Pe,{collection:0})}}export{ze as default};
`}))},s(3,h=Ae.getApiExampleUrl(Me.baseUrl)),[i,a,r,h,u,$]}class ze extends Ce{constructor(l){super(),Re(this,l,Ue,Le,Pe,{collection:0})}}export{ze as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+136
View File
@@ -0,0 +1,136 @@
import{S as Se,i as Ne,s as qe,e,b as s,E as He,f as o,g as u,u as De,y as Fe,o as m,w as _,h as t,N as he,c as Yt,m as Zt,x as we,O as Le,P as Me,k as Ie,Q as Be,n as Ge,t as It,a as Bt,d as te,R as ze,C as _e,p as Ue,r as xe}from"./index.536fddcb.js";import{S as je}from"./SdkTabs.3571c26b.js";function Qe(d){let n,a,r;return{c(){n=e("span"),n.textContent="Show details",a=s(),r=e("i"),o(n,"class","txt"),o(r,"class","ri-arrow-down-s-line")},m(f,p){u(f,n,p),u(f,a,p),u(f,r,p)},d(f){f&&m(n),f&&m(a),f&&m(r)}}}function Je(d){let n,a,r;return{c(){n=e("span"),n.textContent="Hide details",a=s(),r=e("i"),o(n,"class","txt"),o(r,"class","ri-arrow-up-s-line")},m(f,p){u(f,n,p),u(f,a,p),u(f,r,p)},d(f){f&&m(n),f&&m(a),f&&m(r)}}}function Ae(d){let n,a,r,f,p,b,x,$,h,w,c,V,bt,Gt,R,zt,q,it,F,W,ee,B,G,le,at,ht,X,xt,se,rt,ct,Y,O,Ut,wt,v,Z,_t,jt,$t,z,tt,Ct,Qt,kt,L,dt,gt,ne,ft,oe,D,yt,et,vt,U,pt,ie,H,Ft,lt,Lt,st,At,nt,j,E,Jt,Tt,Kt,Pt,C,Q,M,ae,Rt,re,ut,ce,I,Ot,de,Et,Vt,St,Wt,A,mt,J,K,S,Nt,fe,T,k,pe,N,y,ot,ue,P,qt,me,Dt,be,Ht,Xt,Mt;return{c(){n=e("p"),n.innerHTML=`The syntax basically follows the format
<code><span class="txt-success">OPERAND</span>
<span class="txt-danger">OPERATOR</span>
<span class="txt-success">OPERAND</span></code>, where:`,a=s(),r=e("ul"),f=e("li"),f.innerHTML=`<code class="txt-success">OPERAND</code> - could be any of the above field literal, string (single
or double quoted), number, null, true, false`,p=s(),b=e("li"),x=e("code"),x.textContent="OPERATOR",$=_(` - is one of:
`),h=e("br"),w=s(),c=e("ul"),V=e("li"),bt=e("code"),bt.textContent="=",Gt=s(),R=e("span"),R.textContent="Equal",zt=s(),q=e("li"),it=e("code"),it.textContent="!=",F=s(),W=e("span"),W.textContent="NOT equal",ee=s(),B=e("li"),G=e("code"),G.textContent=">",le=s(),at=e("span"),at.textContent="Greater than",ht=s(),X=e("li"),xt=e("code"),xt.textContent=">=",se=s(),rt=e("span"),rt.textContent="Greater than or equal",ct=s(),Y=e("li"),O=e("code"),O.textContent="<",Ut=s(),wt=e("span"),wt.textContent="Less than or equal",v=s(),Z=e("li"),_t=e("code"),_t.textContent="<=",jt=s(),$t=e("span"),$t.textContent="Less than or equal",z=s(),tt=e("li"),Ct=e("code"),Ct.textContent="~",Qt=s(),kt=e("span"),kt.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
wildcard match)`,L=s(),dt=e("li"),gt=e("code"),gt.textContent="!~",ne=s(),ft=e("span"),ft.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
wildcard match)`,oe=s(),D=e("li"),yt=e("code"),yt.textContent="?=",et=s(),vt=e("em"),vt.textContent="Any/At least one of",U=s(),pt=e("span"),pt.textContent="Equal",ie=s(),H=e("li"),Ft=e("code"),Ft.textContent="?!=",lt=s(),Lt=e("em"),Lt.textContent="Any/At least one of",st=s(),At=e("span"),At.textContent="NOT equal",nt=s(),j=e("li"),E=e("code"),E.textContent="?>",Jt=s(),Tt=e("em"),Tt.textContent="Any/At least one of",Kt=s(),Pt=e("span"),Pt.textContent="Greater than",C=s(),Q=e("li"),M=e("code"),M.textContent="?>=",ae=s(),Rt=e("em"),Rt.textContent="Any/At least one of",re=s(),ut=e("span"),ut.textContent="Greater than or equal",ce=s(),I=e("li"),Ot=e("code"),Ot.textContent="?<",de=s(),Et=e("em"),Et.textContent="Any/At least one of",Vt=s(),St=e("span"),St.textContent="Less than or equal",Wt=s(),A=e("li"),mt=e("code"),mt.textContent="?<=",J=s(),K=e("em"),K.textContent="Any/At least one of",S=s(),Nt=e("span"),Nt.textContent="Less than or equal",fe=s(),T=e("li"),k=e("code"),k.textContent="?~",pe=s(),N=e("em"),N.textContent="Any/At least one of",y=s(),ot=e("span"),ot.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
wildcard match)`,ue=s(),P=e("li"),qt=e("code"),qt.textContent="?!~",me=s(),Dt=e("em"),Dt.textContent="Any/At least one of",be=s(),Ht=e("span"),Ht.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
wildcard match)`,Xt=s(),Mt=e("p"),Mt.innerHTML=`To group and combine several expressions you could use brackets
<code>(...)</code>, <code>&amp;&amp;</code> (AND) and <code>||</code> (OR) tokens.`,o(x,"class","txt-danger"),o(bt,"class","filter-op svelte-1w7s5nw"),o(R,"class","txt"),o(it,"class","filter-op svelte-1w7s5nw"),o(W,"class","txt"),o(G,"class","filter-op svelte-1w7s5nw"),o(at,"class","txt"),o(xt,"class","filter-op svelte-1w7s5nw"),o(rt,"class","txt"),o(O,"class","filter-op svelte-1w7s5nw"),o(wt,"class","txt"),o(_t,"class","filter-op svelte-1w7s5nw"),o($t,"class","txt"),o(Ct,"class","filter-op svelte-1w7s5nw"),o(kt,"class","txt"),o(gt,"class","filter-op svelte-1w7s5nw"),o(ft,"class","txt"),o(yt,"class","filter-op svelte-1w7s5nw"),o(vt,"class","txt-hint"),o(pt,"class","txt"),o(Ft,"class","filter-op svelte-1w7s5nw"),o(Lt,"class","txt-hint"),o(At,"class","txt"),o(E,"class","filter-op svelte-1w7s5nw"),o(Tt,"class","txt-hint"),o(Pt,"class","txt"),o(M,"class","filter-op svelte-1w7s5nw"),o(Rt,"class","txt-hint"),o(ut,"class","txt"),o(Ot,"class","filter-op svelte-1w7s5nw"),o(Et,"class","txt-hint"),o(St,"class","txt"),o(mt,"class","filter-op svelte-1w7s5nw"),o(K,"class","txt-hint"),o(Nt,"class","txt"),o(k,"class","filter-op svelte-1w7s5nw"),o(N,"class","txt-hint"),o(ot,"class","txt"),o(qt,"class","filter-op svelte-1w7s5nw"),o(Dt,"class","txt-hint"),o(Ht,"class","txt")},m(l,i){u(l,n,i),u(l,a,i),u(l,r,i),t(r,f),t(r,p),t(r,b),t(b,x),t(b,$),t(b,h),t(b,w),t(b,c),t(c,V),t(V,bt),t(V,Gt),t(V,R),t(c,zt),t(c,q),t(q,it),t(q,F),t(q,W),t(c,ee),t(c,B),t(B,G),t(B,le),t(B,at),t(c,ht),t(c,X),t(X,xt),t(X,se),t(X,rt),t(c,ct),t(c,Y),t(Y,O),t(Y,Ut),t(Y,wt),t(c,v),t(c,Z),t(Z,_t),t(Z,jt),t(Z,$t),t(c,z),t(c,tt),t(tt,Ct),t(tt,Qt),t(tt,kt),t(c,L),t(c,dt),t(dt,gt),t(dt,ne),t(dt,ft),t(c,oe),t(c,D),t(D,yt),t(D,et),t(D,vt),t(D,U),t(D,pt),t(c,ie),t(c,H),t(H,Ft),t(H,lt),t(H,Lt),t(H,st),t(H,At),t(c,nt),t(c,j),t(j,E),t(j,Jt),t(j,Tt),t(j,Kt),t(j,Pt),t(c,C),t(c,Q),t(Q,M),t(Q,ae),t(Q,Rt),t(Q,re),t(Q,ut),t(c,ce),t(c,I),t(I,Ot),t(I,de),t(I,Et),t(I,Vt),t(I,St),t(c,Wt),t(c,A),t(A,mt),t(A,J),t(A,K),t(A,S),t(A,Nt),t(c,fe),t(c,T),t(T,k),t(T,pe),t(T,N),t(T,y),t(T,ot),t(c,ue),t(c,P),t(P,qt),t(P,me),t(P,Dt),t(P,be),t(P,Ht),u(l,Xt,i),u(l,Mt,i)},d(l){l&&m(n),l&&m(a),l&&m(r),l&&m(Xt),l&&m(Mt)}}}function Ke(d){let n,a,r,f,p;function b(w,c){return w[0]?Je:Qe}let x=b(d),$=x(d),h=d[0]&&Ae();return{c(){n=e("button"),$.c(),a=s(),h&&h.c(),r=He(),o(n,"class","btn btn-sm btn-secondary m-t-5")},m(w,c){u(w,n,c),$.m(n,null),u(w,a,c),h&&h.m(w,c),u(w,r,c),f||(p=De(n,"click",d[1]),f=!0)},p(w,[c]){x!==(x=b(w))&&($.d(1),$=x(w),$&&($.c(),$.m(n,null))),w[0]?h||(h=Ae(),h.c(),h.m(r.parentNode,r)):h&&(h.d(1),h=null)},i:Fe,o:Fe,d(w){w&&m(n),$.d(),w&&m(a),h&&h.d(w),w&&m(r),f=!1,p()}}}function Ve(d,n,a){let r=!1;function f(){a(0,r=!r)}return[r,f]}class We extends Se{constructor(n){super(),Ne(this,n,Ve,Ke,qe,{})}}function Te(d,n,a){const r=d.slice();return r[6]=n[a],r}function Pe(d,n,a){const r=d.slice();return r[6]=n[a],r}function Re(d){let n;return{c(){n=e("p"),n.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",o(n,"class","txt-hint txt-sm txt-right")},m(a,r){u(a,n,r)},d(a){a&&m(n)}}}function Oe(d,n){let a,r=n[6].code+"",f,p,b,x;function $(){return n[5](n[6])}return{key:d,first:null,c(){a=e("div"),f=_(r),p=s(),o(a,"class","tab-item"),xe(a,"active",n[2]===n[6].code),this.first=a},m(h,w){u(h,a,w),t(a,f),t(a,p),b||(x=De(a,"click",$),b=!0)},p(h,w){n=h,w&20&&xe(a,"active",n[2]===n[6].code)},d(h){h&&m(a),b=!1,x()}}}function Ee(d,n){let a,r,f,p;return r=new he({props:{content:n[6].body}}),{key:d,first:null,c(){a=e("div"),Yt(r.$$.fragment),f=s(),o(a,"class","tab-item"),xe(a,"active",n[2]===n[6].code),this.first=a},m(b,x){u(b,a,x),Zt(r,a,null),t(a,f),p=!0},p(b,x){n=b,(!p||x&20)&&xe(a,"active",n[2]===n[6].code)},i(b){p||(It(r.$$.fragment,b),p=!0)},o(b){Bt(r.$$.fragment,b),p=!1},d(b){b&&m(a),te(r)}}}function Xe(d){var me,Dt,be,Ht,Xt,Mt;let n,a,r=d[0].name+"",f,p,b,x,$,h,w,c=d[0].name+"",V,bt,Gt,R,zt,q,it,F,W,ee,B,G,le,at,ht=d[0].name+"",X,xt,se,rt,ct,Y,O,Ut,wt,v,Z,_t,jt,$t,z,tt,Ct,Qt,kt,L,dt,gt,ne,ft,oe,D,yt,et,vt,U,pt,ie,H,Ft,lt,Lt,st,At,nt,j,E,Jt,Tt,Kt,Pt,C,Q,M,ae,Rt,re,ut,ce,I,Ot,de,Et,Vt,St,Wt,A,mt,J,K,S=[],Nt=new Map,fe,T,k=[],pe=new Map,N;R=new je({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${d[3]}');
...
// fetch a paginated records list
const resultList = await pb.collection('${(me=d[0])==null?void 0:me.name}').getList(1, 50, {
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
});
// you can also fetch all records at once via getFullList
const records = await pb.collection('${(Dt=d[0])==null?void 0:Dt.name}').getFullList(200 /* batch size */, {
sort: '-created',
});
// or fetch only the first record that matches the specified filter
const record = await pb.collection('${(be=d[0])==null?void 0:be.name}').getFirstListItem('someField="test"', {
expand: 'relField1,relField2.subRelField',
});
`,dart:`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${d[3]}');
...
// fetch a paginated records list
final resultList = await pb.collection('${(Ht=d[0])==null?void 0:Ht.name}').getList(
page: 1,
perPage: 50,
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
);
// you can also fetch all records at once via getFullList
final records = await pb.collection('${(Xt=d[0])==null?void 0:Xt.name}').getFullList(
batch: 200,
sort: '-created',
);
// or fetch only the first record that matches the specified filter
final record = await pb.collection('${(Mt=d[0])==null?void 0:Mt.name}').getFirstListItem(
'someField="test"',
expand: 'relField1,relField2.subRelField',
);
`}});let y=d[1]&&Re();et=new he({props:{content:`
// DESC by created and ASC by id
?sort=-created,id
`}}),st=new he({props:{content:`
?filter=(id='abc' && created>'2022-01-01')
`}}),nt=new We({}),M=new he({props:{content:"?expand=relField1,relField2.subRelField"}});let ot=d[4];const ue=l=>l[6].code;for(let l=0;l<ot.length;l+=1){let i=Pe(d,ot,l),g=ue(i);Nt.set(g,S[l]=Oe(g,i))}let P=d[4];const qt=l=>l[6].code;for(let l=0;l<P.length;l+=1){let i=Te(d,P,l),g=qt(i);pe.set(g,k[l]=Ee(g,i))}return{c(){n=e("h3"),a=_("List/Search ("),f=_(r),p=_(")"),b=s(),x=e("div"),$=e("p"),h=_("Fetch a paginated "),w=e("strong"),V=_(c),bt=_(" records list, supporting sorting and filtering."),Gt=s(),Yt(R.$$.fragment),zt=s(),q=e("h6"),q.textContent="API details",it=s(),F=e("div"),W=e("strong"),W.textContent="GET",ee=s(),B=e("div"),G=e("p"),le=_("/api/collections/"),at=e("strong"),X=_(ht),xt=_("/records"),se=s(),y&&y.c(),rt=s(),ct=e("div"),ct.textContent="Query parameters",Y=s(),O=e("table"),Ut=e("thead"),Ut.innerHTML=`<tr><th>Param</th>
<th>Type</th>
<th width="60%">Description</th></tr>`,wt=s(),v=e("tbody"),Z=e("tr"),Z.innerHTML=`<td>page</td>
<td><span class="label">Number</span></td>
<td>The page (aka. offset) of the paginated list (default to 1).</td>`,_t=s(),jt=e("tr"),jt.innerHTML=`<td>perPage</td>
<td><span class="label">Number</span></td>
<td>Specify the max returned records per page (default to 30).</td>`,$t=s(),z=e("tr"),tt=e("td"),tt.textContent="sort",Ct=s(),Qt=e("td"),Qt.innerHTML='<span class="label">String</span>',kt=s(),L=e("td"),dt=_("Specify the records order attribute(s). "),gt=e("br"),ne=_(`
Add `),ft=e("code"),ft.textContent="-",oe=_(" / "),D=e("code"),D.textContent="+",yt=_(` (default) in front of the attribute for DESC / ASC order.
Ex.:
`),Yt(et.$$.fragment),vt=s(),U=e("tr"),pt=e("td"),pt.textContent="filter",ie=s(),H=e("td"),H.innerHTML='<span class="label">String</span>',Ft=s(),lt=e("td"),Lt=_(`Filter the returned records. Ex.:
`),Yt(st.$$.fragment),At=s(),Yt(nt.$$.fragment),j=s(),E=e("tr"),Jt=e("td"),Jt.textContent="expand",Tt=s(),Kt=e("td"),Kt.innerHTML='<span class="label">String</span>',Pt=s(),C=e("td"),Q=_(`Auto expand record relations. Ex.:
`),Yt(M.$$.fragment),ae=_(`
Supports up to 6-levels depth nested relations expansion. `),Rt=e("br"),re=_(`
The expanded relations will be appended to each individual record under the
`),ut=e("code"),ut.textContent="expand",ce=_(" property (eg. "),I=e("code"),I.textContent='"expand": {"relField1": {...}, ...}',Ot=_(`).
`),de=e("br"),Et=_(`
Only the relations to which the request user has permissions to `),Vt=e("strong"),Vt.textContent="view",St=_(" will be expanded."),Wt=s(),A=e("div"),A.textContent="Responses",mt=s(),J=e("div"),K=e("div");for(let l=0;l<S.length;l+=1)S[l].c();fe=s(),T=e("div");for(let l=0;l<k.length;l+=1)k[l].c();o(n,"class","m-b-sm"),o(x,"class","content txt-lg m-b-sm"),o(q,"class","m-b-xs"),o(W,"class","label label-primary"),o(B,"class","content"),o(F,"class","alert alert-info"),o(ct,"class","section-title"),o(O,"class","table-compact table-border m-b-base"),o(A,"class","section-title"),o(K,"class","tabs-header compact left"),o(T,"class","tabs-content"),o(J,"class","tabs")},m(l,i){u(l,n,i),t(n,a),t(n,f),t(n,p),u(l,b,i),u(l,x,i),t(x,$),t($,h),t($,w),t(w,V),t($,bt),u(l,Gt,i),Zt(R,l,i),u(l,zt,i),u(l,q,i),u(l,it,i),u(l,F,i),t(F,W),t(F,ee),t(F,B),t(B,G),t(G,le),t(G,at),t(at,X),t(G,xt),t(F,se),y&&y.m(F,null),u(l,rt,i),u(l,ct,i),u(l,Y,i),u(l,O,i),t(O,Ut),t(O,wt),t(O,v),t(v,Z),t(v,_t),t(v,jt),t(v,$t),t(v,z),t(z,tt),t(z,Ct),t(z,Qt),t(z,kt),t(z,L),t(L,dt),t(L,gt),t(L,ne),t(L,ft),t(L,oe),t(L,D),t(L,yt),Zt(et,L,null),t(v,vt),t(v,U),t(U,pt),t(U,ie),t(U,H),t(U,Ft),t(U,lt),t(lt,Lt),Zt(st,lt,null),t(lt,At),Zt(nt,lt,null),t(v,j),t(v,E),t(E,Jt),t(E,Tt),t(E,Kt),t(E,Pt),t(E,C),t(C,Q),Zt(M,C,null),t(C,ae),t(C,Rt),t(C,re),t(C,ut),t(C,ce),t(C,I),t(C,Ot),t(C,de),t(C,Et),t(C,Vt),t(C,St),u(l,Wt,i),u(l,A,i),u(l,mt,i),u(l,J,i),t(J,K);for(let g=0;g<S.length;g+=1)S[g].m(K,null);t(J,fe),t(J,T);for(let g=0;g<k.length;g+=1)k[g].m(T,null);N=!0},p(l,[i]){var $e,Ce,ke,ge,ye,ve;(!N||i&1)&&r!==(r=l[0].name+"")&&we(f,r),(!N||i&1)&&c!==(c=l[0].name+"")&&we(V,c);const g={};i&9&&(g.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${l[3]}');
...
// fetch a paginated records list
const resultList = await pb.collection('${($e=l[0])==null?void 0:$e.name}').getList(1, 50, {
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
});
// you can also fetch all records at once via getFullList
const records = await pb.collection('${(Ce=l[0])==null?void 0:Ce.name}').getFullList(200 /* batch size */, {
sort: '-created',
});
// or fetch only the first record that matches the specified filter
const record = await pb.collection('${(ke=l[0])==null?void 0:ke.name}').getFirstListItem('someField="test"', {
expand: 'relField1,relField2.subRelField',
});
`),i&9&&(g.dart=`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${l[3]}');
...
// fetch a paginated records list
final resultList = await pb.collection('${(ge=l[0])==null?void 0:ge.name}').getList(
page: 1,
perPage: 50,
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
);
// you can also fetch all records at once via getFullList
final records = await pb.collection('${(ye=l[0])==null?void 0:ye.name}').getFullList(
batch: 200,
sort: '-created',
);
// or fetch only the first record that matches the specified filter
final record = await pb.collection('${(ve=l[0])==null?void 0:ve.name}').getFirstListItem(
'someField="test"',
expand: 'relField1,relField2.subRelField',
);
`),R.$set(g),(!N||i&1)&&ht!==(ht=l[0].name+"")&&we(X,ht),l[1]?y||(y=Re(),y.c(),y.m(F,null)):y&&(y.d(1),y=null),i&20&&(ot=l[4],S=Le(S,i,ue,1,l,ot,Nt,K,Me,Oe,null,Pe)),i&20&&(P=l[4],Ie(),k=Le(k,i,qt,1,l,P,pe,T,Be,Ee,null,Te),Ge())},i(l){if(!N){It(R.$$.fragment,l),It(et.$$.fragment,l),It(st.$$.fragment,l),It(nt.$$.fragment,l),It(M.$$.fragment,l);for(let i=0;i<P.length;i+=1)It(k[i]);N=!0}},o(l){Bt(R.$$.fragment,l),Bt(et.$$.fragment,l),Bt(st.$$.fragment,l),Bt(nt.$$.fragment,l),Bt(M.$$.fragment,l);for(let i=0;i<k.length;i+=1)Bt(k[i]);N=!1},d(l){l&&m(n),l&&m(b),l&&m(x),l&&m(Gt),te(R,l),l&&m(zt),l&&m(q),l&&m(it),l&&m(F),y&&y.d(),l&&m(rt),l&&m(ct),l&&m(Y),l&&m(O),te(et),te(st),te(nt),te(M),l&&m(Wt),l&&m(A),l&&m(mt),l&&m(J);for(let i=0;i<S.length;i+=1)S[i].d();for(let i=0;i<k.length;i+=1)k[i].d()}}}function Ye(d,n,a){let r,f,{collection:p=new ze}=n,b=200,x=[];const $=h=>a(2,b=h.code);return d.$$set=h=>{"collection"in h&&a(0,p=h.collection)},d.$$.update=()=>{d.$$.dirty&1&&a(1,r=(p==null?void 0:p.listRule)===null),d.$$.dirty&3&&p!=null&&p.id&&(x.push({code:200,body:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[_e.dummyCollectionRecord(p),_e.dummyCollectionRecord(p)]},null,2)}),x.push({code:400,body:`
{
"code": 400,
"message": "Something went wrong while processing your request. Invalid filter.",
"data": {}
}
`}),r&&x.push({code:403,body:`
{
"code": 403,
"message": "Only admins can access this action.",
"data": {}
}
`}))},a(3,f=_e.getApiExampleUrl(Ue.baseUrl)),[p,r,b,f,x,$]}class el extends Se{constructor(n){super(),Ne(this,n,Ye,Xe,qe,{collection:0})}}export{el as default};
-134
View File
@@ -1,134 +0,0 @@
import{S as Et,i as Nt,s as Mt,e as l,b as a,E as qt,f as d,g as p,u as Ht,y as xt,o as u,w as k,h as e,M as Ae,c as ge,m as ye,x as Ue,N as Lt,O as Dt,k as It,P as Bt,n as zt,t as ce,a as de,d as ve,Q as Gt,C as je,p as Ut,r as Ee}from"./index.2a48c906.js";import{S as jt}from"./SdkTabs.9dbb2ca3.js";function Qt(r){let s,n,i;return{c(){s=l("span"),s.textContent="Show details",n=a(),i=l("i"),d(s,"class","txt"),d(i,"class","ri-arrow-down-s-line")},m(c,f){p(c,s,f),p(c,n,f),p(c,i,f)},d(c){c&&u(s),c&&u(n),c&&u(i)}}}function Jt(r){let s,n,i;return{c(){s=l("span"),s.textContent="Hide details",n=a(),i=l("i"),d(s,"class","txt"),d(i,"class","ri-arrow-up-s-line")},m(c,f){p(c,s,f),p(c,n,f),p(c,i,f)},d(c){c&&u(s),c&&u(n),c&&u(i)}}}function Tt(r){let s,n,i,c,f,m,_,w,b,$,h,M,W,fe,T,pe,O,G,C,H,Fe,A,E,Ce,U,X,q,Y,xe,j,Q,D,P,ue,Z,v,I,ee,me,te,N,B,le,be,se,x,J,ne,Le,K,he,V;return{c(){s=l("p"),s.innerHTML=`The syntax basically follows the format
<code><span class="txt-success">OPERAND</span>
<span class="txt-danger">OPERATOR</span>
<span class="txt-success">OPERAND</span></code>, where:`,n=a(),i=l("ul"),c=l("li"),c.innerHTML=`<code class="txt-success">OPERAND</code> - could be any of the above field literal, string (single
or double quoted), number, null, true, false`,f=a(),m=l("li"),_=l("code"),_.textContent="OPERATOR",w=k(` - is one of:
`),b=l("br"),$=a(),h=l("ul"),M=l("li"),W=l("code"),W.textContent="=",fe=a(),T=l("span"),T.textContent="Equal",pe=a(),O=l("li"),G=l("code"),G.textContent="!=",C=a(),H=l("span"),H.textContent="NOT equal",Fe=a(),A=l("li"),E=l("code"),E.textContent=">",Ce=a(),U=l("span"),U.textContent="Greater than",X=a(),q=l("li"),Y=l("code"),Y.textContent=">=",xe=a(),j=l("span"),j.textContent="Greater than or equal",Q=a(),D=l("li"),P=l("code"),P.textContent="<",ue=a(),Z=l("span"),Z.textContent="Less than or equal",v=a(),I=l("li"),ee=l("code"),ee.textContent="<=",me=a(),te=l("span"),te.textContent="Less than or equal",N=a(),B=l("li"),le=l("code"),le.textContent="~",be=a(),se=l("span"),se.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
wildcard match)`,x=a(),J=l("li"),ne=l("code"),ne.textContent="!~",Le=a(),K=l("span"),K.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
wildcard match)`,he=a(),V=l("p"),V.innerHTML=`To group and combine several expressions you could use brackets
<code>(...)</code>, <code>&amp;&amp;</code> (AND) and <code>||</code> (OR) tokens.`,d(_,"class","txt-danger"),d(W,"class","filter-op svelte-1w7s5nw"),d(T,"class","txt-hint"),d(G,"class","filter-op svelte-1w7s5nw"),d(H,"class","txt-hint"),d(E,"class","filter-op svelte-1w7s5nw"),d(U,"class","txt-hint"),d(Y,"class","filter-op svelte-1w7s5nw"),d(j,"class","txt-hint"),d(P,"class","filter-op svelte-1w7s5nw"),d(Z,"class","txt-hint"),d(ee,"class","filter-op svelte-1w7s5nw"),d(te,"class","txt-hint"),d(le,"class","filter-op svelte-1w7s5nw"),d(se,"class","txt-hint"),d(ne,"class","filter-op svelte-1w7s5nw"),d(K,"class","txt-hint")},m(F,R){p(F,s,R),p(F,n,R),p(F,i,R),e(i,c),e(i,f),e(i,m),e(m,_),e(m,w),e(m,b),e(m,$),e(m,h),e(h,M),e(M,W),e(M,fe),e(M,T),e(h,pe),e(h,O),e(O,G),e(O,C),e(O,H),e(h,Fe),e(h,A),e(A,E),e(A,Ce),e(A,U),e(h,X),e(h,q),e(q,Y),e(q,xe),e(q,j),e(h,Q),e(h,D),e(D,P),e(D,ue),e(D,Z),e(h,v),e(h,I),e(I,ee),e(I,me),e(I,te),e(h,N),e(h,B),e(B,le),e(B,be),e(B,se),e(h,x),e(h,J),e(J,ne),e(J,Le),e(J,K),p(F,he,R),p(F,V,R)},d(F){F&&u(s),F&&u(n),F&&u(i),F&&u(he),F&&u(V)}}}function Kt(r){let s,n,i,c,f;function m($,h){return $[0]?Jt:Qt}let _=m(r),w=_(r),b=r[0]&&Tt();return{c(){s=l("button"),w.c(),n=a(),b&&b.c(),i=qt(),d(s,"class","btn btn-sm btn-secondary m-t-5")},m($,h){p($,s,h),w.m(s,null),p($,n,h),b&&b.m($,h),p($,i,h),c||(f=Ht(s,"click",r[1]),c=!0)},p($,[h]){_!==(_=m($))&&(w.d(1),w=_($),w&&(w.c(),w.m(s,null))),$[0]?b||(b=Tt(),b.c(),b.m(i.parentNode,i)):b&&(b.d(1),b=null)},i:xt,o:xt,d($){$&&u(s),w.d(),$&&u(n),b&&b.d($),$&&u(i),c=!1,f()}}}function Vt(r,s,n){let i=!1;function c(){n(0,i=!i)}return[i,c]}class Wt extends Et{constructor(s){super(),Nt(this,s,Vt,Kt,Mt,{})}}function Pt(r,s,n){const i=r.slice();return i[6]=s[n],i}function Rt(r,s,n){const i=r.slice();return i[6]=s[n],i}function St(r){let s;return{c(){s=l("p"),s.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",d(s,"class","txt-hint txt-sm txt-right")},m(n,i){p(n,s,i)},d(n){n&&u(s)}}}function Ot(r,s){let n,i=s[6].code+"",c,f,m,_;function w(){return s[5](s[6])}return{key:r,first:null,c(){n=l("div"),c=k(i),f=a(),d(n,"class","tab-item"),Ee(n,"active",s[2]===s[6].code),this.first=n},m(b,$){p(b,n,$),e(n,c),e(n,f),m||(_=Ht(n,"click",w),m=!0)},p(b,$){s=b,$&20&&Ee(n,"active",s[2]===s[6].code)},d(b){b&&u(n),m=!1,_()}}}function At(r,s){let n,i,c,f;return i=new Ae({props:{content:s[6].body}}),{key:r,first:null,c(){n=l("div"),ge(i.$$.fragment),c=a(),d(n,"class","tab-item"),Ee(n,"active",s[2]===s[6].code),this.first=n},m(m,_){p(m,n,_),ye(i,n,null),e(n,c),f=!0},p(m,_){s=m,(!f||_&20)&&Ee(n,"active",s[2]===s[6].code)},i(m){f||(ce(i.$$.fragment,m),f=!0)},o(m){de(i.$$.fragment,m),f=!1},d(m){m&&u(n),ve(i)}}}function Xt(r){var mt,bt,ht,_t,$t,kt;let s,n,i=r[0].name+"",c,f,m,_,w,b,$,h=r[0].name+"",M,W,fe,T,pe,O,G,C,H,Fe,A,E,Ce,U,X=r[0].name+"",q,Y,xe,j,Q,D,P,ue,Z,v,I,ee,me,te,N,B,le,be,se,x,J,ne,Le,K,he,V,F,R,Qe,ie,Ne,Je,Me,Ke,_e,Ve,$e,We,ke,Xe,oe,He,Ye,qe,Ze,y,et,we,tt,lt,st,De,nt,Ie,it,ot,at,Be,rt,ze,Te,Ge,ae,Pe,z=[],ct=new Map,dt,Re,S=[],ft=new Map,re;T=new jt({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${r[3]}');
...
// fetch a paginated records list
const resultList = await pb.collection('${(mt=r[0])==null?void 0:mt.name}').getList(1, 50, {
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
});
// you can also fetch all records at once via getFullList
const records = await pb.collection('${(bt=r[0])==null?void 0:bt.name}').getFullList(200 /* batch size */, {
sort: '-created',
});
// or fetch only the first record that matches the specified filter
const record = await pb.collection('${(ht=r[0])==null?void 0:ht.name}').getFirstListItem('someField="test"', {
expand: 'relField1,relField2.subRelField',
});
`,dart:`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${r[3]}');
...
// fetch a paginated records list
final resultList = await pb.collection('${(_t=r[0])==null?void 0:_t.name}').getList(
page: 1,
perPage: 50,
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
);
// you can also fetch all records at once via getFullList
final records = await pb.collection('${($t=r[0])==null?void 0:$t.name}').getFullList(
batch: 200,
sort: '-created',
);
// or fetch only the first record that matches the specified filter
final record = await pb.collection('${(kt=r[0])==null?void 0:kt.name}').getFirstListItem(
'someField="test"',
expand: 'relField1,relField2.subRelField',
);
`}});let L=r[1]&&St();R=new Ae({props:{content:`
// DESC by created and ASC by id
?sort=-created,id
`}}),$e=new Ae({props:{content:`
?filter=(id='abc' && created>'2022-01-01')
`}}),ke=new Wt({}),we=new Ae({props:{content:"?expand=relField1,relField2.subRelField"}});let Oe=r[4];const pt=t=>t[6].code;for(let t=0;t<Oe.length;t+=1){let o=Rt(r,Oe,t),g=pt(o);ct.set(g,z[t]=Ot(g,o))}let Se=r[4];const ut=t=>t[6].code;for(let t=0;t<Se.length;t+=1){let o=Pt(r,Se,t),g=ut(o);ft.set(g,S[t]=At(g,o))}return{c(){s=l("h3"),n=k("List/Search ("),c=k(i),f=k(")"),m=a(),_=l("div"),w=l("p"),b=k("Fetch a paginated "),$=l("strong"),M=k(h),W=k(" records list, supporting sorting and filtering."),fe=a(),ge(T.$$.fragment),pe=a(),O=l("h6"),O.textContent="API details",G=a(),C=l("div"),H=l("strong"),H.textContent="GET",Fe=a(),A=l("div"),E=l("p"),Ce=k("/api/collections/"),U=l("strong"),q=k(X),Y=k("/records"),xe=a(),L&&L.c(),j=a(),Q=l("div"),Q.textContent="Query parameters",D=a(),P=l("table"),ue=l("thead"),ue.innerHTML=`<tr><th>Param</th>
<th>Type</th>
<th width="60%">Description</th></tr>`,Z=a(),v=l("tbody"),I=l("tr"),I.innerHTML=`<td>page</td>
<td><span class="label">Number</span></td>
<td>The page (aka. offset) of the paginated list (default to 1).</td>`,ee=a(),me=l("tr"),me.innerHTML=`<td>perPage</td>
<td><span class="label">Number</span></td>
<td>Specify the max returned records per page (default to 30).</td>`,te=a(),N=l("tr"),B=l("td"),B.textContent="sort",le=a(),be=l("td"),be.innerHTML='<span class="label">String</span>',se=a(),x=l("td"),J=k("Specify the records order attribute(s). "),ne=l("br"),Le=k(`
Add `),K=l("code"),K.textContent="-",he=k(" / "),V=l("code"),V.textContent="+",F=k(` (default) in front of the attribute for DESC / ASC order.
Ex.:
`),ge(R.$$.fragment),Qe=a(),ie=l("tr"),Ne=l("td"),Ne.textContent="filter",Je=a(),Me=l("td"),Me.innerHTML='<span class="label">String</span>',Ke=a(),_e=l("td"),Ve=k(`Filter the returned records. Ex.:
`),ge($e.$$.fragment),We=a(),ge(ke.$$.fragment),Xe=a(),oe=l("tr"),He=l("td"),He.textContent="expand",Ye=a(),qe=l("td"),qe.innerHTML='<span class="label">String</span>',Ze=a(),y=l("td"),et=k(`Auto expand record relations. Ex.:
`),ge(we.$$.fragment),tt=k(`
Supports up to 6-levels depth nested relations expansion. `),lt=l("br"),st=k(`
The expanded relations will be appended to each individual record under the
`),De=l("code"),De.textContent="expand",nt=k(" property (eg. "),Ie=l("code"),Ie.textContent='"expand": {"relField1": {...}, ...}',it=k(`).
`),ot=l("br"),at=k(`
Only the relations to which the request user has permissions to `),Be=l("strong"),Be.textContent="view",rt=k(" will be expanded."),ze=a(),Te=l("div"),Te.textContent="Responses",Ge=a(),ae=l("div"),Pe=l("div");for(let t=0;t<z.length;t+=1)z[t].c();dt=a(),Re=l("div");for(let t=0;t<S.length;t+=1)S[t].c();d(s,"class","m-b-sm"),d(_,"class","content txt-lg m-b-sm"),d(O,"class","m-b-xs"),d(H,"class","label label-primary"),d(A,"class","content"),d(C,"class","alert alert-info"),d(Q,"class","section-title"),d(P,"class","table-compact table-border m-b-base"),d(Te,"class","section-title"),d(Pe,"class","tabs-header compact left"),d(Re,"class","tabs-content"),d(ae,"class","tabs")},m(t,o){p(t,s,o),e(s,n),e(s,c),e(s,f),p(t,m,o),p(t,_,o),e(_,w),e(w,b),e(w,$),e($,M),e(w,W),p(t,fe,o),ye(T,t,o),p(t,pe,o),p(t,O,o),p(t,G,o),p(t,C,o),e(C,H),e(C,Fe),e(C,A),e(A,E),e(E,Ce),e(E,U),e(U,q),e(E,Y),e(C,xe),L&&L.m(C,null),p(t,j,o),p(t,Q,o),p(t,D,o),p(t,P,o),e(P,ue),e(P,Z),e(P,v),e(v,I),e(v,ee),e(v,me),e(v,te),e(v,N),e(N,B),e(N,le),e(N,be),e(N,se),e(N,x),e(x,J),e(x,ne),e(x,Le),e(x,K),e(x,he),e(x,V),e(x,F),ye(R,x,null),e(v,Qe),e(v,ie),e(ie,Ne),e(ie,Je),e(ie,Me),e(ie,Ke),e(ie,_e),e(_e,Ve),ye($e,_e,null),e(_e,We),ye(ke,_e,null),e(v,Xe),e(v,oe),e(oe,He),e(oe,Ye),e(oe,qe),e(oe,Ze),e(oe,y),e(y,et),ye(we,y,null),e(y,tt),e(y,lt),e(y,st),e(y,De),e(y,nt),e(y,Ie),e(y,it),e(y,ot),e(y,at),e(y,Be),e(y,rt),p(t,ze,o),p(t,Te,o),p(t,Ge,o),p(t,ae,o),e(ae,Pe);for(let g=0;g<z.length;g+=1)z[g].m(Pe,null);e(ae,dt),e(ae,Re);for(let g=0;g<S.length;g+=1)S[g].m(Re,null);re=!0},p(t,[o]){var wt,gt,yt,vt,Ft,Ct;(!re||o&1)&&i!==(i=t[0].name+"")&&Ue(c,i),(!re||o&1)&&h!==(h=t[0].name+"")&&Ue(M,h);const g={};o&9&&(g.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${t[3]}');
...
// fetch a paginated records list
const resultList = await pb.collection('${(wt=t[0])==null?void 0:wt.name}').getList(1, 50, {
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
});
// you can also fetch all records at once via getFullList
const records = await pb.collection('${(gt=t[0])==null?void 0:gt.name}').getFullList(200 /* batch size */, {
sort: '-created',
});
// or fetch only the first record that matches the specified filter
const record = await pb.collection('${(yt=t[0])==null?void 0:yt.name}').getFirstListItem('someField="test"', {
expand: 'relField1,relField2.subRelField',
});
`),o&9&&(g.dart=`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${t[3]}');
...
// fetch a paginated records list
final resultList = await pb.collection('${(vt=t[0])==null?void 0:vt.name}').getList(
page: 1,
perPage: 50,
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
);
// you can also fetch all records at once via getFullList
final records = await pb.collection('${(Ft=t[0])==null?void 0:Ft.name}').getFullList(
batch: 200,
sort: '-created',
);
// or fetch only the first record that matches the specified filter
final record = await pb.collection('${(Ct=t[0])==null?void 0:Ct.name}').getFirstListItem(
'someField="test"',
expand: 'relField1,relField2.subRelField',
);
`),T.$set(g),(!re||o&1)&&X!==(X=t[0].name+"")&&Ue(q,X),t[1]?L||(L=St(),L.c(),L.m(C,null)):L&&(L.d(1),L=null),o&20&&(Oe=t[4],z=Lt(z,o,pt,1,t,Oe,ct,Pe,Dt,Ot,null,Rt)),o&20&&(Se=t[4],It(),S=Lt(S,o,ut,1,t,Se,ft,Re,Bt,At,null,Pt),zt())},i(t){if(!re){ce(T.$$.fragment,t),ce(R.$$.fragment,t),ce($e.$$.fragment,t),ce(ke.$$.fragment,t),ce(we.$$.fragment,t);for(let o=0;o<Se.length;o+=1)ce(S[o]);re=!0}},o(t){de(T.$$.fragment,t),de(R.$$.fragment,t),de($e.$$.fragment,t),de(ke.$$.fragment,t),de(we.$$.fragment,t);for(let o=0;o<S.length;o+=1)de(S[o]);re=!1},d(t){t&&u(s),t&&u(m),t&&u(_),t&&u(fe),ve(T,t),t&&u(pe),t&&u(O),t&&u(G),t&&u(C),L&&L.d(),t&&u(j),t&&u(Q),t&&u(D),t&&u(P),ve(R),ve($e),ve(ke),ve(we),t&&u(ze),t&&u(Te),t&&u(Ge),t&&u(ae);for(let o=0;o<z.length;o+=1)z[o].d();for(let o=0;o<S.length;o+=1)S[o].d()}}}function Yt(r,s,n){let i,c,{collection:f=new Gt}=s,m=200,_=[];const w=b=>n(2,m=b.code);return r.$$set=b=>{"collection"in b&&n(0,f=b.collection)},r.$$.update=()=>{r.$$.dirty&1&&n(1,i=(f==null?void 0:f.listRule)===null),r.$$.dirty&3&&f!=null&&f.id&&(_.push({code:200,body:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[je.dummyCollectionRecord(f),je.dummyCollectionRecord(f)]},null,2)}),_.push({code:400,body:`
{
"code": 400,
"message": "Something went wrong while processing your request. Invalid filter.",
"data": {}
}
`}),i&&_.push({code:403,body:`
{
"code": 403,
"message": "Only admins can access this action.",
"data": {}
}
`}))},n(3,c=je.getApiExampleUrl(Ut.baseUrl)),[f,i,m,c,_,w]}class tl extends Et{constructor(s){super(),Nt(this,s,Yt,Xt,Mt,{collection:0})}}export{tl as default};
@@ -1,4 +1,4 @@
import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s,m as Se,x as U,N as Pe,O as Oe,k as Le,P as We,n as ze,t as te,a as le,o as d,d as Ee,Q as De,C as He,p as Re,r as j,u as Ue,M as je}from"./index.2a48c906.js";import{S as Ne}from"./SdkTabs.9dbb2ca3.js";function ye(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ae(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ce(a,l){let o,n=l[5].code+"",f,h,c,u;function m(){return l[4](l[5])}return{key:a,first:null,c(){o=i("button"),f=v(n),h=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(g,P){r(g,o,P),s(o,f),s(o,h),c||(u=Ue(o,"click",m),c=!0)},p(g,P){l=g,P&4&&n!==(n=l[5].code+"")&&U(f,n),P&6&&j(o,"active",l[1]===l[5].code)},d(g){g&&d(o),c=!1,u()}}}function Te(a,l){let o,n,f,h;return n=new je({props:{content:l[5].body}}),{key:a,first:null,c(){o=i("div"),Ie(n.$$.fragment),f=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(c,u){r(c,o,u),Se(n,o,null),s(o,f),h=!0},p(c,u){l=c;const m={};u&4&&(m.content=l[5].body),n.$set(m),(!h||u&6)&&j(o,"active",l[1]===l[5].code)},i(c){h||(te(n.$$.fragment,c),h=!0)},o(c){le(n.$$.fragment,c),h=!1},d(c){c&&d(o),Ee(n)}}}function Ge(a){var be,he,_e,ke;let l,o,n=a[0].name+"",f,h,c,u,m,g,P,L=a[0].name+"",N,oe,se,G,K,y,Q,I,F,w,W,ae,z,A,ne,J,D=a[0].name+"",V,ie,X,ce,re,H,Y,S,Z,E,x,B,ee,C,q,$=[],de=new Map,ue,M,k=[],pe=new Map,T;y=new Ne({props:{js:`
import{S as Be,i as qe,s as Oe,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s,m as Se,x as U,O as Pe,P as Le,k as Me,Q as Re,n as We,t as te,a as le,o as d,d as Ee,R as ze,C as De,p as He,r as j,u as Ue,N as je}from"./index.536fddcb.js";import{S as Ne}from"./SdkTabs.3571c26b.js";function ye(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ae(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ce(a,l){let o,n=l[5].code+"",f,h,c,u;function m(){return l[4](l[5])}return{key:a,first:null,c(){o=i("button"),f=v(n),h=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(g,P){r(g,o,P),s(o,f),s(o,h),c||(u=Ue(o,"click",m),c=!0)},p(g,P){l=g,P&4&&n!==(n=l[5].code+"")&&U(f,n),P&6&&j(o,"active",l[1]===l[5].code)},d(g){g&&d(o),c=!1,u()}}}function Te(a,l){let o,n,f,h;return n=new je({props:{content:l[5].body}}),{key:a,first:null,c(){o=i("div"),Ie(n.$$.fragment),f=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(c,u){r(c,o,u),Se(n,o,null),s(o,f),h=!0},p(c,u){l=c;const m={};u&4&&(m.content=l[5].body),n.$set(m),(!h||u&6)&&j(o,"active",l[1]===l[5].code)},i(c){h||(te(n.$$.fragment,c),h=!0)},o(c){le(n.$$.fragment,c),h=!1},d(c){c&&d(o),Ee(n)}}}function Ge(a){var be,he,_e,ke;let l,o,n=a[0].name+"",f,h,c,u,m,g,P,M=a[0].name+"",N,oe,se,G,K,y,Q,I,F,w,R,ae,W,A,ne,J,z=a[0].name+"",V,ie,X,ce,re,D,Y,S,Z,E,x,B,ee,C,q,$=[],de=new Map,ue,O,k=[],pe=new Map,T;y=new Ne({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${a[3]}');
@@ -22,12 +22,12 @@ import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s
final result = await pb.collection('${(ke=a[0])==null?void 0:ke.name}').listExternalAuths(
pb.authStore.model.id,
);
`}});let R=a[2];const fe=e=>e[5].code;for(let e=0;e<R.length;e+=1){let t=Ae(a,R,e),p=fe(t);de.set(p,$[e]=Ce(p,t))}let O=a[2];const me=e=>e[5].code;for(let e=0;e<O.length;e+=1){let t=ye(a,O,e),p=me(t);pe.set(p,k[e]=Te(p,t))}return{c(){l=i("h3"),o=v("List OAuth2 accounts ("),f=v(n),h=v(")"),c=_(),u=i("div"),m=i("p"),g=v("Returns a list with all OAuth2 providers linked to a single "),P=i("strong"),N=v(L),oe=v("."),se=_(),G=i("p"),G.textContent="Only admins and the account owner can access this action.",K=_(),Ie(y.$$.fragment),Q=_(),I=i("h6"),I.textContent="API details",F=_(),w=i("div"),W=i("strong"),W.textContent="GET",ae=_(),z=i("div"),A=i("p"),ne=v("/api/collections/"),J=i("strong"),V=v(D),ie=v("/records/"),X=i("strong"),X.textContent=":id",ce=v("/external-auths"),re=_(),H=i("p"),H.innerHTML="Requires <code>Authorization:TOKEN</code> header",Y=_(),S=i("div"),S.textContent="Path Parameters",Z=_(),E=i("table"),E.innerHTML=`<thead><tr><th>Param</th>
`}});let H=a[2];const fe=e=>e[5].code;for(let e=0;e<H.length;e+=1){let t=Ae(a,H,e),p=fe(t);de.set(p,$[e]=Ce(p,t))}let L=a[2];const me=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=ye(a,L,e),p=me(t);pe.set(p,k[e]=Te(p,t))}return{c(){l=i("h3"),o=v("List OAuth2 accounts ("),f=v(n),h=v(")"),c=_(),u=i("div"),m=i("p"),g=v("Returns a list with all OAuth2 providers linked to a single "),P=i("strong"),N=v(M),oe=v("."),se=_(),G=i("p"),G.textContent="Only admins and the account owner can access this action.",K=_(),Ie(y.$$.fragment),Q=_(),I=i("h6"),I.textContent="API details",F=_(),w=i("div"),R=i("strong"),R.textContent="GET",ae=_(),W=i("div"),A=i("p"),ne=v("/api/collections/"),J=i("strong"),V=v(z),ie=v("/records/"),X=i("strong"),X.textContent=":id",ce=v("/external-auths"),re=_(),D=i("p"),D.innerHTML="Requires <code>Authorization:TOKEN</code> header",Y=_(),S=i("div"),S.textContent="Path Parameters",Z=_(),E=i("table"),E.innerHTML=`<thead><tr><th>Param</th>
<th>Type</th>
<th width="60%">Description</th></tr></thead>
<tbody><tr><td>id</td>
<td><span class="label">String</span></td>
<td>ID of the auth record.</td></tr></tbody>`,x=_(),B=i("div"),B.textContent="Responses",ee=_(),C=i("div"),q=i("div");for(let e=0;e<$.length;e+=1)$[e].c();ue=_(),M=i("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(u,"class","content txt-lg m-b-sm"),b(I,"class","m-b-xs"),b(W,"class","label label-primary"),b(z,"class","content"),b(H,"class","txt-hint txt-sm txt-right"),b(w,"class","alert alert-info"),b(S,"class","section-title"),b(E,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(M,"class","tabs-content"),b(C,"class","tabs")},m(e,t){r(e,l,t),s(l,o),s(l,f),s(l,h),r(e,c,t),r(e,u,t),s(u,m),s(m,g),s(m,P),s(P,N),s(m,oe),s(u,se),s(u,G),r(e,K,t),Se(y,e,t),r(e,Q,t),r(e,I,t),r(e,F,t),r(e,w,t),s(w,W),s(w,ae),s(w,z),s(z,A),s(A,ne),s(A,J),s(J,V),s(A,ie),s(A,X),s(A,ce),s(w,re),s(w,H),r(e,Y,t),r(e,S,t),r(e,Z,t),r(e,E,t),r(e,x,t),r(e,B,t),r(e,ee,t),r(e,C,t),s(C,q);for(let p=0;p<$.length;p+=1)$[p].m(q,null);s(C,ue),s(C,M);for(let p=0;p<k.length;p+=1)k[p].m(M,null);T=!0},p(e,[t]){var ve,we,$e,ge;(!T||t&1)&&n!==(n=e[0].name+"")&&U(f,n),(!T||t&1)&&L!==(L=e[0].name+"")&&U(N,L);const p={};t&9&&(p.js=`
<td>ID of the auth record.</td></tr></tbody>`,x=_(),B=i("div"),B.textContent="Responses",ee=_(),C=i("div"),q=i("div");for(let e=0;e<$.length;e+=1)$[e].c();ue=_(),O=i("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(u,"class","content txt-lg m-b-sm"),b(I,"class","m-b-xs"),b(R,"class","label label-primary"),b(W,"class","content"),b(D,"class","txt-hint txt-sm txt-right"),b(w,"class","alert alert-info"),b(S,"class","section-title"),b(E,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(O,"class","tabs-content"),b(C,"class","tabs")},m(e,t){r(e,l,t),s(l,o),s(l,f),s(l,h),r(e,c,t),r(e,u,t),s(u,m),s(m,g),s(m,P),s(P,N),s(m,oe),s(u,se),s(u,G),r(e,K,t),Se(y,e,t),r(e,Q,t),r(e,I,t),r(e,F,t),r(e,w,t),s(w,R),s(w,ae),s(w,W),s(W,A),s(A,ne),s(A,J),s(J,V),s(A,ie),s(A,X),s(A,ce),s(w,re),s(w,D),r(e,Y,t),r(e,S,t),r(e,Z,t),r(e,E,t),r(e,x,t),r(e,B,t),r(e,ee,t),r(e,C,t),s(C,q);for(let p=0;p<$.length;p+=1)$[p].m(q,null);s(C,ue),s(C,O);for(let p=0;p<k.length;p+=1)k[p].m(O,null);T=!0},p(e,[t]){var ve,we,$e,ge;(!T||t&1)&&n!==(n=e[0].name+"")&&U(f,n),(!T||t&1)&&M!==(M=e[0].name+"")&&U(N,M);const p={};t&9&&(p.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -51,7 +51,7 @@ import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s
final result = await pb.collection('${(ge=e[0])==null?void 0:ge.name}').listExternalAuths(
pb.authStore.model.id,
);
`),y.$set(p),(!T||t&1)&&D!==(D=e[0].name+"")&&U(V,D),t&6&&(R=e[2],$=Pe($,t,fe,1,e,R,de,q,Oe,Ce,null,Ae)),t&6&&(O=e[2],Le(),k=Pe(k,t,me,1,e,O,pe,M,We,Te,null,ye),ze())},i(e){if(!T){te(y.$$.fragment,e);for(let t=0;t<O.length;t+=1)te(k[t]);T=!0}},o(e){le(y.$$.fragment,e);for(let t=0;t<k.length;t+=1)le(k[t]);T=!1},d(e){e&&d(l),e&&d(c),e&&d(u),e&&d(K),Ee(y,e),e&&d(Q),e&&d(I),e&&d(F),e&&d(w),e&&d(Y),e&&d(S),e&&d(Z),e&&d(E),e&&d(x),e&&d(B),e&&d(ee),e&&d(C);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Ke(a,l,o){let n,{collection:f=new De}=l,h=200,c=[];const u=m=>o(1,h=m.code);return a.$$set=m=>{"collection"in m&&o(0,f=m.collection)},a.$$.update=()=>{a.$$.dirty&1&&o(2,c=[{code:200,body:`
`),y.$set(p),(!T||t&1)&&z!==(z=e[0].name+"")&&U(V,z),t&6&&(H=e[2],$=Pe($,t,fe,1,e,H,de,q,Le,Ce,null,Ae)),t&6&&(L=e[2],Me(),k=Pe(k,t,me,1,e,L,pe,O,Re,Te,null,ye),We())},i(e){if(!T){te(y.$$.fragment,e);for(let t=0;t<L.length;t+=1)te(k[t]);T=!0}},o(e){le(y.$$.fragment,e);for(let t=0;t<k.length;t+=1)le(k[t]);T=!1},d(e){e&&d(l),e&&d(c),e&&d(u),e&&d(K),Ee(y,e),e&&d(Q),e&&d(I),e&&d(F),e&&d(w),e&&d(Y),e&&d(S),e&&d(Z),e&&d(E),e&&d(x),e&&d(B),e&&d(ee),e&&d(C);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Ke(a,l,o){let n,{collection:f=new ze}=l,h=200,c=[];const u=m=>o(1,h=m.code);return a.$$set=m=>{"collection"in m&&o(0,f=m.collection)},a.$$.update=()=>{a.$$.dirty&1&&o(2,c=[{code:200,body:`
[
{
"id": "8171022dc95a4e8",
@@ -90,4 +90,4 @@ import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s
"message": "The requested resource wasn't found.",
"data": {}
}
`}])},o(3,n=He.getApiExampleUrl(Re.baseUrl)),[f,h,c,n,u]}class Je extends Be{constructor(l){super(),qe(this,l,Ke,Ge,Me,{collection:0})}}export{Je as default};
`}])},o(3,n=De.getApiExampleUrl(He.baseUrl)),[f,h,c,n,u]}class Je extends Be{constructor(l){super(),qe(this,l,Ke,Ge,Oe,{collection:0})}}export{Je as default};
@@ -1,2 +1,2 @@
import{S as E,i as G,s as I,F as K,c as A,m as B,t as H,a as N,d as T,C as M,q as J,e as c,w as q,b as C,f as u,r as L,g as b,h as _,u as h,v as O,j as Q,l as U,o as w,A as V,p as W,B as X,D as Y,x as Z,z as S}from"./index.2a48c906.js";function y(f){let e,o,s;return{c(){e=q("for "),o=c("strong"),s=q(f[3]),u(o,"class","txt-nowrap")},m(l,t){b(l,e,t),b(l,o,t),_(o,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&w(e),l&&w(o)}}}function x(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0,t.autofocus=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[0]),t.focus(),p||(d=h(t,"input",f[6]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&1&&t.value!==n[0]&&S(t,n[0])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function ee(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password confirm"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[1]),p||(d=h(t,"input",f[7]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&2&&t.value!==n[1]&&S(t,n[1])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function te(f){let e,o,s,l,t,r,p,d,n,i,g,R,P,v,k,F,j,m=f[3]&&y(f);return r=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),d=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),{c(){e=c("form"),o=c("div"),s=c("h4"),l=q(`Reset your admin password
import{S as E,i as G,s as I,F as K,c as A,m as B,t as H,a as N,d as T,C as M,q as J,e as c,w as q,b as C,f as u,r as L,g as b,h as _,u as h,v as O,j as Q,l as U,o as w,A as V,p as W,B as X,D as Y,x as Z,z as S}from"./index.536fddcb.js";function y(f){let e,o,s;return{c(){e=q("for "),o=c("strong"),s=q(f[3]),u(o,"class","txt-nowrap")},m(l,t){b(l,e,t),b(l,o,t),_(o,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&w(e),l&&w(o)}}}function x(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0,t.autofocus=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[0]),t.focus(),p||(d=h(t,"input",f[6]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&1&&t.value!==n[0]&&S(t,n[0])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function ee(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password confirm"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[1]),p||(d=h(t,"input",f[7]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&2&&t.value!==n[1]&&S(t,n[1])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function te(f){let e,o,s,l,t,r,p,d,n,i,g,R,P,v,k,F,j,m=f[3]&&y(f);return r=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),d=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),{c(){e=c("form"),o=c("div"),s=c("h4"),l=q(`Reset your admin password
`),m&&m.c(),t=C(),A(r.$$.fragment),p=C(),A(d.$$.fragment),n=C(),i=c("button"),g=c("span"),g.textContent="Set new password",R=C(),P=c("div"),v=c("a"),v.textContent="Back to login",u(s,"class","m-b-xs"),u(o,"class","content txt-center m-b-sm"),u(g,"class","txt"),u(i,"type","submit"),u(i,"class","btn btn-lg btn-block"),i.disabled=f[2],L(i,"btn-loading",f[2]),u(e,"class","m-b-base"),u(v,"href","/login"),u(v,"class","link-hint"),u(P,"class","content txt-center")},m(a,$){b(a,e,$),_(e,o),_(o,s),_(s,l),m&&m.m(s,null),_(e,t),B(r,e,null),_(e,p),B(d,e,null),_(e,n),_(e,i),_(i,g),b(a,R,$),b(a,P,$),_(P,v),k=!0,F||(j=[h(e,"submit",O(f[4])),Q(U.call(null,v))],F=!0)},p(a,$){a[3]?m?m.p(a,$):(m=y(a),m.c(),m.m(s,null)):m&&(m.d(1),m=null);const z={};$&769&&(z.$$scope={dirty:$,ctx:a}),r.$set(z);const D={};$&770&&(D.$$scope={dirty:$,ctx:a}),d.$set(D),(!k||$&4)&&(i.disabled=a[2]),(!k||$&4)&&L(i,"btn-loading",a[2])},i(a){k||(H(r.$$.fragment,a),H(d.$$.fragment,a),k=!0)},o(a){N(r.$$.fragment,a),N(d.$$.fragment,a),k=!1},d(a){a&&w(e),m&&m.d(),T(r),T(d),a&&w(R),a&&w(P),F=!1,V(j)}}}function se(f){let e,o;return e=new K({props:{$$slots:{default:[te]},$$scope:{ctx:f}}}),{c(){A(e.$$.fragment)},m(s,l){B(e,s,l),o=!0},p(s,[l]){const t={};l&527&&(t.$$scope={dirty:l,ctx:s}),e.$set(t)},i(s){o||(H(e.$$.fragment,s),o=!0)},o(s){N(e.$$.fragment,s),o=!1},d(s){T(e,s)}}}function le(f,e,o){let s,{params:l}=e,t="",r="",p=!1;async function d(){if(!p){o(2,p=!0);try{await W.admins.confirmPasswordReset(l==null?void 0:l.token,t,r),X("Successfully set a new admin password."),Y("/")}catch(g){W.errorResponseHandler(g)}o(2,p=!1)}}function n(){t=this.value,o(0,t)}function i(){r=this.value,o(1,r)}return f.$$set=g=>{"params"in g&&o(5,l=g.params)},f.$$.update=()=>{f.$$.dirty&32&&o(3,s=M.getJWTPayload(l==null?void 0:l.token).email||"")},[t,r,p,s,d,l,n,i]}class ae extends E{constructor(e){super(),G(this,e,le,se,I,{params:5})}}export{ae as default};
@@ -1,2 +1,2 @@
import{S as M,i as T,s as j,F as z,c as H,m as L,t as w,a as y,d as S,b as g,e as _,f as p,g as k,h as d,j as A,l as B,k as N,n as D,o as v,p as C,q as G,r as F,u as E,v as I,w as h,x as J,y as P,z as R}from"./index.2a48c906.js";function K(c){let e,s,n,l,t,o,f,m,i,a,b,u;return l=new G({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:r})=>({5:r}),({uniqueId:r})=>r?32:0]},$$scope:{ctx:c}}}),{c(){e=_("form"),s=_("div"),s.innerHTML=`<h4 class="m-b-xs">Forgotten admin password</h4>
import{S as M,i as T,s as j,F as z,c as H,m as L,t as w,a as y,d as S,b as g,e as _,f as p,g as k,h as d,j as A,l as B,k as N,n as D,o as v,p as C,q as G,r as F,u as E,v as I,w as h,x as J,y as P,z as R}from"./index.536fddcb.js";function K(c){let e,s,n,l,t,o,f,m,i,a,b,u;return l=new G({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:r})=>({5:r}),({uniqueId:r})=>r?32:0]},$$scope:{ctx:c}}}),{c(){e=_("form"),s=_("div"),s.innerHTML=`<h4 class="m-b-xs">Forgotten admin password</h4>
<p>Enter the email associated with your account and we\u2019ll send you a recovery link:</p>`,n=g(),H(l.$$.fragment),t=g(),o=_("button"),f=_("i"),m=g(),i=_("span"),i.textContent="Send recovery link",p(s,"class","content txt-center m-b-sm"),p(f,"class","ri-mail-send-line"),p(i,"class","txt"),p(o,"type","submit"),p(o,"class","btn btn-lg btn-block"),o.disabled=c[1],F(o,"btn-loading",c[1]),p(e,"class","m-b-base")},m(r,$){k(r,e,$),d(e,s),d(e,n),L(l,e,null),d(e,t),d(e,o),d(o,f),d(o,m),d(o,i),a=!0,b||(u=E(e,"submit",I(c[3])),b=!0)},p(r,$){const q={};$&97&&(q.$$scope={dirty:$,ctx:r}),l.$set(q),(!a||$&2)&&(o.disabled=r[1]),(!a||$&2)&&F(o,"btn-loading",r[1])},i(r){a||(w(l.$$.fragment,r),a=!0)},o(r){y(l.$$.fragment,r),a=!1},d(r){r&&v(e),S(l),b=!1,u()}}}function O(c){let e,s,n,l,t,o,f,m,i;return{c(){e=_("div"),s=_("div"),s.innerHTML='<i class="ri-checkbox-circle-line"></i>',n=g(),l=_("div"),t=_("p"),o=h("Check "),f=_("strong"),m=h(c[0]),i=h(" for the recovery link."),p(s,"class","icon"),p(f,"class","txt-nowrap"),p(l,"class","content"),p(e,"class","alert alert-success")},m(a,b){k(a,e,b),d(e,s),d(e,n),d(e,l),d(l,t),d(t,o),d(t,f),d(f,m),d(t,i)},p(a,b){b&1&&J(m,a[0])},i:P,o:P,d(a){a&&v(e)}}}function Q(c){let e,s,n,l,t,o,f,m;return{c(){e=_("label"),s=h("Email"),l=g(),t=_("input"),p(e,"for",n=c[5]),p(t,"type","email"),p(t,"id",o=c[5]),t.required=!0,t.autofocus=!0},m(i,a){k(i,e,a),d(e,s),k(i,l,a),k(i,t,a),R(t,c[0]),t.focus(),f||(m=E(t,"input",c[4]),f=!0)},p(i,a){a&32&&n!==(n=i[5])&&p(e,"for",n),a&32&&o!==(o=i[5])&&p(t,"id",o),a&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&v(e),i&&v(l),i&&v(t),f=!1,m()}}}function U(c){let e,s,n,l,t,o,f,m;const i=[O,K],a=[];function b(u,r){return u[2]?0:1}return e=b(c),s=a[e]=i[e](c),{c(){s.c(),n=g(),l=_("div"),t=_("a"),t.textContent="Back to login",p(t,"href","/login"),p(t,"class","link-hint"),p(l,"class","content txt-center")},m(u,r){a[e].m(u,r),k(u,n,r),k(u,l,r),d(l,t),o=!0,f||(m=A(B.call(null,t)),f=!0)},p(u,r){let $=e;e=b(u),e===$?a[e].p(u,r):(N(),y(a[$],1,1,()=>{a[$]=null}),D(),s=a[e],s?s.p(u,r):(s=a[e]=i[e](u),s.c()),w(s,1),s.m(n.parentNode,n))},i(u){o||(w(s),o=!0)},o(u){y(s),o=!1},d(u){a[e].d(u),u&&v(n),u&&v(l),f=!1,m()}}}function V(c){let e,s;return e=new z({props:{$$slots:{default:[U]},$$scope:{ctx:c}}}),{c(){H(e.$$.fragment)},m(n,l){L(e,n,l),s=!0},p(n,[l]){const t={};l&71&&(t.$$scope={dirty:l,ctx:n}),e.$set(t)},i(n){s||(w(e.$$.fragment,n),s=!0)},o(n){y(e.$$.fragment,n),s=!1},d(n){S(e,n)}}}function W(c,e,s){let n="",l=!1,t=!1;async function o(){if(!l){s(1,l=!0);try{await C.admins.requestPasswordReset(n),s(2,t=!0)}catch(m){C.errorResponseHandler(m)}s(1,l=!1)}}function f(){n=this.value,s(0,n)}return[n,l,t,o,f]}class Y extends M{constructor(e){super(),T(this,e,W,V,j,{})}}export{Y as default};
@@ -1,4 +0,0 @@
import{S as z,i as G,s as I,F as J,c as S,m as T,t as v,a as y,d as L,C as M,E as N,g as _,k as W,n as Y,o as b,R as j,G as A,p as B,q as D,e as m,w as C,b as h,f as d,r as F,h as k,u as q,v as K,y as E,x as O,z as H}from"./index.2a48c906.js";function Q(r){let e,t,l,s,n,o,c,i,a,u,g,$,p=r[3]&&R(r);return o=new D({props:{class:"form-field required",name:"password",$$slots:{default:[V,({uniqueId:f})=>({8:f}),({uniqueId:f})=>f?256:0]},$$scope:{ctx:r}}}),{c(){e=m("form"),t=m("div"),l=m("h5"),s=C(`Type your password to confirm changing your email address
`),p&&p.c(),n=h(),S(o.$$.fragment),c=h(),i=m("button"),a=m("span"),a.textContent="Confirm new email",d(t,"class","content txt-center m-b-base"),d(a,"class","txt"),d(i,"type","submit"),d(i,"class","btn btn-lg btn-block"),i.disabled=r[1],F(i,"btn-loading",r[1])},m(f,w){_(f,e,w),k(e,t),k(t,l),k(l,s),p&&p.m(l,null),k(e,n),T(o,e,null),k(e,c),k(e,i),k(i,a),u=!0,g||($=q(e,"submit",K(r[4])),g=!0)},p(f,w){f[3]?p?p.p(f,w):(p=R(f),p.c(),p.m(l,null)):p&&(p.d(1),p=null);const P={};w&769&&(P.$$scope={dirty:w,ctx:f}),o.$set(P),(!u||w&2)&&(i.disabled=f[1]),(!u||w&2)&&F(i,"btn-loading",f[1])},i(f){u||(v(o.$$.fragment,f),u=!0)},o(f){y(o.$$.fragment,f),u=!1},d(f){f&&b(e),p&&p.d(),L(o),g=!1,$()}}}function U(r){let e,t,l,s,n;return{c(){e=m("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully changed the user email address.</p>
<p>You can now sign in with your new email address.</p></div>`,t=h(),l=m("button"),l.textContent="Close",d(e,"class","alert alert-success"),d(l,"type","button"),d(l,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,t,c),_(o,l,c),s||(n=q(l,"click",r[6]),s=!0)},p:E,i:E,o:E,d(o){o&&b(e),o&&b(t),o&&b(l),s=!1,n()}}}function R(r){let e,t,l;return{c(){e=C("to "),t=m("strong"),l=C(r[3]),d(t,"class","txt-nowrap")},m(s,n){_(s,e,n),_(s,t,n),k(t,l)},p(s,n){n&8&&O(l,s[3])},d(s){s&&b(e),s&&b(t)}}}function V(r){let e,t,l,s,n,o,c,i;return{c(){e=m("label"),t=C("Password"),s=h(),n=m("input"),d(e,"for",l=r[8]),d(n,"type","password"),d(n,"id",o=r[8]),n.required=!0,n.autofocus=!0},m(a,u){_(a,e,u),k(e,t),_(a,s,u),_(a,n,u),H(n,r[0]),n.focus(),c||(i=q(n,"input",r[7]),c=!0)},p(a,u){u&256&&l!==(l=a[8])&&d(e,"for",l),u&256&&o!==(o=a[8])&&d(n,"id",o),u&1&&n.value!==a[0]&&H(n,a[0])},d(a){a&&b(e),a&&b(s),a&&b(n),c=!1,i()}}}function X(r){let e,t,l,s;const n=[U,Q],o=[];function c(i,a){return i[2]?0:1}return e=c(r),t=o[e]=n[e](r),{c(){t.c(),l=N()},m(i,a){o[e].m(i,a),_(i,l,a),s=!0},p(i,a){let u=e;e=c(i),e===u?o[e].p(i,a):(W(),y(o[u],1,1,()=>{o[u]=null}),Y(),t=o[e],t?t.p(i,a):(t=o[e]=n[e](i),t.c()),v(t,1),t.m(l.parentNode,l))},i(i){s||(v(t),s=!0)},o(i){y(t),s=!1},d(i){o[e].d(i),i&&b(l)}}}function Z(r){let e,t;return e=new J({props:{nobranding:!0,$$slots:{default:[X]},$$scope:{ctx:r}}}),{c(){S(e.$$.fragment)},m(l,s){T(e,l,s),t=!0},p(l,[s]){const n={};s&527&&(n.$$scope={dirty:s,ctx:l}),e.$set(n)},i(l){t||(v(e.$$.fragment,l),t=!0)},o(l){y(e.$$.fragment,l),t=!1},d(l){L(e,l)}}}function x(r,e,t){let l,{params:s}=e,n="",o=!1,c=!1;async function i(){if(o)return;t(1,o=!0);const g=new j("../");try{const $=A(s==null?void 0:s.token);await g.collection($.collectionId).confirmEmailChange(s==null?void 0:s.token,n),t(2,c=!0)}catch($){B.errorResponseHandler($)}t(1,o=!1)}const a=()=>window.close();function u(){n=this.value,t(0,n)}return r.$$set=g=>{"params"in g&&t(5,s=g.params)},r.$$.update=()=>{r.$$.dirty&32&&t(3,l=M.getJWTPayload(s==null?void 0:s.token).newEmail||"")},[n,o,c,l,i,s,a,u]}class te extends z{constructor(e){super(),G(this,e,x,Z,I,{params:5})}}export{te as default};
@@ -0,0 +1,4 @@
import{S as z,i as A,s as G,F as I,c as T,m as L,t as v,a as y,d as R,C as J,E as M,g as _,k as N,n as W,o as b,G as Y,H as j,p as B,q as D,e as m,w as C,b as h,f as d,r as P,h as k,u as q,v as K,y as E,x as O,z as F}from"./index.536fddcb.js";function Q(r){let e,t,s,l,n,o,c,i,a,u,g,$,p=r[3]&&S(r);return o=new D({props:{class:"form-field required",name:"password",$$slots:{default:[V,({uniqueId:f})=>({8:f}),({uniqueId:f})=>f?256:0]},$$scope:{ctx:r}}}),{c(){e=m("form"),t=m("div"),s=m("h5"),l=C(`Type your password to confirm changing your email address
`),p&&p.c(),n=h(),T(o.$$.fragment),c=h(),i=m("button"),a=m("span"),a.textContent="Confirm new email",d(t,"class","content txt-center m-b-base"),d(a,"class","txt"),d(i,"type","submit"),d(i,"class","btn btn-lg btn-block"),i.disabled=r[1],P(i,"btn-loading",r[1])},m(f,w){_(f,e,w),k(e,t),k(t,s),k(s,l),p&&p.m(s,null),k(e,n),L(o,e,null),k(e,c),k(e,i),k(i,a),u=!0,g||($=q(e,"submit",K(r[4])),g=!0)},p(f,w){f[3]?p?p.p(f,w):(p=S(f),p.c(),p.m(s,null)):p&&(p.d(1),p=null);const H={};w&769&&(H.$$scope={dirty:w,ctx:f}),o.$set(H),(!u||w&2)&&(i.disabled=f[1]),(!u||w&2)&&P(i,"btn-loading",f[1])},i(f){u||(v(o.$$.fragment,f),u=!0)},o(f){y(o.$$.fragment,f),u=!1},d(f){f&&b(e),p&&p.d(),R(o),g=!1,$()}}}function U(r){let e,t,s,l,n;return{c(){e=m("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully changed the user email address.</p>
<p>You can now sign in with your new email address.</p></div>`,t=h(),s=m("button"),s.textContent="Close",d(e,"class","alert alert-success"),d(s,"type","button"),d(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,t,c),_(o,s,c),l||(n=q(s,"click",r[6]),l=!0)},p:E,i:E,o:E,d(o){o&&b(e),o&&b(t),o&&b(s),l=!1,n()}}}function S(r){let e,t,s;return{c(){e=C("to "),t=m("strong"),s=C(r[3]),d(t,"class","txt-nowrap")},m(l,n){_(l,e,n),_(l,t,n),k(t,s)},p(l,n){n&8&&O(s,l[3])},d(l){l&&b(e),l&&b(t)}}}function V(r){let e,t,s,l,n,o,c,i;return{c(){e=m("label"),t=C("Password"),l=h(),n=m("input"),d(e,"for",s=r[8]),d(n,"type","password"),d(n,"id",o=r[8]),n.required=!0,n.autofocus=!0},m(a,u){_(a,e,u),k(e,t),_(a,l,u),_(a,n,u),F(n,r[0]),n.focus(),c||(i=q(n,"input",r[7]),c=!0)},p(a,u){u&256&&s!==(s=a[8])&&d(e,"for",s),u&256&&o!==(o=a[8])&&d(n,"id",o),u&1&&n.value!==a[0]&&F(n,a[0])},d(a){a&&b(e),a&&b(l),a&&b(n),c=!1,i()}}}function X(r){let e,t,s,l;const n=[U,Q],o=[];function c(i,a){return i[2]?0:1}return e=c(r),t=o[e]=n[e](r),{c(){t.c(),s=M()},m(i,a){o[e].m(i,a),_(i,s,a),l=!0},p(i,a){let u=e;e=c(i),e===u?o[e].p(i,a):(N(),y(o[u],1,1,()=>{o[u]=null}),W(),t=o[e],t?t.p(i,a):(t=o[e]=n[e](i),t.c()),v(t,1),t.m(s.parentNode,s))},i(i){l||(v(t),l=!0)},o(i){y(t),l=!1},d(i){o[e].d(i),i&&b(s)}}}function Z(r){let e,t;return e=new I({props:{nobranding:!0,$$slots:{default:[X]},$$scope:{ctx:r}}}),{c(){T(e.$$.fragment)},m(s,l){L(e,s,l),t=!0},p(s,[l]){const n={};l&527&&(n.$$scope={dirty:l,ctx:s}),e.$set(n)},i(s){t||(v(e.$$.fragment,s),t=!0)},o(s){y(e.$$.fragment,s),t=!1},d(s){R(e,s)}}}function x(r,e,t){let s,{params:l}=e,n="",o=!1,c=!1;async function i(){if(o)return;t(1,o=!0);const g=new Y("../");try{const $=j(l==null?void 0:l.token);await g.collection($.collectionId).confirmEmailChange(l==null?void 0:l.token,n),t(2,c=!0)}catch($){B.errorResponseHandler($)}t(1,o=!1)}const a=()=>window.close();function u(){n=this.value,t(0,n)}return r.$$set=g=>{"params"in g&&t(5,l=g.params)},r.$$.update=()=>{r.$$.dirty&32&&t(3,s=J.getJWTPayload(l==null?void 0:l.token).newEmail||"")},[n,o,c,s,i,l,a,u]}class te extends z{constructor(e){super(),A(this,e,x,Z,G,{params:5})}}export{te as default};
@@ -1,4 +1,4 @@
import{S as J,i as M,s as W,F as Y,c as H,m as N,t as P,a as q,d as L,C as j,E as A,g as _,k as B,n as D,o as m,R as K,G as O,p as Q,q as E,e as b,w as R,b as y,f as p,r as G,h as w,u as S,v as U,y as F,x as V,z as h}from"./index.2a48c906.js";function X(r){let e,l,s,n,t,o,c,u,i,a,v,k,g,C,d=r[4]&&I(r);return o=new E({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),u=new E({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=R(`Reset your user password
`),d&&d.c(),t=y(),H(o.$$.fragment),c=y(),H(u.$$.fragment),i=y(),a=b("button"),v=b("span"),v.textContent="Set new password",p(l,"class","content txt-center m-b-base"),p(v,"class","txt"),p(a,"type","submit"),p(a,"class","btn btn-lg btn-block"),a.disabled=r[2],G(a,"btn-loading",r[2])},m(f,$){_(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),N(o,e,null),w(e,c),N(u,e,null),w(e,i),w(e,a),w(a,v),k=!0,g||(C=S(e,"submit",U(r[5])),g=!0)},p(f,$){f[4]?d?d.p(f,$):(d=I(f),d.c(),d.m(s,null)):d&&(d.d(1),d=null);const T={};$&3073&&(T.$$scope={dirty:$,ctx:f}),o.$set(T);const z={};$&3074&&(z.$$scope={dirty:$,ctx:f}),u.$set(z),(!k||$&4)&&(a.disabled=f[2]),(!k||$&4)&&G(a,"btn-loading",f[2])},i(f){k||(P(o.$$.fragment,f),P(u.$$.fragment,f),k=!0)},o(f){q(o.$$.fragment,f),q(u.$$.fragment,f),k=!1},d(f){f&&m(e),d&&d.d(),L(o),L(u),g=!1,C()}}}function Z(r){let e,l,s,n,t;return{c(){e=b("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
import{S as I,i as J,s as M,F as W,c as F,m as N,t as P,a as q,d as L,C as Y,E as j,g as _,k as B,n as D,o as m,G as K,H as O,p as Q,q as A,e as b,w as h,b as y,f as p,r as E,h as w,u as H,v as U,y as S,x as V,z as R}from"./index.536fddcb.js";function X(r){let e,l,s,n,t,o,c,u,i,a,v,k,g,C,d=r[4]&&G(r);return o=new A({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),u=new A({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=h(`Reset your user password
`),d&&d.c(),t=y(),F(o.$$.fragment),c=y(),F(u.$$.fragment),i=y(),a=b("button"),v=b("span"),v.textContent="Set new password",p(l,"class","content txt-center m-b-base"),p(v,"class","txt"),p(a,"type","submit"),p(a,"class","btn btn-lg btn-block"),a.disabled=r[2],E(a,"btn-loading",r[2])},m(f,$){_(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),N(o,e,null),w(e,c),N(u,e,null),w(e,i),w(e,a),w(a,v),k=!0,g||(C=H(e,"submit",U(r[5])),g=!0)},p(f,$){f[4]?d?d.p(f,$):(d=G(f),d.c(),d.m(s,null)):d&&(d.d(1),d=null);const T={};$&3073&&(T.$$scope={dirty:$,ctx:f}),o.$set(T);const z={};$&3074&&(z.$$scope={dirty:$,ctx:f}),u.$set(z),(!k||$&4)&&(a.disabled=f[2]),(!k||$&4)&&E(a,"btn-loading",f[2])},i(f){k||(P(o.$$.fragment,f),P(u.$$.fragment,f),k=!0)},o(f){q(o.$$.fragment,f),q(u.$$.fragment,f),k=!1},d(f){f&&m(e),d&&d.d(),L(o),L(u),g=!1,C()}}}function Z(r){let e,l,s,n,t;return{c(){e=b("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully changed the user password.</p>
<p>You can now sign in with your new password.</p></div>`,l=y(),s=b("button"),s.textContent="Close",p(e,"class","alert alert-success"),p(s,"type","button"),p(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,l,c),_(o,s,c),n||(t=S(s,"click",r[7]),n=!0)},p:F,i:F,o:F,d(o){o&&m(e),o&&m(l),o&&m(s),n=!1,t()}}}function I(r){let e,l,s;return{c(){e=R("for "),l=b("strong"),s=R(r[4])},m(n,t){_(n,e,t),_(n,l,t),w(l,s)},p(n,t){t&16&&V(s,n[4])},d(n){n&&m(e),n&&m(l)}}}function x(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=R("New password"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0,t.autofocus=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),h(t,r[0]),t.focus(),c||(u=S(t,"input",r[8]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&1&&t.value!==i[0]&&h(t,i[0])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function ee(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=R("New password confirm"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),h(t,r[1]),c||(u=S(t,"input",r[9]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&2&&t.value!==i[1]&&h(t,i[1])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function te(r){let e,l,s,n;const t=[Z,X],o=[];function c(u,i){return u[3]?0:1}return e=c(r),l=o[e]=t[e](r),{c(){l.c(),s=A()},m(u,i){o[e].m(u,i),_(u,s,i),n=!0},p(u,i){let a=e;e=c(u),e===a?o[e].p(u,i):(B(),q(o[a],1,1,()=>{o[a]=null}),D(),l=o[e],l?l.p(u,i):(l=o[e]=t[e](u),l.c()),P(l,1),l.m(s.parentNode,s))},i(u){n||(P(l),n=!0)},o(u){q(l),n=!1},d(u){o[e].d(u),u&&m(s)}}}function se(r){let e,l;return e=new Y({props:{nobranding:!0,$$slots:{default:[te]},$$scope:{ctx:r}}}),{c(){H(e.$$.fragment)},m(s,n){N(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(P(e.$$.fragment,s),l=!0)},o(s){q(e.$$.fragment,s),l=!1},d(s){L(e,s)}}}function le(r,e,l){let s,{params:n}=e,t="",o="",c=!1,u=!1;async function i(){if(c)return;l(2,c=!0);const g=new K("../");try{const C=O(n==null?void 0:n.token);await g.collection(C.collectionId).confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,u=!0)}catch(C){Q.errorResponseHandler(C)}l(2,c=!1)}const a=()=>window.close();function v(){t=this.value,l(0,t)}function k(){o=this.value,l(1,o)}return r.$$set=g=>{"params"in g&&l(6,n=g.params)},r.$$.update=()=>{r.$$.dirty&64&&l(4,s=j.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,c,u,s,i,n,a,v,k]}class oe extends J{constructor(e){super(),M(this,e,le,se,W,{params:6})}}export{oe as default};
<p>You can now sign in with your new password.</p></div>`,l=y(),s=b("button"),s.textContent="Close",p(e,"class","alert alert-success"),p(s,"type","button"),p(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,l,c),_(o,s,c),n||(t=H(s,"click",r[7]),n=!0)},p:S,i:S,o:S,d(o){o&&m(e),o&&m(l),o&&m(s),n=!1,t()}}}function G(r){let e,l,s;return{c(){e=h("for "),l=b("strong"),s=h(r[4])},m(n,t){_(n,e,t),_(n,l,t),w(l,s)},p(n,t){t&16&&V(s,n[4])},d(n){n&&m(e),n&&m(l)}}}function x(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=h("New password"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0,t.autofocus=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),R(t,r[0]),t.focus(),c||(u=H(t,"input",r[8]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function ee(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=h("New password confirm"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),R(t,r[1]),c||(u=H(t,"input",r[9]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&2&&t.value!==i[1]&&R(t,i[1])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function te(r){let e,l,s,n;const t=[Z,X],o=[];function c(u,i){return u[3]?0:1}return e=c(r),l=o[e]=t[e](r),{c(){l.c(),s=j()},m(u,i){o[e].m(u,i),_(u,s,i),n=!0},p(u,i){let a=e;e=c(u),e===a?o[e].p(u,i):(B(),q(o[a],1,1,()=>{o[a]=null}),D(),l=o[e],l?l.p(u,i):(l=o[e]=t[e](u),l.c()),P(l,1),l.m(s.parentNode,s))},i(u){n||(P(l),n=!0)},o(u){q(l),n=!1},d(u){o[e].d(u),u&&m(s)}}}function se(r){let e,l;return e=new W({props:{nobranding:!0,$$slots:{default:[te]},$$scope:{ctx:r}}}),{c(){F(e.$$.fragment)},m(s,n){N(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(P(e.$$.fragment,s),l=!0)},o(s){q(e.$$.fragment,s),l=!1},d(s){L(e,s)}}}function le(r,e,l){let s,{params:n}=e,t="",o="",c=!1,u=!1;async function i(){if(c)return;l(2,c=!0);const g=new K("../");try{const C=O(n==null?void 0:n.token);await g.collection(C.collectionId).confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,u=!0)}catch(C){Q.errorResponseHandler(C)}l(2,c=!1)}const a=()=>window.close();function v(){t=this.value,l(0,t)}function k(){o=this.value,l(1,o)}return r.$$set=g=>{"params"in g&&l(6,n=g.params)},r.$$.update=()=>{r.$$.dirty&64&&l(4,s=Y.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,c,u,s,i,n,a,v,k]}class oe extends I{constructor(e){super(),J(this,e,le,se,M,{params:6})}}export{oe as default};
@@ -1,3 +1,3 @@
import{S as v,i as y,s as w,F as x,c as C,m as g,t as $,a as L,d as H,R as M,G as P,E as S,g as r,o as a,e as u,b as _,f,u as b,y as p}from"./index.2a48c906.js";function T(o){let t,s,e,n,l;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-error-warning-line"></i></div>
import{S as v,i as y,s as w,F as x,c as C,m as g,t as $,a as H,d as L,G as M,H as P,E as S,g as r,o as a,e as u,b as _,f,u as b,y as p}from"./index.536fddcb.js";function T(o){let t,s,e,n,l;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-error-warning-line"></i></div>
<div class="content txt-bold"><p>Invalid or expired verification token.</p></div>`,s=_(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-danger"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(i,c){r(i,t,c),r(i,s,c),r(i,e,c),n||(l=b(e,"click",o[4]),n=!0)},p,d(i){i&&a(t),i&&a(s),i&&a(e),n=!1,l()}}}function F(o){let t,s,e,n,l;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully verified email address.</p></div>`,s=_(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-success"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(i,c){r(i,t,c),r(i,s,c),r(i,e,c),n||(l=b(e,"click",o[3]),n=!0)},p,d(i){i&&a(t),i&&a(s),i&&a(e),n=!1,l()}}}function I(o){let t;return{c(){t=u("div"),t.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',f(t,"class","txt-center")},m(s,e){r(s,t,e)},p,d(s){s&&a(t)}}}function R(o){let t;function s(l,i){return l[1]?I:l[0]?F:T}let e=s(o),n=e(o);return{c(){n.c(),t=S()},m(l,i){n.m(l,i),r(l,t,i)},p(l,i){e===(e=s(l))&&n?n.p(l,i):(n.d(1),n=e(l),n&&(n.c(),n.m(t.parentNode,t)))},d(l){n.d(l),l&&a(t)}}}function V(o){let t,s;return t=new x({props:{nobranding:!0,$$slots:{default:[R]},$$scope:{ctx:o}}}),{c(){C(t.$$.fragment)},m(e,n){g(t,e,n),s=!0},p(e,[n]){const l={};n&67&&(l.$$scope={dirty:n,ctx:e}),t.$set(l)},i(e){s||($(t.$$.fragment,e),s=!0)},o(e){L(t.$$.fragment,e),s=!1},d(e){H(t,e)}}}function q(o,t,s){let{params:e}=t,n=!1,l=!1;i();async function i(){s(1,l=!0);const d=new M("../");try{const m=P(e==null?void 0:e.token);await d.collection(m.collectionId).confirmVerification(e==null?void 0:e.token),s(0,n=!0)}catch{s(0,n=!1)}s(1,l=!1)}const c=()=>window.close(),k=()=>window.close();return o.$$set=d=>{"params"in d&&s(2,e=d.params)},[n,l,e,c,k]}class G extends v{constructor(t){super(),y(this,t,q,V,w,{params:2})}}export{G as default};
<div class="content txt-bold"><p>Successfully verified email address.</p></div>`,s=_(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-success"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(i,c){r(i,t,c),r(i,s,c),r(i,e,c),n||(l=b(e,"click",o[3]),n=!0)},p,d(i){i&&a(t),i&&a(s),i&&a(e),n=!1,l()}}}function I(o){let t;return{c(){t=u("div"),t.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',f(t,"class","txt-center")},m(s,e){r(s,t,e)},p,d(s){s&&a(t)}}}function V(o){let t;function s(l,i){return l[1]?I:l[0]?F:T}let e=s(o),n=e(o);return{c(){n.c(),t=S()},m(l,i){n.m(l,i),r(l,t,i)},p(l,i){e===(e=s(l))&&n?n.p(l,i):(n.d(1),n=e(l),n&&(n.c(),n.m(t.parentNode,t)))},d(l){n.d(l),l&&a(t)}}}function q(o){let t,s;return t=new x({props:{nobranding:!0,$$slots:{default:[V]},$$scope:{ctx:o}}}),{c(){C(t.$$.fragment)},m(e,n){g(t,e,n),s=!0},p(e,[n]){const l={};n&67&&(l.$$scope={dirty:n,ctx:e}),t.$set(l)},i(e){s||($(t.$$.fragment,e),s=!0)},o(e){H(t.$$.fragment,e),s=!1},d(e){L(t,e)}}}function A(o,t,s){let{params:e}=t,n=!1,l=!1;i();async function i(){s(1,l=!0);const d=new M("../");try{const m=P(e==null?void 0:e.token);await d.collection(m.collectionId).confirmVerification(e==null?void 0:e.token),s(0,n=!0)}catch{s(0,n=!1)}s(1,l=!1)}const c=()=>window.close(),k=()=>window.close();return o.$$set=d=>{"params"in d&&s(2,e=d.params)},[n,l,e,c,k]}class G extends v{constructor(t){super(),y(this,t,A,q,w,{params:2})}}export{G as default};
@@ -1,4 +1,4 @@
import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as p,g as t,h as I,m as ne,x as pe,t as ie,a as le,o as n,d as ce,Q as me,p as de}from"./index.2a48c906.js";import{S as fe}from"./SdkTabs.9dbb2ca3.js";function $e(o){var B,U,W,A,H,L,M,T,q,j,J,N;let i,m,l=o[0].name+"",b,d,h,f,_,$,k,c,S,v,w,R,C,g,E,r,D;return c=new fe({props:{js:`
import{S as re,i as ae,s as be,N as ue,C as P,e as u,w as y,b as a,c as te,f as p,g as t,h as I,m as ne,x as pe,t as ie,a as le,o as n,d as ce,R as me,p as de}from"./index.536fddcb.js";import{S as fe}from"./SdkTabs.3571c26b.js";function $e(o){var B,U,W,A,H,L,T,q,M,N,j,J;let i,m,l=o[0].name+"",b,d,h,f,_,$,k,c,S,v,w,R,C,g,E,r,D;return c=new fe({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${o[1]}');
@@ -32,20 +32,20 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
// (Optionally) authenticate
await pb.collection('users').authWithPassword('test@example.com', '123456');
// Subscribe to changes in any ${(M=o[0])==null?void 0:M.name} record
pb.collection('${(T=o[0])==null?void 0:T.name}').subscribe('*', (e) {
// Subscribe to changes in any ${(T=o[0])==null?void 0:T.name} record
pb.collection('${(q=o[0])==null?void 0:q.name}').subscribe('*', (e) {
console.log(e.record);
});
// Subscribe to changes only in the specified record
pb.collection('${(q=o[0])==null?void 0:q.name}').subscribe('RECORD_ID', (e) {
pb.collection('${(M=o[0])==null?void 0:M.name}').subscribe('RECORD_ID', (e) {
console.log(e.record);
});
// Unsubscribe
pb.collection('${(j=o[0])==null?void 0:j.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
pb.collection('${(J=o[0])==null?void 0:J.name}').unsubscribe('*'); // remove all '*' topic subscriptions
pb.collection('${(N=o[0])==null?void 0:N.name}').unsubscribe(); // remove all subscriptions in the collection
pb.collection('${(N=o[0])==null?void 0:N.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
pb.collection('${(j=o[0])==null?void 0:j.name}').unsubscribe('*'); // remove all '*' topic subscriptions
pb.collection('${(J=o[0])==null?void 0:J.name}').unsubscribe(); // remove all subscriptions in the collection
`}}),r=new ue({props:{content:JSON.stringify({action:"create",record:P.dummyCollectionRecord(o[0])},null,2).replace('"action": "create"','"action": "create" // create, update or delete')}}),{c(){i=u("h3"),m=y("Realtime ("),b=y(l),d=y(")"),h=a(),f=u("div"),f.innerHTML=`<p>Subscribe to realtime changes via Server-Sent Events (SSE).</p>
<p>Events are sent for <strong>create</strong>, <strong>update</strong>
and <strong>delete</strong> record operations (see &quot;Event data format&quot; section below).</p>`,_=a(),$=u("div"),$.innerHTML=`<div class="icon"><i class="ri-information-line"></i></div>
@@ -56,7 +56,7 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
<p>When you subscribe to an <strong>entire collection</strong>, the collection&#39;s
<strong>ListRule</strong> will be used to determine whether the subscriber has access to receive the
event message.</p></div>`,k=a(),te(c.$$.fragment),S=a(),v=u("h6"),v.textContent="API details",w=a(),R=u("div"),R.innerHTML=`<strong class="label label-primary">SSE</strong>
<div class="content"><p>/api/realtime</p></div>`,C=a(),g=u("div"),g.textContent="Event data format",E=a(),te(r.$$.fragment),p(i,"class","m-b-sm"),p(f,"class","content txt-lg m-b-sm"),p($,"class","alert alert-info m-t-10 m-b-sm"),p(v,"class","m-b-xs"),p(R,"class","alert"),p(g,"class","section-title")},m(e,s){t(e,i,s),I(i,m),I(i,b),I(i,d),t(e,h,s),t(e,f,s),t(e,_,s),t(e,$,s),t(e,k,s),ne(c,e,s),t(e,S,s),t(e,v,s),t(e,w,s),t(e,R,s),t(e,C,s),t(e,g,s),t(e,E,s),ne(r,e,s),D=!0},p(e,[s]){var V,Y,z,F,G,K,X,Z,x,ee,se,oe;(!D||s&1)&&l!==(l=e[0].name+"")&&pe(b,l);const O={};s&3&&(O.js=`
<div class="content"><p>/api/realtime</p></div>`,C=a(),g=u("div"),g.textContent="Event data format",E=a(),te(r.$$.fragment),p(i,"class","m-b-sm"),p(f,"class","content txt-lg m-b-sm"),p($,"class","alert alert-info m-t-10 m-b-sm"),p(v,"class","m-b-xs"),p(R,"class","alert"),p(g,"class","section-title")},m(e,s){t(e,i,s),I(i,m),I(i,b),I(i,d),t(e,h,s),t(e,f,s),t(e,_,s),t(e,$,s),t(e,k,s),ne(c,e,s),t(e,S,s),t(e,v,s),t(e,w,s),t(e,R,s),t(e,C,s),t(e,g,s),t(e,E,s),ne(r,e,s),D=!0},p(e,[s]){var Y,z,F,G,K,Q,X,Z,x,ee,se,oe;(!D||s&1)&&l!==(l=e[0].name+"")&&pe(b,l);const O={};s&3&&(O.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[1]}');
@@ -66,20 +66,20 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
// (Optionally) authenticate
await pb.collection('users').authWithPassword('test@example.com', '123456');
// Subscribe to changes in any ${(V=e[0])==null?void 0:V.name} record
pb.collection('${(Y=e[0])==null?void 0:Y.name}').subscribe('*', function (e) {
// Subscribe to changes in any ${(Y=e[0])==null?void 0:Y.name} record
pb.collection('${(z=e[0])==null?void 0:z.name}').subscribe('*', function (e) {
console.log(e.record);
});
// Subscribe to changes only in the specified record
pb.collection('${(z=e[0])==null?void 0:z.name}').subscribe('RECORD_ID', function (e) {
pb.collection('${(F=e[0])==null?void 0:F.name}').subscribe('RECORD_ID', function (e) {
console.log(e.record);
});
// Unsubscribe
pb.collection('${(F=e[0])==null?void 0:F.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
pb.collection('${(G=e[0])==null?void 0:G.name}').unsubscribe('*'); // remove all '*' topic subscriptions
pb.collection('${(K=e[0])==null?void 0:K.name}').unsubscribe(); // remove all subscriptions in the collection
pb.collection('${(G=e[0])==null?void 0:G.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
pb.collection('${(K=e[0])==null?void 0:K.name}').unsubscribe('*'); // remove all '*' topic subscriptions
pb.collection('${(Q=e[0])==null?void 0:Q.name}').unsubscribe(); // remove all subscriptions in the collection
`),s&3&&(O.dart=`
import 'package:pocketbase/pocketbase.dart';
@@ -104,4 +104,4 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
pb.collection('${(ee=e[0])==null?void 0:ee.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
pb.collection('${(se=e[0])==null?void 0:se.name}').unsubscribe('*'); // remove all '*' topic subscriptions
pb.collection('${(oe=e[0])==null?void 0:oe.name}').unsubscribe(); // remove all subscriptions in the collection
`),c.$set(O);const Q={};s&1&&(Q.content=JSON.stringify({action:"create",record:P.dummyCollectionRecord(e[0])},null,2).replace('"action": "create"','"action": "create" // create, update or delete')),r.$set(Q)},i(e){D||(ie(c.$$.fragment,e),ie(r.$$.fragment,e),D=!0)},o(e){le(c.$$.fragment,e),le(r.$$.fragment,e),D=!1},d(e){e&&n(i),e&&n(h),e&&n(f),e&&n(_),e&&n($),e&&n(k),ce(c,e),e&&n(S),e&&n(v),e&&n(w),e&&n(R),e&&n(C),e&&n(g),e&&n(E),ce(r,e)}}}function ve(o,i,m){let l,{collection:b=new me}=i;return o.$$set=d=>{"collection"in d&&m(0,b=d.collection)},m(1,l=P.getApiExampleUrl(de.baseUrl)),[b,l]}class De extends re{constructor(i){super(),ae(this,i,ve,$e,be,{collection:0})}}export{De as default};
`),c.$set(O);const V={};s&1&&(V.content=JSON.stringify({action:"create",record:P.dummyCollectionRecord(e[0])},null,2).replace('"action": "create"','"action": "create" // create, update or delete')),r.$set(V)},i(e){D||(ie(c.$$.fragment,e),ie(r.$$.fragment,e),D=!0)},o(e){le(c.$$.fragment,e),le(r.$$.fragment,e),D=!1},d(e){e&&n(i),e&&n(h),e&&n(f),e&&n(_),e&&n($),e&&n(k),ce(c,e),e&&n(S),e&&n(v),e&&n(w),e&&n(R),e&&n(C),e&&n(g),e&&n(E),ce(r,e)}}}function ve(o,i,m){let l,{collection:b=new me}=i;return o.$$set=d=>{"collection"in d&&m(0,b=d.collection)},m(1,l=P.getApiExampleUrl(de.baseUrl)),[b,l]}class De extends re{constructor(i){super(),ae(this,i,ve,$e,be,{collection:0})}}export{De as default};

Some files were not shown because too many files have changed in this diff Show More