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
- ![PocketBase Toolbar](/assets/toolbar.png)
11
+ ![PocketBase Toolbar](https://github.com/pawcoding/astro-integration-pocketbase/blob/master/assets/toolbar.png?raw=true)
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 | Required | Description |
110
- | ---------------------- | ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
111
- | `url` | `string` | x | The URL of your PocketBase instance. |
112
- | `collectionsToWatch` | `Array<string>` | | Collections to watch for changes. |
113
- | `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. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-integration-pocketbase",
3
- "version": "1.1.0",
3
+ "version": "1.2.0-next.2",
4
4
  "license": "MIT",
5
5
  "author": "Luis Wolf <development@pawcode.de> (https://pawcode.de)",
6
6
  "homepage": "https://github.com/pawcoding/astro-integration-pocketbase",
@@ -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
- if (!collectionsToWatch || collectionsToWatch.length === 0) {
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 collectionsToWatch) {
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: collectionsToWatch.map((collection) => `${collection}/*`)
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 ${collectionsToWatch.join(
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
+ }