astro-loader-pocketbase 2.2.1 → 2.3.0
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-loader-pocketbase",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "A content loader for Astro that uses the PocketBase API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Luis Wolf <development@pawcode.de> (https://pawcode.de)",
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"astro": "^5.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@eslint/js": "^9.
|
|
25
|
-
"@stylistic/eslint-plugin": "^
|
|
26
|
-
"@types/node": "^22.
|
|
27
|
-
"astro": "^5.
|
|
28
|
-
"eslint": "^9.
|
|
24
|
+
"@eslint/js": "^9.19.0",
|
|
25
|
+
"@stylistic/eslint-plugin": "^3.0.1",
|
|
26
|
+
"@types/node": "^22.13.0",
|
|
27
|
+
"astro": "^5.2.3",
|
|
28
|
+
"eslint": "^9.19.0",
|
|
29
29
|
"globals": "^15.14.0",
|
|
30
30
|
"husky": "^9.1.7",
|
|
31
31
|
"typescript": "^5.7.3",
|
|
32
|
-
"typescript-eslint": "^8.
|
|
32
|
+
"typescript-eslint": "^8.22.0"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"astro",
|
package/src/cleanup-entries.ts
CHANGED
|
@@ -45,7 +45,7 @@ export async function cleanupEntries(
|
|
|
45
45
|
// If the collection is locked, an superuser token is required
|
|
46
46
|
if (collectionRequest.status === 403) {
|
|
47
47
|
context.logger.error(
|
|
48
|
-
`
|
|
48
|
+
`The collection is not accessible without superuser rights. Please provide superuser credentials in the config.`
|
|
49
49
|
);
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
@@ -53,7 +53,7 @@ export async function cleanupEntries(
|
|
|
53
53
|
const reason = await collectionRequest
|
|
54
54
|
.json()
|
|
55
55
|
.then((data) => data.message);
|
|
56
|
-
const errorMessage = `
|
|
56
|
+
const errorMessage = `Fetching ids failed with status code ${collectionRequest.status}.\nReason: ${reason}`;
|
|
57
57
|
context.logger.error(errorMessage);
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
@@ -74,7 +74,9 @@ export async function cleanupEntries(
|
|
|
74
74
|
let cleanedUp = 0;
|
|
75
75
|
|
|
76
76
|
// Get all ids of the entries in the store
|
|
77
|
-
const storedIds = context.store
|
|
77
|
+
const storedIds = context.store
|
|
78
|
+
.values()
|
|
79
|
+
.map((entry) => entry.data.id) as Array<string>;
|
|
78
80
|
for (const id of storedIds) {
|
|
79
81
|
// If the id is not in the entries set, remove the entry from the store
|
|
80
82
|
if (!entries.has(id)) {
|
|
@@ -85,8 +87,6 @@ export async function cleanupEntries(
|
|
|
85
87
|
|
|
86
88
|
if (cleanedUp > 0) {
|
|
87
89
|
// Log the number of cleaned up entries
|
|
88
|
-
context.logger.info(
|
|
89
|
-
`(${options.collectionName}) Cleaned up ${cleanedUp} old entries.`
|
|
90
|
-
);
|
|
90
|
+
context.logger.info(`Cleaned up ${cleanedUp} old entries.`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { LoaderContext } from "astro/loaders";
|
|
2
|
+
import type { PocketBaseLoaderOptions } from "./types/pocketbase-loader-options.type";
|
|
3
|
+
import { isRealtimeData } from "./utils/is-realtime-data";
|
|
4
|
+
import { parseEntry } from "./utils/parse-entry";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Handles realtime updates for the loader without making any new network requests.
|
|
8
|
+
*
|
|
9
|
+
* Returns `true` if the data was handled and no further action is needed.
|
|
10
|
+
*/
|
|
11
|
+
export async function handleRealtimeUpdates(
|
|
12
|
+
context: LoaderContext,
|
|
13
|
+
options: PocketBaseLoaderOptions
|
|
14
|
+
): Promise<boolean> {
|
|
15
|
+
// Check if data was provided via the refresh context
|
|
16
|
+
if (!context.refreshContextData?.data) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Check if the data is PocketBase realtime data
|
|
21
|
+
const data = context.refreshContextData.data;
|
|
22
|
+
if (!isRealtimeData(data)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if the collection name matches the current collection
|
|
27
|
+
if (data.record.collectionName !== options.collectionName) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle deleted entry
|
|
32
|
+
if (data.action === "delete") {
|
|
33
|
+
context.logger.info("Removing deleted entry");
|
|
34
|
+
context.store.delete(data.record.id);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Handle updated or new entry
|
|
39
|
+
if (data.action === "update") {
|
|
40
|
+
context.logger.info("Updating outdated entry");
|
|
41
|
+
} else {
|
|
42
|
+
context.logger.info("Creating new entry");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Parse the entry and store
|
|
46
|
+
await parseEntry(data.record, context, options);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
package/src/load-entries.ts
CHANGED
|
@@ -32,11 +32,9 @@ export async function loadEntries(
|
|
|
32
32
|
|
|
33
33
|
// Log the fetching of the entries
|
|
34
34
|
context.logger.info(
|
|
35
|
-
`
|
|
36
|
-
lastModified ?
|
|
37
|
-
}
|
|
38
|
-
superuserToken ? " as superuser" : ""
|
|
39
|
-
}`
|
|
35
|
+
`Fetching${lastModified ? " modified" : ""} data${
|
|
36
|
+
lastModified ? ` starting at ${lastModified}` : ""
|
|
37
|
+
}${superuserToken ? " as superuser" : ""}`
|
|
40
38
|
);
|
|
41
39
|
|
|
42
40
|
// Prepare pagination variables
|
|
@@ -64,7 +62,7 @@ export async function loadEntries(
|
|
|
64
62
|
// If the collection is locked, an superuser token is required
|
|
65
63
|
if (collectionRequest.status === 403) {
|
|
66
64
|
throw new Error(
|
|
67
|
-
`
|
|
65
|
+
`The collection is not accessible without superuser rights. Please provide superuser credentials in the config.`
|
|
68
66
|
);
|
|
69
67
|
}
|
|
70
68
|
|
|
@@ -72,7 +70,7 @@ export async function loadEntries(
|
|
|
72
70
|
const reason = await collectionRequest
|
|
73
71
|
.json()
|
|
74
72
|
.then((data) => data.message);
|
|
75
|
-
const errorMessage = `
|
|
73
|
+
const errorMessage = `Fetching data failed with status code ${collectionRequest.status}.\nReason: ${reason}`;
|
|
76
74
|
throw new Error(errorMessage);
|
|
77
75
|
}
|
|
78
76
|
|
|
@@ -92,8 +90,6 @@ export async function loadEntries(
|
|
|
92
90
|
|
|
93
91
|
// Log the number of fetched entries
|
|
94
92
|
context.logger.info(
|
|
95
|
-
`
|
|
96
|
-
lastModified ? " changed" : ""
|
|
97
|
-
} entries.`
|
|
93
|
+
`Fetched ${entries}${lastModified ? " changed" : ""} entries.`
|
|
98
94
|
);
|
|
99
95
|
}
|
package/src/pocketbase-loader.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Loader, LoaderContext } from "astro/loaders";
|
|
2
|
+
import type { ZodSchema } from "astro/zod";
|
|
2
3
|
import packageJson from "./../package.json";
|
|
3
4
|
import { cleanupEntries } from "./cleanup-entries";
|
|
4
5
|
import { generateSchema } from "./generate-schema";
|
|
6
|
+
import { handleRealtimeUpdates } from "./handle-realtime-updates";
|
|
5
7
|
import { loadEntries } from "./load-entries";
|
|
6
8
|
import type { PocketBaseLoaderOptions } from "./types/pocketbase-loader-options.type";
|
|
7
9
|
import { getSuperuserToken } from "./utils/get-superuser-token";
|
|
@@ -16,6 +18,8 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
|
|
|
16
18
|
return {
|
|
17
19
|
name: "pocketbase-loader",
|
|
18
20
|
load: async (context: LoaderContext): Promise<void> => {
|
|
21
|
+
context.logger.label = `pocketbase-loader:${options.collectionName}`;
|
|
22
|
+
|
|
19
23
|
// Check if the collection should be refreshed.
|
|
20
24
|
const refresh = shouldRefresh(
|
|
21
25
|
context.refreshContextData,
|
|
@@ -25,6 +29,12 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
|
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
// Handle realtime updates
|
|
33
|
+
const handled = await handleRealtimeUpdates(context, options);
|
|
34
|
+
if (handled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
// Get the date of the last fetch to only update changed entries.
|
|
29
39
|
let lastModified = context.meta.get("last-modified");
|
|
30
40
|
|
|
@@ -45,7 +55,7 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
|
|
|
45
55
|
// Disable incremental builds if no updated field is provided
|
|
46
56
|
if (!options.updatedField) {
|
|
47
57
|
context.logger.info(
|
|
48
|
-
`
|
|
58
|
+
`No "updatedField" was provided. Incremental builds are disabled.`
|
|
49
59
|
);
|
|
50
60
|
lastModified = undefined;
|
|
51
61
|
}
|
|
@@ -76,7 +86,7 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
|
|
|
76
86
|
|
|
77
87
|
context.meta.set("version", packageJson.version);
|
|
78
88
|
},
|
|
79
|
-
schema: async () => {
|
|
89
|
+
schema: async (): Promise<ZodSchema> => {
|
|
80
90
|
// Generate the schema for the collection according to the API
|
|
81
91
|
return await generateSchema(options);
|
|
82
92
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "astro/zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Schema for realtime data received from PocketBase.
|
|
5
|
+
*/
|
|
6
|
+
const realtimeDataSchema = z.object({
|
|
7
|
+
action: z.union([
|
|
8
|
+
z.literal("create"),
|
|
9
|
+
z.literal("update"),
|
|
10
|
+
z.literal("delete")
|
|
11
|
+
]),
|
|
12
|
+
record: z.object({
|
|
13
|
+
id: z.string(),
|
|
14
|
+
collectionName: z.string(),
|
|
15
|
+
collectionId: z.string()
|
|
16
|
+
})
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Type for realtime data received from PocketBase.
|
|
21
|
+
*/
|
|
22
|
+
export type RealtimeData = z.infer<typeof realtimeDataSchema>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Checks if the given data is realtime data received from PocketBase.
|
|
26
|
+
*/
|
|
27
|
+
export function isRealtimeData(data: unknown): data is RealtimeData {
|
|
28
|
+
try {
|
|
29
|
+
realtimeDataSchema.parse(data);
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -106,7 +106,7 @@ export function parseSchema(
|
|
|
106
106
|
function parseSingleOrMultipleValues(
|
|
107
107
|
field: PocketBaseSchemaEntry,
|
|
108
108
|
type: z.ZodType
|
|
109
|
-
) {
|
|
109
|
+
): z.ZodType {
|
|
110
110
|
// If the select allows multiple values, create an array of the enum
|
|
111
111
|
if (field.maxSelect === undefined || field.maxSelect === 1) {
|
|
112
112
|
return type;
|