@backstage/plugin-user-settings 0.8.28 → 0.8.29

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/CHANGELOG.md CHANGED
@@ -1,10 +1,40 @@
1
1
  # @backstage/plugin-user-settings
2
2
 
3
- ## 0.8.28
3
+ ## 0.8.29
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - e38a874: Revert `storageApiRef` implementation
7
+ - 2b6fda3: Revert `storageApiRef` implementation
8
+ - 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
9
+ - Updated dependencies
10
+ - @backstage/frontend-plugin-api@0.13.0
11
+ - @backstage/core-compat-api@0.5.4
12
+ - @backstage/plugin-catalog-react@1.21.3
13
+ - @backstage/core-components@0.18.3
14
+ - @backstage/core-plugin-api@1.12.0
15
+ - @backstage/core-app-api@1.19.2
16
+ - @backstage/catalog-model@1.7.6
17
+ - @backstage/plugin-signals-react@0.0.17
18
+
19
+ ## 0.8.29-next.0
20
+
21
+ ### Patch Changes
22
+
23
+ - 2b6fda3: Revert `storageApiRef` implementation
24
+ - 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
25
+ - Updated dependencies
26
+ - @backstage/plugin-catalog-react@1.21.3-next.0
27
+ - @backstage/core-app-api@1.19.2-next.0
28
+ - @backstage/core-plugin-api@1.11.2-next.0
29
+ - @backstage/core-components@0.18.3-next.0
30
+ - @backstage/catalog-model@1.7.6-next.0
31
+ - @backstage/frontend-plugin-api@0.12.2-next.0
32
+ - @backstage/core-compat-api@0.5.4-next.0
33
+ - @backstage/errors@1.2.7
34
+ - @backstage/theme@0.7.0
35
+ - @backstage/types@1.2.2
36
+ - @backstage/plugin-signals-react@0.0.17-next.0
37
+ - @backstage/plugin-user-settings-common@0.0.1
8
38
 
9
39
  ## 0.8.27
10
40
 
package/dist/alpha.d.ts CHANGED
@@ -58,7 +58,7 @@ declare const userSettingsTranslationRef: _backstage_core_plugin_api_alpha.Trans
58
58
  }>;
59
59
 
60
60
  /** @alpha */
61
- declare const settingsNavItem: _backstage_frontend_plugin_api.ExtensionDefinition<{
61
+ declare const settingsNavItem: _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
62
62
  kind: "nav-item";
63
63
  name: undefined;
64
64
  config: {};
@@ -81,7 +81,7 @@ declare const settingsNavItem: _backstage_frontend_plugin_api.ExtensionDefinitio
81
81
  declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
82
82
  root: _backstage_frontend_plugin_api.RouteRef<undefined>;
83
83
  }, {}, {
84
- "nav-item:user-settings": _backstage_frontend_plugin_api.ExtensionDefinition<{
84
+ "nav-item:user-settings": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
85
85
  kind: "nav-item";
86
86
  name: undefined;
87
87
  config: {};
@@ -98,7 +98,7 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
98
98
  routeRef: _backstage_frontend_plugin_api.RouteRef<undefined>;
99
99
  };
100
100
  }>;
101
- "page:user-settings": _backstage_frontend_plugin_api.ExtensionDefinition<{
101
+ "page:user-settings": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
102
102
  config: {
103
103
  path: string | undefined;
104
104
  };
@@ -8,6 +8,15 @@ const JSON_HEADERS = {
8
8
  };
9
9
  const buckets = /* @__PURE__ */ new Map();
10
10
  class UserSettingsStorage {
11
+ subscribers = /* @__PURE__ */ new Set();
12
+ observables = /* @__PURE__ */ new Map();
13
+ namespace;
14
+ fetchApi;
15
+ discoveryApi;
16
+ errorApi;
17
+ identityApi;
18
+ fallback;
19
+ signalApi;
11
20
  constructor(namespace, fetchApi, discoveryApi, errorApi, identityApi, fallback, signalApi) {
12
21
  this.namespace = namespace;
13
22
  this.fetchApi = fetchApi;
@@ -17,8 +26,6 @@ class UserSettingsStorage {
17
26
  this.fallback = fallback;
18
27
  this.signalApi = signalApi;
19
28
  }
20
- subscribers = /* @__PURE__ */ new Set();
21
- observables = /* @__PURE__ */ new Map();
22
29
  static create(options) {
23
30
  return new UserSettingsStorage(
24
31
  options.namespace ?? "default",
@@ -1 +1 @@
1
- {"version":3,"file":"UserSettingsStorage.esm.js","sources":["../../../src/apis/StorageApi/UserSettingsStorage.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebStorage } from '@backstage/core-app-api';\nimport {\n DiscoveryApi,\n ErrorApi,\n FetchApi,\n IdentityApi,\n StorageApi,\n StorageValueSnapshot,\n} from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonValue, Observable } from '@backstage/types';\nimport { SignalApi, SignalSubscriber } from '@backstage/plugin-signals-react';\nimport ObservableImpl from 'zen-observable';\nimport { UserSettingsSignal } from '@backstage/plugin-user-settings-common';\n\nconst JSON_HEADERS = {\n 'Content-Type': 'application/json; charset=utf-8',\n Accept: 'application/json',\n};\n\nconst buckets = new Map<string, UserSettingsStorage>();\n\n/**\n * An implementation of the storage API, that uses the user-settings backend to\n * persist the data in the DB.\n *\n * @public\n */\nexport class UserSettingsStorage implements StorageApi {\n private subscribers = new Set<\n ZenObservable.SubscriptionObserver<StorageValueSnapshot<JsonValue>>\n >();\n\n private readonly observables = new Map<\n string,\n Observable<StorageValueSnapshot<JsonValue>>\n >();\n\n private constructor(\n private readonly namespace: string,\n private readonly fetchApi: FetchApi,\n private readonly discoveryApi: DiscoveryApi,\n private readonly errorApi: ErrorApi,\n private readonly identityApi: IdentityApi,\n private readonly fallback: WebStorage,\n private readonly signalApi?: SignalApi,\n ) {}\n\n static create(options: {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n errorApi: ErrorApi;\n identityApi: IdentityApi;\n signalApi?: SignalApi;\n namespace?: string;\n }): UserSettingsStorage {\n return new UserSettingsStorage(\n options.namespace ?? 'default',\n options.fetchApi,\n options.discoveryApi,\n options.errorApi,\n options.identityApi,\n WebStorage.create({\n namespace: options.namespace,\n errorApi: options.errorApi,\n }),\n options.signalApi,\n );\n }\n\n forBucket(name: string): StorageApi {\n // use dot instead of slash separator to have nicer URLs\n const bucketPath = `${this.namespace}.${name}`;\n\n if (!buckets.has(bucketPath)) {\n buckets.set(\n bucketPath,\n new UserSettingsStorage(\n bucketPath,\n this.fetchApi,\n this.discoveryApi,\n this.errorApi,\n this.identityApi,\n this.fallback,\n ),\n );\n }\n\n return buckets.get(bucketPath)!;\n }\n\n async remove(key: string): Promise<void> {\n const fetchUrl = await this.getFetchUrl(key);\n\n const response = await this.fetchApi.fetch(fetchUrl, {\n method: 'DELETE',\n });\n\n if (!response.ok && response.status !== 404) {\n throw await ResponseError.fromResponse(response);\n }\n\n this.notifyChanges({ key, presence: 'absent' });\n }\n\n async set<T extends JsonValue>(key: string, data: T): Promise<void> {\n if (!(await this.isSignedIn())) {\n await this.fallback.set(key, data);\n this.notifyChanges({ key, presence: 'present', value: data });\n return;\n }\n\n const fetchUrl = await this.getFetchUrl(key);\n\n const response = await this.fetchApi.fetch(fetchUrl, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ value: data }),\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const { value } = await response.json();\n\n this.notifyChanges({ key, value, presence: 'present' });\n }\n\n observe$<T extends JsonValue>(\n key: string,\n ): Observable<StorageValueSnapshot<T>> {\n if (!this.observables.has(key)) {\n this.observables.set(\n key,\n new ObservableImpl<StorageValueSnapshot<JsonValue>>(subscriber => {\n let signalSubscription: SignalSubscriber | undefined;\n this.subscribers.add(subscriber);\n\n const updateSnapshot = () => {\n Promise.resolve()\n .then(() => this.get(key))\n .then(snapshot => subscriber.next(snapshot))\n .catch(error => this.errorApi.post(error));\n };\n\n if (this.signalApi) {\n signalSubscription = this.signalApi.subscribe(\n `user-settings`,\n (msg: UserSettingsSignal) => {\n if (msg.key === key) {\n updateSnapshot();\n }\n },\n );\n }\n\n updateSnapshot();\n\n return () => {\n if (signalSubscription) {\n signalSubscription.unsubscribe();\n }\n this.subscribers.delete(subscriber);\n };\n }).filter(({ key: messageKey }) => messageKey === key),\n );\n }\n\n return this.observables.get(key) as Observable<StorageValueSnapshot<T>>;\n }\n\n snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T> {\n return { key, presence: 'unknown' };\n }\n\n private async get<T extends JsonValue>(\n key: string,\n ): Promise<StorageValueSnapshot<T>> {\n if (!(await this.isSignedIn())) {\n // This explicitly uses WebStorage, which we know is synchronous and doesn't return presence: unknown\n return this.fallback.snapshot(key);\n }\n\n const fetchUrl = await this.getFetchUrl(key);\n const response = await this.fetchApi.fetch(fetchUrl);\n\n if (response.status === 404) {\n return { key, presence: 'absent' };\n }\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n try {\n const { value: rawValue } = await response.json();\n const value = JSON.parse(JSON.stringify(rawValue), (_key, val) => {\n if (typeof val === 'object' && val !== null) {\n Object.freeze(val);\n }\n return val;\n });\n\n return { key, presence: 'present', value };\n } catch {\n // If the value is not valid JSON, we return an unknown presence. This should never happen\n return { key, presence: 'absent' };\n }\n }\n\n private async getFetchUrl(key: string) {\n const baseUrl = await this.discoveryApi.getBaseUrl('user-settings');\n const encodedNamespace = encodeURIComponent(this.namespace);\n const encodedKey = encodeURIComponent(key);\n return `${baseUrl}/buckets/${encodedNamespace}/keys/${encodedKey}`;\n }\n\n private async notifyChanges<T extends JsonValue>(\n snapshot: StorageValueSnapshot<T>,\n ) {\n for (const subscription of this.subscribers) {\n try {\n subscription.next(snapshot);\n } catch {\n // ignore\n }\n }\n }\n\n private async isSignedIn(): Promise<boolean> {\n try {\n const credentials = await this.identityApi.getCredentials();\n return credentials?.token ? true : false;\n } catch {\n return false;\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"UserSettingsStorage.esm.js","sources":["../../../src/apis/StorageApi/UserSettingsStorage.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebStorage } from '@backstage/core-app-api';\nimport {\n DiscoveryApi,\n ErrorApi,\n FetchApi,\n IdentityApi,\n StorageApi,\n StorageValueSnapshot,\n} from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonValue, Observable } from '@backstage/types';\nimport { SignalApi, SignalSubscriber } from '@backstage/plugin-signals-react';\nimport ObservableImpl from 'zen-observable';\nimport { UserSettingsSignal } from '@backstage/plugin-user-settings-common';\n\nconst JSON_HEADERS = {\n 'Content-Type': 'application/json; charset=utf-8',\n Accept: 'application/json',\n};\n\nconst buckets = new Map<string, UserSettingsStorage>();\n\n/**\n * An implementation of the storage API, that uses the user-settings backend to\n * persist the data in the DB.\n *\n * @public\n */\nexport class UserSettingsStorage implements StorageApi {\n private subscribers = new Set<\n ZenObservable.SubscriptionObserver<StorageValueSnapshot<JsonValue>>\n >();\n\n private readonly observables = new Map<\n string,\n Observable<StorageValueSnapshot<JsonValue>>\n >();\n\n private readonly namespace: string;\n private readonly fetchApi: FetchApi;\n private readonly discoveryApi: DiscoveryApi;\n private readonly errorApi: ErrorApi;\n private readonly identityApi: IdentityApi;\n private readonly fallback: WebStorage;\n private readonly signalApi?: SignalApi;\n\n private constructor(\n namespace: string,\n fetchApi: FetchApi,\n discoveryApi: DiscoveryApi,\n errorApi: ErrorApi,\n identityApi: IdentityApi,\n fallback: WebStorage,\n signalApi?: SignalApi,\n ) {\n this.namespace = namespace;\n this.fetchApi = fetchApi;\n this.discoveryApi = discoveryApi;\n this.errorApi = errorApi;\n this.identityApi = identityApi;\n this.fallback = fallback;\n this.signalApi = signalApi;\n }\n\n static create(options: {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n errorApi: ErrorApi;\n identityApi: IdentityApi;\n signalApi?: SignalApi;\n namespace?: string;\n }): UserSettingsStorage {\n return new UserSettingsStorage(\n options.namespace ?? 'default',\n options.fetchApi,\n options.discoveryApi,\n options.errorApi,\n options.identityApi,\n WebStorage.create({\n namespace: options.namespace,\n errorApi: options.errorApi,\n }),\n options.signalApi,\n );\n }\n\n forBucket(name: string): StorageApi {\n // use dot instead of slash separator to have nicer URLs\n const bucketPath = `${this.namespace}.${name}`;\n\n if (!buckets.has(bucketPath)) {\n buckets.set(\n bucketPath,\n new UserSettingsStorage(\n bucketPath,\n this.fetchApi,\n this.discoveryApi,\n this.errorApi,\n this.identityApi,\n this.fallback,\n ),\n );\n }\n\n return buckets.get(bucketPath)!;\n }\n\n async remove(key: string): Promise<void> {\n const fetchUrl = await this.getFetchUrl(key);\n\n const response = await this.fetchApi.fetch(fetchUrl, {\n method: 'DELETE',\n });\n\n if (!response.ok && response.status !== 404) {\n throw await ResponseError.fromResponse(response);\n }\n\n this.notifyChanges({ key, presence: 'absent' });\n }\n\n async set<T extends JsonValue>(key: string, data: T): Promise<void> {\n if (!(await this.isSignedIn())) {\n await this.fallback.set(key, data);\n this.notifyChanges({ key, presence: 'present', value: data });\n return;\n }\n\n const fetchUrl = await this.getFetchUrl(key);\n\n const response = await this.fetchApi.fetch(fetchUrl, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ value: data }),\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const { value } = await response.json();\n\n this.notifyChanges({ key, value, presence: 'present' });\n }\n\n observe$<T extends JsonValue>(\n key: string,\n ): Observable<StorageValueSnapshot<T>> {\n if (!this.observables.has(key)) {\n this.observables.set(\n key,\n new ObservableImpl<StorageValueSnapshot<JsonValue>>(subscriber => {\n let signalSubscription: SignalSubscriber | undefined;\n this.subscribers.add(subscriber);\n\n const updateSnapshot = () => {\n Promise.resolve()\n .then(() => this.get(key))\n .then(snapshot => subscriber.next(snapshot))\n .catch(error => this.errorApi.post(error));\n };\n\n if (this.signalApi) {\n signalSubscription = this.signalApi.subscribe(\n `user-settings`,\n (msg: UserSettingsSignal) => {\n if (msg.key === key) {\n updateSnapshot();\n }\n },\n );\n }\n\n updateSnapshot();\n\n return () => {\n if (signalSubscription) {\n signalSubscription.unsubscribe();\n }\n this.subscribers.delete(subscriber);\n };\n }).filter(({ key: messageKey }) => messageKey === key),\n );\n }\n\n return this.observables.get(key) as Observable<StorageValueSnapshot<T>>;\n }\n\n snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T> {\n return { key, presence: 'unknown' };\n }\n\n private async get<T extends JsonValue>(\n key: string,\n ): Promise<StorageValueSnapshot<T>> {\n if (!(await this.isSignedIn())) {\n // This explicitly uses WebStorage, which we know is synchronous and doesn't return presence: unknown\n return this.fallback.snapshot(key);\n }\n\n const fetchUrl = await this.getFetchUrl(key);\n const response = await this.fetchApi.fetch(fetchUrl);\n\n if (response.status === 404) {\n return { key, presence: 'absent' };\n }\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n try {\n const { value: rawValue } = await response.json();\n const value = JSON.parse(JSON.stringify(rawValue), (_key, val) => {\n if (typeof val === 'object' && val !== null) {\n Object.freeze(val);\n }\n return val;\n });\n\n return { key, presence: 'present', value };\n } catch {\n // If the value is not valid JSON, we return an unknown presence. This should never happen\n return { key, presence: 'absent' };\n }\n }\n\n private async getFetchUrl(key: string) {\n const baseUrl = await this.discoveryApi.getBaseUrl('user-settings');\n const encodedNamespace = encodeURIComponent(this.namespace);\n const encodedKey = encodeURIComponent(key);\n return `${baseUrl}/buckets/${encodedNamespace}/keys/${encodedKey}`;\n }\n\n private async notifyChanges<T extends JsonValue>(\n snapshot: StorageValueSnapshot<T>,\n ) {\n for (const subscription of this.subscribers) {\n try {\n subscription.next(snapshot);\n } catch {\n // ignore\n }\n }\n }\n\n private async isSignedIn(): Promise<boolean> {\n try {\n const credentials = await this.identityApi.getCredentials();\n return credentials?.token ? true : false;\n } catch {\n return false;\n }\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ import { TabProps } from '@material-ui/core/Tab';
13
13
  * @public
14
14
  */
15
15
  declare class UserSettingsStorage implements StorageApi {
16
+ private subscribers;
17
+ private readonly observables;
16
18
  private readonly namespace;
17
19
  private readonly fetchApi;
18
20
  private readonly discoveryApi;
@@ -20,8 +22,6 @@ declare class UserSettingsStorage implements StorageApi {
20
22
  private readonly identityApi;
21
23
  private readonly fallback;
22
24
  private readonly signalApi?;
23
- private subscribers;
24
- private readonly observables;
25
25
  private constructor();
26
26
  static create(options: {
27
27
  fetchApi: FetchApi;
@@ -1,5 +1,5 @@
1
1
  var name = "@backstage/plugin-user-settings";
2
- var version = "0.8.28";
2
+ var version = "0.8.29";
3
3
  var description = "A Backstage plugin that provides a settings page";
4
4
  var backstage = {
5
5
  role: "frontend-plugin",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-user-settings",
3
- "version": "0.8.28",
3
+ "version": "0.8.29",
4
4
  "description": "A Backstage plugin that provides a settings page",
5
5
  "backstage": {
6
6
  "role": "frontend-plugin",
@@ -67,15 +67,15 @@
67
67
  "test": "backstage-cli package test"
68
68
  },
69
69
  "dependencies": {
70
- "@backstage/catalog-model": "^1.7.5",
71
- "@backstage/core-app-api": "^1.19.1",
72
- "@backstage/core-compat-api": "^0.5.3",
73
- "@backstage/core-components": "^0.18.2",
74
- "@backstage/core-plugin-api": "^1.11.1",
70
+ "@backstage/catalog-model": "^1.7.6",
71
+ "@backstage/core-app-api": "^1.19.2",
72
+ "@backstage/core-compat-api": "^0.5.4",
73
+ "@backstage/core-components": "^0.18.3",
74
+ "@backstage/core-plugin-api": "^1.12.0",
75
75
  "@backstage/errors": "^1.2.7",
76
- "@backstage/frontend-plugin-api": "^0.12.1",
77
- "@backstage/plugin-catalog-react": "^1.21.2",
78
- "@backstage/plugin-signals-react": "^0.0.16",
76
+ "@backstage/frontend-plugin-api": "^0.13.0",
77
+ "@backstage/plugin-catalog-react": "^1.21.3",
78
+ "@backstage/plugin-signals-react": "^0.0.17",
79
79
  "@backstage/plugin-user-settings-common": "^0.0.1",
80
80
  "@backstage/theme": "^0.7.0",
81
81
  "@backstage/types": "^1.2.2",
@@ -86,10 +86,10 @@
86
86
  "zen-observable": "^0.10.0"
87
87
  },
88
88
  "devDependencies": {
89
- "@backstage/cli": "^0.34.4",
90
- "@backstage/dev-utils": "^1.1.16",
91
- "@backstage/plugin-catalog": "^1.31.4",
92
- "@backstage/test-utils": "^1.7.12",
89
+ "@backstage/cli": "^0.34.5",
90
+ "@backstage/dev-utils": "^1.1.17",
91
+ "@backstage/plugin-catalog": "^1.32.0",
92
+ "@backstage/test-utils": "^1.7.13",
93
93
  "@testing-library/dom": "^10.0.0",
94
94
  "@testing-library/jest-dom": "^6.0.0",
95
95
  "@testing-library/react": "^16.0.0",