astro-integration-pocketbase 1.1.0 → 1.2.0-next.2
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
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
This package provides an Astro toolbar for users of [astro-loader-pocketbase](https://github.com/pawcoding/astro-loader-pocketbase) to view PocketBase data directly in the Astro dev server.
|
|
10
10
|
|
|
11
|
-

|
|
11
|
+

|
|
12
12
|
|
|
13
13
|
## Compatibility
|
|
14
14
|
|
|
@@ -43,6 +43,8 @@ If a loader is found, the viewer will show a refresh button to reload all entrie
|
|
|
43
43
|
|
|
44
44
|
## Realtime updates
|
|
45
45
|
|
|
46
|
+
### Basic setup
|
|
47
|
+
|
|
46
48
|
PocketBase allows you to subscribe to collection changes via its [Realtime API](https://pocketbase.io/docs/api-realtime/).
|
|
47
49
|
This integration allows you to subscribe to these changes and reload the entries / collections.
|
|
48
50
|
Note that Node.js currently does not support the [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) API, so the integration uses the [eventsource](https://www.npmjs.com/package/eventsource) package to provide the same functionality.
|
|
@@ -64,6 +66,28 @@ pocketbaseIntegration({
|
|
|
64
66
|
|
|
65
67
|
**Tip:** You can disable the realtime updates temporarily via the toolbar.
|
|
66
68
|
|
|
69
|
+
### Advanced setup
|
|
70
|
+
|
|
71
|
+
If you work with view collections, you need some more advanced options to get the realtime updates working as expected.
|
|
72
|
+
Since [view collections don't receive realtime events](https://pocketbase.io/docs/collections/#view-collection), you need to watch the source base collection instead, by providing one or more collections to watch.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
pocketbaseIntegration({
|
|
76
|
+
...options,
|
|
77
|
+
collectionsToWatch: {
|
|
78
|
+
// Same effect as basic setup watching the same collection
|
|
79
|
+
// Recommended for basic / auth collections
|
|
80
|
+
users: true,
|
|
81
|
+
// Watch the source collection(s) of a view collection
|
|
82
|
+
// Recommended for view collections
|
|
83
|
+
postings: ["posts", "comments"]
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
When using `true`, the integration will subscribe and reload the entries of the same collection mentioned in the key.
|
|
89
|
+
When using an array of other collections, the integration will subscribe to changes of collections given in the array and reload the entries of the collection mentioned in the key.
|
|
90
|
+
|
|
67
91
|
## Entity viewer
|
|
68
92
|
|
|
69
93
|
To view the PocketBase entries inside the entity viewer, you need to use `Astro.props` to pass the entries to your page (and thus to the toolbar).
|
|
@@ -106,8 +130,8 @@ The integration will automatically detect PocketBase entries in the props and di
|
|
|
106
130
|
|
|
107
131
|
## All options
|
|
108
132
|
|
|
109
|
-
| Option | Type
|
|
110
|
-
| ---------------------- |
|
|
111
|
-
| `url` | `string`
|
|
112
|
-
| `collectionsToWatch` | `Array<string
|
|
113
|
-
| `superuserCredentials` | `{ email: string, password: string }`
|
|
133
|
+
| Option | Type | Required | Description |
|
|
134
|
+
| ---------------------- | -------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
135
|
+
| `url` | `string` | x | The URL of your PocketBase instance. |
|
|
136
|
+
| `collectionsToWatch` | `Array<string> \| Record<string, true \| Array<string>>` | | Collections to watch for changes. |
|
|
137
|
+
| `superuserCredentials` | `{ email: string, password: string }` | | The email and password of a superuser of the PocketBase instance. This is used for realtime updates of restricted collection. |
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import type { BaseIntegrationHooks } from "astro";
|
|
|
2
2
|
import { EventSource } from "eventsource";
|
|
3
3
|
import type { PocketBaseIntegrationOptions } from "../types/pocketbase-integration-options.type";
|
|
4
4
|
import { getSuperuserToken } from "../utils/get-superuser-token";
|
|
5
|
+
import { mapCollectionsToWatch } from "../utils/map-collections-to-watch";
|
|
5
6
|
|
|
6
7
|
export function refreshCollectionsRealtime(
|
|
7
8
|
{
|
|
@@ -16,9 +17,11 @@ export function refreshCollectionsRealtime(
|
|
|
16
17
|
}: Parameters<BaseIntegrationHooks["astro:server:setup"]>[0]
|
|
17
18
|
): EventSource | undefined {
|
|
18
19
|
// Check if collections should be watched
|
|
19
|
-
|
|
20
|
+
const collectionsMap = mapCollectionsToWatch(collectionsToWatch);
|
|
21
|
+
if (!collectionsMap) {
|
|
20
22
|
return undefined;
|
|
21
23
|
}
|
|
24
|
+
const remoteCollections = [...collectionsMap.keys()];
|
|
22
25
|
|
|
23
26
|
// Check if content loader is used
|
|
24
27
|
if (!refreshContent) {
|
|
@@ -65,7 +68,7 @@ export function refreshCollectionsRealtime(
|
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
// Add event listeners for all collections
|
|
68
|
-
for (const collection of
|
|
71
|
+
for (const collection of remoteCollections) {
|
|
69
72
|
eventSource.addEventListener(`${collection}/*`, async () => {
|
|
70
73
|
// Do not refresh if the refresh is disabled
|
|
71
74
|
if (!refreshEnabled) {
|
|
@@ -78,7 +81,7 @@ export function refreshCollectionsRealtime(
|
|
|
78
81
|
loaders: ["pocketbase-loader"],
|
|
79
82
|
context: {
|
|
80
83
|
source: "astro-integration-pocketbase",
|
|
81
|
-
collection
|
|
84
|
+
collection: collectionsMap.get(collection)
|
|
82
85
|
}
|
|
83
86
|
});
|
|
84
87
|
});
|
|
@@ -108,7 +111,7 @@ export function refreshCollectionsRealtime(
|
|
|
108
111
|
},
|
|
109
112
|
body: JSON.stringify({
|
|
110
113
|
clientId: clientId,
|
|
111
|
-
subscriptions:
|
|
114
|
+
subscriptions: remoteCollections.map((collection) => `${collection}/*`)
|
|
112
115
|
})
|
|
113
116
|
});
|
|
114
117
|
|
|
@@ -123,7 +126,7 @@ export function refreshCollectionsRealtime(
|
|
|
123
126
|
if (!wasConnectedOnce) {
|
|
124
127
|
wasConnectedOnce = true;
|
|
125
128
|
logger.info(
|
|
126
|
-
`Subscribed to PocketBase realtime API. Waiting for updates on ${
|
|
129
|
+
`Subscribed to PocketBase realtime API. Waiting for updates on ${remoteCollections.join(
|
|
127
130
|
", "
|
|
128
131
|
)}.`
|
|
129
132
|
);
|
|
@@ -23,6 +23,28 @@ export interface PocketBaseIntegrationOptions {
|
|
|
23
23
|
/**
|
|
24
24
|
* List of PocketBase collections to watch for changes.
|
|
25
25
|
* When an entry in one of these collections changes, the content will be reloaded.
|
|
26
|
+
*
|
|
27
|
+
* Example:
|
|
28
|
+
* ```ts
|
|
29
|
+
* collectionsToWatch: ["users", "posts"]
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* For advanced usage, you can specify a map where the key is the collection that is used to load the content.
|
|
33
|
+
* The value can be:
|
|
34
|
+
* - `true` to watch the collection itself for changes (base collection)
|
|
35
|
+
* - an array of strings to watch multiple collections for changes (view collection)
|
|
36
|
+
*
|
|
37
|
+
* These advanced options are especially useful when you're working with view collections.
|
|
38
|
+
* [View collections](https://pocketbase.io/docs/collections/#view-collection) don't emit events when their
|
|
39
|
+
* entries change, since they are based on other collections.
|
|
40
|
+
*
|
|
41
|
+
* Example:
|
|
42
|
+
* ```ts
|
|
43
|
+
* collectionsToWatch: {
|
|
44
|
+
* "users": true,
|
|
45
|
+
* "postings": ["posts", "comments"]
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
26
48
|
*/
|
|
27
|
-
collectionsToWatch?: Array<string
|
|
49
|
+
collectionsToWatch?: Array<string> | Record<string, true | Array<string>>;
|
|
28
50
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { PocketBaseIntegrationOptions } from "../types/pocketbase-integration-options.type";
|
|
2
|
+
import { pushToMapArray } from "./push-to-map-array";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create a map of remote collections to watch.
|
|
6
|
+
* Each key in the map represents a remote collection to subscribe to.
|
|
7
|
+
* The value is an array of local collections that should be refreshed when an entry in the remote collection changes.
|
|
8
|
+
*/
|
|
9
|
+
export function mapCollectionsToWatch(
|
|
10
|
+
collectionsToWatch: PocketBaseIntegrationOptions["collectionsToWatch"]
|
|
11
|
+
): Map<string, Array<string>> | undefined {
|
|
12
|
+
// Check if collections should be watched
|
|
13
|
+
if (!collectionsToWatch) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Check if collectionsToWatch is an array
|
|
18
|
+
if (Array.isArray(collectionsToWatch)) {
|
|
19
|
+
// Check if the array is empty
|
|
20
|
+
if (collectionsToWatch.length === 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Create a map where each collection is watched by itself
|
|
25
|
+
return new Map(
|
|
26
|
+
collectionsToWatch.map((collection) => [collection, [collection]])
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check if collectionsToWatch is an empty object
|
|
31
|
+
if (Object.keys(collectionsToWatch).length === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Map collections to watch
|
|
36
|
+
const collectionsMap = new Map<string, Array<string>>();
|
|
37
|
+
for (const localCollection in collectionsToWatch) {
|
|
38
|
+
const watch = collectionsToWatch[localCollection];
|
|
39
|
+
|
|
40
|
+
// Check if collection should be watched by itself
|
|
41
|
+
if (watch === true) {
|
|
42
|
+
pushToMapArray(collectionsMap, localCollection, localCollection);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Collection should be watched by multiple collections
|
|
47
|
+
for (const remoteCollection of watch) {
|
|
48
|
+
pushToMapArray(collectionsMap, remoteCollection, localCollection);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return collectionsMap;
|
|
53
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds a value to an array in a map. If the key does not exist, it will be created.
|
|
3
|
+
*/
|
|
4
|
+
export function pushToMapArray<TKey, TArray>(
|
|
5
|
+
map: Map<TKey, Array<TArray>>,
|
|
6
|
+
key: TKey,
|
|
7
|
+
value: TArray
|
|
8
|
+
): void {
|
|
9
|
+
if (map.has(key)) {
|
|
10
|
+
map.get(key)!.push(value);
|
|
11
|
+
} else {
|
|
12
|
+
map.set(key, [value]);
|
|
13
|
+
}
|
|
14
|
+
}
|