astro-loader-pocketbase 2.8.2-next.2 → 2.8.2-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/package.json +8 -7
- package/src/loader/cleanup-entries.ts +27 -6
- package/src/loader/fetch-collection.ts +13 -6
- package/src/loader/fetch-entry.ts +9 -5
- package/src/loader/live-collection-loader.ts +1 -2
- package/src/loader/live-entry-loader.ts +1 -1
- package/src/pocketbase-loader.ts +3 -3
- package/src/schema/get-remote-schema.ts +8 -3
- package/src/schema/parse-schema.ts +2 -0
- package/src/schema/read-local-schema.ts +4 -3
- package/src/schema/transform-files.ts +3 -2
- package/src/types/pocketbase-api-response.type.ts +40 -0
- package/src/types/pocketbase-entry.type.ts +19 -7
- package/src/types/pocketbase-schema.type.ts +47 -15
- package/src/utils/get-superuser-token.ts +27 -3
package/README.md
CHANGED
|
@@ -172,7 +172,9 @@ const blog = defineCollection({
|
|
|
172
172
|
});
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
> [!TIP]
|
|
176
|
+
> It's recommended to use an [impersonate token (API token)](https://pocketbase.io/docs/authentication/#api-keys) instead of the email and password, as this is more secure and can be easily revoked.
|
|
177
|
+
> This also prevents the loader from hitting some rate limits with PocketBase, since the default is 2 authentication requests per 3 second interval.
|
|
176
178
|
|
|
177
179
|
Under the hood, the loader will use the [PocketBase API](https://pocketbase.io/docs/api-collections/#view-collection) to fetch the schema of your collection and generate types with Zod based on that schema.
|
|
178
180
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-loader-pocketbase",
|
|
3
|
-
"version": "2.8.2-next.
|
|
3
|
+
"version": "2.8.2-next.4",
|
|
4
4
|
"description": "A content loader for Astro that uses the PocketBase API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"format": "npx prettier . --write --cache",
|
|
31
31
|
"format:check": "npx prettier . --check --cache",
|
|
32
|
-
"lint": "oxlint",
|
|
33
|
-
"lint:fix": "oxlint --fix",
|
|
32
|
+
"lint": "oxlint --type-aware",
|
|
33
|
+
"lint:fix": "oxlint --type-aware --fix",
|
|
34
34
|
"prepare": "husky",
|
|
35
35
|
"test": "vitest run",
|
|
36
36
|
"test:e2e": "vitest run $(find test -name '*.e2e-spec.ts')",
|
|
@@ -45,13 +45,14 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@commitlint/cli": "20.1.0",
|
|
47
47
|
"@commitlint/config-conventional": "20.0.0",
|
|
48
|
-
"@types/node": "24.
|
|
48
|
+
"@types/node": "24.9.1",
|
|
49
49
|
"@vitest/coverage-v8": "3.2.4",
|
|
50
|
-
"astro": "5.
|
|
50
|
+
"astro": "5.15.1",
|
|
51
51
|
"globals": "16.4.0",
|
|
52
52
|
"husky": "9.1.7",
|
|
53
|
-
"lint-staged": "16.2.
|
|
54
|
-
"oxlint": "1.
|
|
53
|
+
"lint-staged": "16.2.6",
|
|
54
|
+
"oxlint": "1.24.0",
|
|
55
|
+
"oxlint-tsgolint": "^0.3.0",
|
|
55
56
|
"prettier": "3.6.2",
|
|
56
57
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
57
58
|
"prettier-plugin-packagejson": "2.5.19",
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { LoaderContext } from "astro/loaders";
|
|
2
|
+
import { z } from "astro/zod";
|
|
3
|
+
import {
|
|
4
|
+
pocketBaseErrorResponse,
|
|
5
|
+
pocketBaseListResponse
|
|
6
|
+
} from "../types/pocketbase-api-response.type";
|
|
7
|
+
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
8
|
+
import { pocketBaseBaseEntry } from "../types/pocketbase-entry.type";
|
|
2
9
|
import type { PocketBaseLoaderBaseOptions } from "../types/pocketbase-loader-options.type";
|
|
3
10
|
|
|
4
11
|
/**
|
|
@@ -67,10 +74,10 @@ export async function cleanupEntries(
|
|
|
67
74
|
);
|
|
68
75
|
}
|
|
69
76
|
} else {
|
|
70
|
-
const
|
|
71
|
-
.json()
|
|
72
|
-
|
|
73
|
-
const errorMessage = `Fetching ids failed with status code ${collectionRequest.status}.\nReason: ${
|
|
77
|
+
const errorResponse = pocketBaseErrorResponse.parse(
|
|
78
|
+
await collectionRequest.json()
|
|
79
|
+
);
|
|
80
|
+
const errorMessage = `Fetching ids failed with status code ${collectionRequest.status}.\nReason: ${errorResponse.message}`;
|
|
74
81
|
context.logger.error(errorMessage);
|
|
75
82
|
}
|
|
76
83
|
|
|
@@ -81,7 +88,9 @@ export async function cleanupEntries(
|
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
// Get the data from the response
|
|
84
|
-
const response =
|
|
91
|
+
const response = cleanUpEntriesResponse.parse(
|
|
92
|
+
await collectionRequest.json()
|
|
93
|
+
);
|
|
85
94
|
|
|
86
95
|
// Add the ids to the set
|
|
87
96
|
for (const item of response.items) {
|
|
@@ -97,7 +106,10 @@ export async function cleanupEntries(
|
|
|
97
106
|
|
|
98
107
|
// Create a mapping from PocketBase IDs to store keys for proper cleanup
|
|
99
108
|
const storedIds = new Map<string, string>(
|
|
100
|
-
context.store
|
|
109
|
+
context.store
|
|
110
|
+
.values()
|
|
111
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
112
|
+
.map((entry) => [(entry.data as PocketBaseEntry).id, entry.id])
|
|
101
113
|
);
|
|
102
114
|
|
|
103
115
|
// Check which PocketBase IDs are missing from the server response
|
|
@@ -114,3 +126,12 @@ export async function cleanupEntries(
|
|
|
114
126
|
context.logger.info(`Cleaned up ${cleanedUp} old entries.`);
|
|
115
127
|
}
|
|
116
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The response schema for cleaning up entries.
|
|
132
|
+
*/
|
|
133
|
+
const cleanUpEntriesResponse = pocketBaseListResponse
|
|
134
|
+
.omit({ items: true })
|
|
135
|
+
.extend({
|
|
136
|
+
items: z.array(pocketBaseBaseEntry.pick({ id: true }))
|
|
137
|
+
});
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pocketBaseErrorResponse,
|
|
3
|
+
pocketBaseListResponse
|
|
4
|
+
} from "../types/pocketbase-api-response.type";
|
|
1
5
|
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
2
6
|
import type { ExperimentalPocketBaseLiveLoaderCollectionFilter } from "../types/pocketbase-live-loader-filter.type";
|
|
3
7
|
import type { PocketBaseLoaderBaseOptions } from "../types/pocketbase-loader-options.type";
|
|
@@ -77,18 +81,21 @@ export async function fetchCollection<TEntry extends PocketBaseEntry>(
|
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
// Get the reason for the error
|
|
80
|
-
const
|
|
81
|
-
.json()
|
|
82
|
-
|
|
83
|
-
const errorMessage = `Fetching data failed with status code ${collectionRequest.status}.\nReason: ${
|
|
84
|
+
const errorResponse = pocketBaseErrorResponse.parse(
|
|
85
|
+
await collectionRequest.json()
|
|
86
|
+
);
|
|
87
|
+
const errorMessage = `Fetching data failed with status code ${collectionRequest.status}.\nReason: ${errorResponse.message}`;
|
|
84
88
|
throw new Error(errorMessage);
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
// Get the data from the response
|
|
88
|
-
const response =
|
|
92
|
+
const response = pocketBaseListResponse.parse(
|
|
93
|
+
await collectionRequest.json()
|
|
94
|
+
);
|
|
89
95
|
|
|
90
96
|
// Return current chunk
|
|
91
|
-
|
|
97
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
98
|
+
await chunkLoaded(response.items as Array<TEntry>);
|
|
92
99
|
|
|
93
100
|
// Update the page and total pages
|
|
94
101
|
page = response.page;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { pocketBaseErrorResponse } from "../types/pocketbase-api-response.type";
|
|
1
2
|
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
3
|
+
import { pocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
2
4
|
import type { ExperimentalPocketBaseLiveLoaderOptions } from "../types/pocketbase-loader-options.type";
|
|
3
5
|
import { combineFieldsForRequest } from "../utils/combine-fields-for-request";
|
|
4
6
|
import { formatFields } from "../utils/format-fields";
|
|
@@ -52,13 +54,15 @@ export async function fetchEntry<TEntry extends PocketBaseEntry>(
|
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
// Get the reason for the error
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
+
const errorResponse = pocketBaseErrorResponse.parse(
|
|
58
|
+
await entryRequest.json()
|
|
59
|
+
);
|
|
60
|
+
const errorMessage = `Fetching entry "${id}" from collection "${options.collectionName}" failed.\nReason: ${errorResponse.message}`;
|
|
57
61
|
throw new Error(errorMessage);
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
// Get the data from the response
|
|
61
|
-
const response = await entryRequest.json();
|
|
62
|
-
|
|
63
|
-
return response;
|
|
65
|
+
const response = pocketBaseEntry.parse(await entryRequest.json());
|
|
66
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
67
|
+
return response as TEntry;
|
|
64
68
|
}
|
|
@@ -20,7 +20,6 @@ export async function liveCollectionLoader<TEntry extends PocketBaseEntry>(
|
|
|
20
20
|
try {
|
|
21
21
|
await fetchCollection<TEntry>(
|
|
22
22
|
options,
|
|
23
|
-
// oxlint-disable-next-line require-await
|
|
24
23
|
async (chunk) => {
|
|
25
24
|
entries.push(...chunk.map((entry) => parseLiveEntry(entry, options)));
|
|
26
25
|
},
|
|
@@ -28,7 +27,7 @@ export async function liveCollectionLoader<TEntry extends PocketBaseEntry>(
|
|
|
28
27
|
collectionFilter
|
|
29
28
|
);
|
|
30
29
|
} catch (error) {
|
|
31
|
-
return { error: error
|
|
30
|
+
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
return {
|
|
@@ -16,6 +16,6 @@ export async function liveEntryLoader<TEntry extends PocketBaseEntry>(
|
|
|
16
16
|
const entry = await fetchEntry<TEntry>(id, options, token);
|
|
17
17
|
return parseLiveEntry(entry, options);
|
|
18
18
|
} catch (error) {
|
|
19
|
-
return { error: error
|
|
19
|
+
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
20
20
|
}
|
|
21
21
|
}
|
package/src/pocketbase-loader.ts
CHANGED
|
@@ -45,7 +45,7 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
|
|
|
45
45
|
const token = await tokenPromise;
|
|
46
46
|
|
|
47
47
|
// Generate the schema for the collection according to the API
|
|
48
|
-
return
|
|
48
|
+
return generateSchema(options, token);
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
}
|
|
@@ -78,7 +78,7 @@ export function experimentalPocketbaseLiveLoader<
|
|
|
78
78
|
const token = await tokenPromise;
|
|
79
79
|
|
|
80
80
|
// Load entries from the collection
|
|
81
|
-
return
|
|
81
|
+
return liveCollectionLoader<TEntry>(filter, options, token);
|
|
82
82
|
},
|
|
83
83
|
loadEntry: async ({
|
|
84
84
|
filter
|
|
@@ -86,7 +86,7 @@ export function experimentalPocketbaseLiveLoader<
|
|
|
86
86
|
const token = await tokenPromise;
|
|
87
87
|
|
|
88
88
|
// Load a single entry from the collection
|
|
89
|
-
return
|
|
89
|
+
return liveEntryLoader<TEntry>(filter.id, options, token);
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { pocketBaseErrorResponse } from "../types/pocketbase-api-response.type";
|
|
1
2
|
import type { PocketBaseLoaderOptions } from "../types/pocketbase-loader-options.type";
|
|
2
3
|
import type { PocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
4
|
+
import { pocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Fetches the schema for the specified collection from the PocketBase instance.
|
|
@@ -26,13 +28,16 @@ export async function getRemoteSchema(
|
|
|
26
28
|
|
|
27
29
|
// If the request was not successful, try another method
|
|
28
30
|
if (!schemaRequest.ok) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
+
const errorResponse = pocketBaseErrorResponse.parse(
|
|
32
|
+
await schemaRequest.json()
|
|
33
|
+
);
|
|
34
|
+
const errorMessage = `Fetching schema from ${options.collectionName} failed with status code ${schemaRequest.status}.\nReason: ${errorResponse.message}`;
|
|
31
35
|
console.error(errorMessage);
|
|
32
36
|
|
|
33
37
|
return undefined;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
// Get the schema from the response
|
|
37
|
-
|
|
41
|
+
const response = pocketBaseCollection.parse(await schemaRequest.json());
|
|
42
|
+
return response;
|
|
38
43
|
}
|
|
@@ -46,6 +46,7 @@ export function parseSchema(
|
|
|
46
46
|
let fieldType;
|
|
47
47
|
|
|
48
48
|
// Determine the field type and create the corresponding Zod type
|
|
49
|
+
// oxlint-disable-next-line switch-exhaustiveness-check
|
|
49
50
|
switch (field.type) {
|
|
50
51
|
case "number":
|
|
51
52
|
fieldType = z.number();
|
|
@@ -77,6 +78,7 @@ export function parseSchema(
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
// Create an enum for the select values
|
|
81
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
80
82
|
const values = z.enum(field.values as [string, ...Array<string>]);
|
|
81
83
|
|
|
82
84
|
// Parse the field type based on the number of values it can have
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import type { PocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
4
|
+
import { pocketBaseDatabase } from "../types/pocketbase-schema.type";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Reads the local PocketBase schema file and returns the schema for the specified collection.
|
|
@@ -17,15 +18,15 @@ export async function readLocalSchema(
|
|
|
17
18
|
try {
|
|
18
19
|
// Read the schema file
|
|
19
20
|
const schemaFile = await fs.readFile(realPath, "utf-8");
|
|
20
|
-
const
|
|
21
|
+
const fileContent = pocketBaseDatabase.safeParse(JSON.parse(schemaFile));
|
|
21
22
|
|
|
22
23
|
// Check if the database file is valid
|
|
23
|
-
if (!
|
|
24
|
+
if (!fileContent.success) {
|
|
24
25
|
throw new Error("Invalid schema file");
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
// Find and return the schema for the collection
|
|
28
|
-
const schema =
|
|
29
|
+
const schema = fileContent.data.find(
|
|
29
30
|
(collection) => collection.name === collectionName
|
|
30
31
|
);
|
|
31
32
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "astro/zod";
|
|
1
2
|
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
2
3
|
import type { PocketBaseSchemaEntry } from "../types/pocketbase-schema.type";
|
|
3
4
|
|
|
@@ -18,7 +19,7 @@ export function transformFiles(
|
|
|
18
19
|
const fieldName = field.name;
|
|
19
20
|
|
|
20
21
|
if (field.maxSelect === 1) {
|
|
21
|
-
const fileName = entry[fieldName]
|
|
22
|
+
const fileName = z.optional(z.string()).parse(entry[fieldName]);
|
|
22
23
|
// Check if a file name is present
|
|
23
24
|
if (!fileName) {
|
|
24
25
|
continue;
|
|
@@ -32,7 +33,7 @@ export function transformFiles(
|
|
|
32
33
|
fileName
|
|
33
34
|
);
|
|
34
35
|
} else {
|
|
35
|
-
const fileNames = entry[fieldName]
|
|
36
|
+
const fileNames = z.optional(z.array(z.string())).parse(entry[fieldName]);
|
|
36
37
|
// Check if file names are present
|
|
37
38
|
if (!fileNames) {
|
|
38
39
|
continue;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "astro/zod";
|
|
2
|
+
import { pocketBaseEntry } from "./pocketbase-entry.type";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The schema for a PocketBase error response.
|
|
6
|
+
*/
|
|
7
|
+
export const pocketBaseErrorResponse = z.object({
|
|
8
|
+
/**
|
|
9
|
+
* The error message returned by PocketBase.
|
|
10
|
+
*/
|
|
11
|
+
message: z.string()
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The schema for a PocketBase list response.
|
|
16
|
+
*/
|
|
17
|
+
export const pocketBaseListResponse = z.object({
|
|
18
|
+
/**
|
|
19
|
+
* Current page number.
|
|
20
|
+
*/
|
|
21
|
+
page: z.number(),
|
|
22
|
+
/**
|
|
23
|
+
* Total number of pages available.
|
|
24
|
+
*/
|
|
25
|
+
totalPages: z.number(),
|
|
26
|
+
/**
|
|
27
|
+
* Array of items in the current page.
|
|
28
|
+
*/
|
|
29
|
+
items: z.array(pocketBaseEntry)
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The schema for a PocketBase login response.
|
|
34
|
+
*/
|
|
35
|
+
export const pocketBaseLoginResponse = z.object({
|
|
36
|
+
/**
|
|
37
|
+
* The authentication token returned by PocketBase.
|
|
38
|
+
*/
|
|
39
|
+
token: z.string()
|
|
40
|
+
});
|
|
@@ -1,22 +1,34 @@
|
|
|
1
|
+
import { z } from "astro/zod";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* Base
|
|
4
|
+
* Base schema for all PocketBase entries.
|
|
3
5
|
*/
|
|
4
|
-
|
|
6
|
+
export const pocketBaseBaseEntry = z.object({
|
|
5
7
|
/**
|
|
6
8
|
* ID of the entry.
|
|
7
9
|
*/
|
|
8
|
-
id: string
|
|
10
|
+
id: z.string(),
|
|
9
11
|
/**
|
|
10
12
|
* ID of the collection the entry belongs to.
|
|
11
13
|
*/
|
|
12
|
-
collectionId: string
|
|
14
|
+
collectionId: z.string(),
|
|
13
15
|
/**
|
|
14
16
|
* Name of the collection the entry belongs to.
|
|
15
17
|
*/
|
|
16
|
-
collectionName: string
|
|
17
|
-
}
|
|
18
|
+
collectionName: z.string()
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base interface for all PocketBase entries.
|
|
23
|
+
*/
|
|
24
|
+
export type PocketBaseBaseEntry = z.infer<typeof pocketBaseBaseEntry>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schema for a PocketBase entry.
|
|
28
|
+
*/
|
|
29
|
+
export const pocketBaseEntry = pocketBaseBaseEntry.passthrough();
|
|
18
30
|
|
|
19
31
|
/**
|
|
20
32
|
* Type for a PocketBase entry.
|
|
21
33
|
*/
|
|
22
|
-
export type PocketBaseEntry =
|
|
34
|
+
export type PocketBaseEntry = z.infer<typeof pocketBaseEntry>;
|
|
@@ -1,60 +1,92 @@
|
|
|
1
|
+
import { z } from "astro/zod";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Entry for a collections schema in PocketBase.
|
|
3
5
|
*/
|
|
4
|
-
export
|
|
6
|
+
export const pocketBaseSchemaEntry = z.object({
|
|
5
7
|
/**
|
|
6
8
|
* Flag to indicate if the field is hidden.
|
|
7
9
|
* Hidden fields are not returned in the API response.
|
|
8
10
|
*/
|
|
9
|
-
hidden: boolean
|
|
11
|
+
hidden: z.optional(z.boolean()),
|
|
10
12
|
/**
|
|
11
13
|
* Name of the field.
|
|
12
14
|
*/
|
|
13
|
-
name: string
|
|
15
|
+
name: z.string(),
|
|
14
16
|
/**
|
|
15
17
|
* Type of the field.
|
|
16
18
|
*/
|
|
17
|
-
type:
|
|
19
|
+
type: z.enum([
|
|
20
|
+
"text",
|
|
21
|
+
"editor",
|
|
22
|
+
"number",
|
|
23
|
+
"bool",
|
|
24
|
+
"email",
|
|
25
|
+
"url",
|
|
26
|
+
"date",
|
|
27
|
+
"autodate",
|
|
28
|
+
"select",
|
|
29
|
+
"file",
|
|
30
|
+
"relation",
|
|
31
|
+
"json",
|
|
32
|
+
"geoPoint",
|
|
33
|
+
"password"
|
|
34
|
+
]),
|
|
18
35
|
/**
|
|
19
36
|
* Whether the field is required.
|
|
20
37
|
*/
|
|
21
|
-
required: boolean
|
|
38
|
+
required: z.optional(z.boolean()),
|
|
22
39
|
/**
|
|
23
40
|
* Values for a select field.
|
|
24
41
|
* This is only present if the field type is "select".
|
|
25
42
|
*/
|
|
26
|
-
values
|
|
43
|
+
values: z.optional(z.array(z.string())),
|
|
27
44
|
/**
|
|
28
45
|
* Maximum number of values for a select field.
|
|
29
46
|
* This is only present on "select", "relation", and "file" fields.
|
|
30
47
|
*/
|
|
31
|
-
maxSelect
|
|
48
|
+
maxSelect: z.optional(z.number()),
|
|
32
49
|
/**
|
|
33
50
|
* Whether the field is filled when the entry is created.
|
|
34
51
|
* This is only present on "autodate" fields.
|
|
35
52
|
*/
|
|
36
|
-
onCreate
|
|
53
|
+
onCreate: z.optional(z.boolean()),
|
|
37
54
|
/**
|
|
38
55
|
* Whether the field is updated when the entry is updated.
|
|
39
56
|
* This is only present on "autodate" fields.
|
|
40
57
|
*/
|
|
41
|
-
onUpdate
|
|
42
|
-
}
|
|
58
|
+
onUpdate: z.optional(z.boolean())
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Entry for a collections schema in PocketBase.
|
|
63
|
+
*/
|
|
64
|
+
export type PocketBaseSchemaEntry = z.infer<typeof pocketBaseSchemaEntry>;
|
|
43
65
|
|
|
44
66
|
/**
|
|
45
67
|
* Schema for a PocketBase collection.
|
|
46
68
|
*/
|
|
47
|
-
export
|
|
69
|
+
export const pocketBaseCollection = z.object({
|
|
48
70
|
/**
|
|
49
71
|
* Name of the collection.
|
|
50
72
|
*/
|
|
51
|
-
name: string
|
|
73
|
+
name: z.string(),
|
|
52
74
|
/**
|
|
53
75
|
* Type of the collection.
|
|
54
76
|
*/
|
|
55
|
-
type: "base"
|
|
77
|
+
type: z.enum(["base", "view", "auth"]),
|
|
56
78
|
/**
|
|
57
79
|
* Schema of the collection.
|
|
58
80
|
*/
|
|
59
|
-
fields:
|
|
60
|
-
}
|
|
81
|
+
fields: z.array(pocketBaseSchemaEntry)
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Type for a PocketBase collection.
|
|
86
|
+
*/
|
|
87
|
+
export type PocketBaseCollection = z.infer<typeof pocketBaseCollection>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Schema for an entire PocketBase database.
|
|
91
|
+
*/
|
|
92
|
+
export const pocketBaseDatabase = z.array(pocketBaseCollection);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { AstroIntegrationLogger } from "astro";
|
|
2
|
+
import {
|
|
3
|
+
pocketBaseErrorResponse,
|
|
4
|
+
pocketBaseLoginResponse
|
|
5
|
+
} from "../types/pocketbase-api-response.type";
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* This function will get a superuser token from the given PocketBase instance.
|
|
@@ -35,8 +39,27 @@ export async function getSuperuserToken(
|
|
|
35
39
|
|
|
36
40
|
// If the login request was not successful, print the error message and return undefined
|
|
37
41
|
if (!loginRequest.ok) {
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
if (loginRequest.status === 429) {
|
|
43
|
+
const info =
|
|
44
|
+
"A rate limit was hit while trying to authenticate with PocketBase. Consider using an `impersonateToken` as credentials to avoid this issue.";
|
|
45
|
+
if (logger) {
|
|
46
|
+
logger.info(info);
|
|
47
|
+
} else {
|
|
48
|
+
console.info(info);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Random wait between 3 (default rate limit interval) and 8 seconds
|
|
52
|
+
const retryAfter = Math.random() * 5 + 3;
|
|
53
|
+
// oxlint-disable-next-line promise/avoid-new
|
|
54
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
|
|
55
|
+
|
|
56
|
+
return getSuperuserToken(url, superuserCredentials, logger);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const errorResponse = pocketBaseErrorResponse.parse(
|
|
60
|
+
await loginRequest.json()
|
|
61
|
+
);
|
|
62
|
+
const errorMessage = `The given email / password for ${url} was not correct. Astro can't generate type definitions automatically and may not have access to all resources.\nReason: ${errorResponse.message}`;
|
|
40
63
|
if (logger) {
|
|
41
64
|
logger.error(errorMessage);
|
|
42
65
|
} else {
|
|
@@ -46,5 +69,6 @@ export async function getSuperuserToken(
|
|
|
46
69
|
}
|
|
47
70
|
|
|
48
71
|
// Return the token
|
|
49
|
-
|
|
72
|
+
const response = pocketBaseLoginResponse.parse(await loginRequest.json());
|
|
73
|
+
return response.token;
|
|
50
74
|
}
|