@guinetik/primitives-ts 0.14.1 → 0.15.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/dist/home-dashboard.types.d.ts +15 -2
- package/dist/home-dashboard.types.d.ts.map +1 -1
- package/dist/home-dashboard.types.js +35 -1
- package/dist/home-dashboard.types.spec.js +31 -0
- package/dist/widget-services.types.d.ts +67 -1
- package/dist/widget-services.types.d.ts.map +1 -1
- package/dist/widget-services.types.js +41 -0
- package/dist/widget-services.types.spec.js +27 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { GeoLocation } from "./geo.types";
|
|
2
|
-
export type HomeWidgetType = "launchpad" | "clock" | "webpage" | "analytics" | "utility" | "news" | "weather" | "currency";
|
|
2
|
+
export type HomeWidgetType = "launchpad" | "clock" | "webpage" | "analytics" | "utility" | "news" | "weather" | "currency" | "crypto" | "apod";
|
|
3
3
|
export type HomeWidgetColSpan = 1 | 2 | 3;
|
|
4
4
|
export interface LaunchpadWidgetConfig {
|
|
5
5
|
/** Launchpad group ids to show. Omitted on legacy widgets means all groups. */
|
|
@@ -51,7 +51,20 @@ export interface CurrencyWidgetConfig {
|
|
|
51
51
|
quote: string;
|
|
52
52
|
defaultAmount: number;
|
|
53
53
|
}
|
|
54
|
-
export
|
|
54
|
+
export declare const CRYPTO_MAX_COINS = 5;
|
|
55
|
+
export interface CryptoWidgetConfig {
|
|
56
|
+
/** Enabled provider to use. Omitted means the category default. */
|
|
57
|
+
providerId?: string;
|
|
58
|
+
/** CoinGecko-style coin ids, e.g. "bitcoin". */
|
|
59
|
+
coins: string[];
|
|
60
|
+
/** Uppercase ISO 4217 code the coins are priced in, e.g. "BRL". */
|
|
61
|
+
vsCurrency: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ApodWidgetConfig {
|
|
64
|
+
/** Enabled NASA provider to use. Omitted means the NASA category default. */
|
|
65
|
+
providerId?: string;
|
|
66
|
+
}
|
|
67
|
+
export type HomeWidgetConfig = LaunchpadWidgetConfig | ClockWidgetConfig | WebpageWidgetConfig | AnalyticsWidgetConfig | UtilityWidgetConfig | NewsWidgetConfig | WeatherWidgetConfig | CurrencyWidgetConfig | CryptoWidgetConfig | ApodWidgetConfig;
|
|
55
68
|
export interface HomeDashboardWidget {
|
|
56
69
|
id: string;
|
|
57
70
|
/** Optional user-defined chrome title. The widget type is used when omitted. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home-dashboard.types.d.ts","sourceRoot":"","sources":["../src/home-dashboard.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,OAAO,GACP,SAAS,GACT,WAAW,GACX,SAAS,GACT,MAAM,GACN,SAAS,GACT,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"home-dashboard.types.d.ts","sourceRoot":"","sources":["../src/home-dashboard.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CLOCK_PRESETS = exports.CLOCK_DEFAULT_PRESET = exports.CLOCK_DEFAULT_TIMEZONE = exports.MAX_HOME_WIDGETS = exports.HOME_WIDGET_TYPES = exports.UTILITY_TOOL_IDS = void 0;
|
|
3
|
+
exports.CLOCK_PRESETS = exports.CLOCK_DEFAULT_PRESET = exports.CLOCK_DEFAULT_TIMEZONE = exports.MAX_HOME_WIDGETS = exports.HOME_WIDGET_TYPES = exports.CRYPTO_MAX_COINS = exports.UTILITY_TOOL_IDS = void 0;
|
|
4
4
|
exports.isHomeWidgetType = isHomeWidgetType;
|
|
5
5
|
exports.validateHomeWidgetConfig = validateHomeWidgetConfig;
|
|
6
6
|
exports.UTILITY_TOOL_IDS = [
|
|
@@ -8,6 +8,7 @@ exports.UTILITY_TOOL_IDS = [
|
|
|
8
8
|
"text-workbench",
|
|
9
9
|
"date-calculator",
|
|
10
10
|
];
|
|
11
|
+
exports.CRYPTO_MAX_COINS = 5;
|
|
11
12
|
exports.HOME_WIDGET_TYPES = [
|
|
12
13
|
"launchpad",
|
|
13
14
|
"clock",
|
|
@@ -17,6 +18,8 @@ exports.HOME_WIDGET_TYPES = [
|
|
|
17
18
|
"news",
|
|
18
19
|
"weather",
|
|
19
20
|
"currency",
|
|
21
|
+
"crypto",
|
|
22
|
+
"apod",
|
|
20
23
|
];
|
|
21
24
|
exports.MAX_HOME_WIDGETS = 30;
|
|
22
25
|
exports.CLOCK_DEFAULT_TIMEZONE = "America/Sao_Paulo";
|
|
@@ -186,6 +189,37 @@ const HOME_WIDGET_CONFIG_VALIDATORS = {
|
|
|
186
189
|
? "currency widget requires base, quote, and defaultAmount"
|
|
187
190
|
: null;
|
|
188
191
|
},
|
|
192
|
+
crypto(config) {
|
|
193
|
+
const value = configRecord(config);
|
|
194
|
+
if (!value)
|
|
195
|
+
return "crypto widget config must be an object";
|
|
196
|
+
const providerError = providerIdError(value);
|
|
197
|
+
if (providerError)
|
|
198
|
+
return providerError;
|
|
199
|
+
const coins = value.coins;
|
|
200
|
+
if (!Array.isArray(coins) ||
|
|
201
|
+
coins.length < 1 ||
|
|
202
|
+
coins.length > exports.CRYPTO_MAX_COINS ||
|
|
203
|
+
coins.some((coin) => typeof coin !== "string" || !/^[a-z0-9-]{1,100}$/.test(coin)) ||
|
|
204
|
+
new Set(coins).size !== coins.length) {
|
|
205
|
+
return `crypto widget requires 1 to ${exports.CRYPTO_MAX_COINS} unique lowercase coin ids`;
|
|
206
|
+
}
|
|
207
|
+
return typeof value.vsCurrency !== "string" ||
|
|
208
|
+
!/^[A-Z]{3}$/.test(value.vsCurrency)
|
|
209
|
+
? "crypto widget vsCurrency must be a 3-letter uppercase currency code"
|
|
210
|
+
: null;
|
|
211
|
+
},
|
|
212
|
+
apod(config) {
|
|
213
|
+
const value = configRecord(config);
|
|
214
|
+
if (!value)
|
|
215
|
+
return "apod widget config must be an object";
|
|
216
|
+
const providerError = providerIdError(value);
|
|
217
|
+
if (providerError)
|
|
218
|
+
return providerError;
|
|
219
|
+
return Object.keys(value).some((key) => key !== "providerId")
|
|
220
|
+
? "apod widget config contains an unsupported setting"
|
|
221
|
+
: null;
|
|
222
|
+
},
|
|
189
223
|
};
|
|
190
224
|
function validateHomeWidgetConfig(type, config) {
|
|
191
225
|
return HOME_WIDGET_CONFIG_VALIDATORS[type](config);
|
|
@@ -16,13 +16,44 @@ describe("home dashboard types", () => {
|
|
|
16
16
|
"news",
|
|
17
17
|
"weather",
|
|
18
18
|
"currency",
|
|
19
|
+
"crypto",
|
|
20
|
+
"apod",
|
|
19
21
|
]);
|
|
20
22
|
expect((0, home_dashboard_types_1.isHomeWidgetType)("clock")).toBe(true);
|
|
21
23
|
expect((0, home_dashboard_types_1.isHomeWidgetType)("analytics")).toBe(true);
|
|
22
24
|
expect((0, home_dashboard_types_1.isHomeWidgetType)("utility")).toBe(true);
|
|
23
25
|
expect((0, home_dashboard_types_1.isHomeWidgetType)("weather")).toBe(true);
|
|
26
|
+
expect((0, home_dashboard_types_1.isHomeWidgetType)("apod")).toBe(true);
|
|
24
27
|
expect((0, home_dashboard_types_1.isHomeWidgetType)("stock-ticker")).toBe(false);
|
|
25
28
|
});
|
|
29
|
+
it("validates the APOD provider selection", () => {
|
|
30
|
+
expect((0, home_dashboard_types_1.validateHomeWidgetConfig)("apod", {})).toBeNull();
|
|
31
|
+
expect((0, home_dashboard_types_1.validateHomeWidgetConfig)("apod", { providerId: "nasa-1" })).toBeNull();
|
|
32
|
+
expect((0, home_dashboard_types_1.validateHomeWidgetConfig)("apod", { providerId: "" })).not.toBeNull();
|
|
33
|
+
});
|
|
34
|
+
it("validates crypto widget configs", () => {
|
|
35
|
+
expect((0, home_dashboard_types_1.validateHomeWidgetConfig)("crypto", {
|
|
36
|
+
coins: ["bitcoin", "ethereum"],
|
|
37
|
+
vsCurrency: "BRL",
|
|
38
|
+
})).toBeNull();
|
|
39
|
+
expect((0, home_dashboard_types_1.validateHomeWidgetConfig)("crypto", {
|
|
40
|
+
providerId: "crypto-1",
|
|
41
|
+
coins: ["bitcoin"],
|
|
42
|
+
vsCurrency: "USD",
|
|
43
|
+
})).toBeNull();
|
|
44
|
+
const invalid = [
|
|
45
|
+
{ coins: [], vsCurrency: "BRL" },
|
|
46
|
+
{ coins: ["a", "b", "c", "d", "e", "f"], vsCurrency: "BRL" },
|
|
47
|
+
{ coins: ["bitcoin", "bitcoin"], vsCurrency: "BRL" },
|
|
48
|
+
{ coins: ["Bitcoin!"], vsCurrency: "BRL" },
|
|
49
|
+
{ coins: ["bitcoin"], vsCurrency: "brl" },
|
|
50
|
+
{ coins: ["bitcoin"], vsCurrency: "BRLX" },
|
|
51
|
+
{ providerId: "", coins: ["bitcoin"], vsCurrency: "BRL" },
|
|
52
|
+
];
|
|
53
|
+
for (const config of invalid) {
|
|
54
|
+
expect((0, home_dashboard_types_1.validateHomeWidgetConfig)("crypto", config)).not.toBeNull();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
26
57
|
it("stores a versioned allowlisted utility configuration", () => {
|
|
27
58
|
const widget = {
|
|
28
59
|
id: "utility-1",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Shared contracts for provider-backed dashboard widgets. */
|
|
2
|
-
export type WidgetServiceKind = "news" | "weather" | "currency" | "geo";
|
|
2
|
+
export type WidgetServiceKind = "news" | "weather" | "currency" | "geo" | "crypto" | "nasa";
|
|
3
3
|
export type WidgetServiceSettingValue = string | number | boolean;
|
|
4
4
|
export type WidgetServiceSettings = Record<string, WidgetServiceSettingValue>;
|
|
5
5
|
export interface WidgetServiceProviderSettingsMap {
|
|
@@ -11,6 +11,12 @@ export interface WidgetServiceProviderSettingsMap {
|
|
|
11
11
|
"google-places": {
|
|
12
12
|
apiKey: string;
|
|
13
13
|
};
|
|
14
|
+
coingecko: {
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
};
|
|
17
|
+
nasa: {
|
|
18
|
+
apiKey?: string;
|
|
19
|
+
};
|
|
14
20
|
}
|
|
15
21
|
export type WidgetServiceProviderType = keyof WidgetServiceProviderSettingsMap;
|
|
16
22
|
export interface WidgetServiceSettingsField {
|
|
@@ -37,6 +43,12 @@ declare function normalizeRssSettings(settings: WidgetServiceSettings): {
|
|
|
37
43
|
declare function normalizeGooglePlacesSettings(settings: WidgetServiceSettings): {
|
|
38
44
|
apiKey: string;
|
|
39
45
|
};
|
|
46
|
+
declare function normalizeCoingeckoSettings(settings: WidgetServiceSettings): {
|
|
47
|
+
apiKey?: string;
|
|
48
|
+
};
|
|
49
|
+
declare function normalizeOptionalApiKey(settings: WidgetServiceSettings): {
|
|
50
|
+
apiKey?: string;
|
|
51
|
+
};
|
|
40
52
|
/** Single registration point for provider metadata and per-type settings schemas. */
|
|
41
53
|
export declare const WIDGET_SERVICE_PROVIDER_DEFINITIONS: {
|
|
42
54
|
rss: {
|
|
@@ -84,6 +96,34 @@ export declare const WIDGET_SERVICE_PROVIDER_DEFINITIONS: {
|
|
|
84
96
|
secretSettingKeys: "apiKey"[];
|
|
85
97
|
normalizeSettings: typeof normalizeGooglePlacesSettings;
|
|
86
98
|
};
|
|
99
|
+
coingecko: {
|
|
100
|
+
type: "coingecko";
|
|
101
|
+
kind: "crypto";
|
|
102
|
+
label: string;
|
|
103
|
+
settingsFields: {
|
|
104
|
+
key: string;
|
|
105
|
+
label: string;
|
|
106
|
+
input: "password";
|
|
107
|
+
required: false;
|
|
108
|
+
maxLength: number;
|
|
109
|
+
}[];
|
|
110
|
+
secretSettingKeys: "apiKey"[];
|
|
111
|
+
normalizeSettings: typeof normalizeCoingeckoSettings;
|
|
112
|
+
};
|
|
113
|
+
nasa: {
|
|
114
|
+
type: "nasa";
|
|
115
|
+
kind: "nasa";
|
|
116
|
+
label: string;
|
|
117
|
+
settingsFields: {
|
|
118
|
+
key: string;
|
|
119
|
+
label: string;
|
|
120
|
+
input: "password";
|
|
121
|
+
required: false;
|
|
122
|
+
maxLength: number;
|
|
123
|
+
}[];
|
|
124
|
+
secretSettingKeys: "apiKey"[];
|
|
125
|
+
normalizeSettings: typeof normalizeOptionalApiKey;
|
|
126
|
+
};
|
|
87
127
|
};
|
|
88
128
|
export declare const WIDGET_SERVICE_PROVIDER_TYPES: readonly (keyof WidgetServiceProviderSettingsMap)[];
|
|
89
129
|
export declare function normalizeWidgetServiceProviderSettings<T extends WidgetServiceProviderType>(type: T, settings: WidgetServiceSettings): WidgetServiceProviderSettingsMap[T];
|
|
@@ -145,5 +185,31 @@ export interface CurrencyRate {
|
|
|
145
185
|
rate: number;
|
|
146
186
|
date: string;
|
|
147
187
|
}
|
|
188
|
+
export interface CryptoQuote {
|
|
189
|
+
/** CoinGecko-style coin id, e.g. "bitcoin". */
|
|
190
|
+
coinId: string;
|
|
191
|
+
/** Display symbol, e.g. "BTC". */
|
|
192
|
+
symbol: string;
|
|
193
|
+
/** Price in `vsCurrency`. */
|
|
194
|
+
price: number;
|
|
195
|
+
/** Uppercase ISO 4217 code the price is quoted in. */
|
|
196
|
+
vsCurrency: string;
|
|
197
|
+
change24hPercent: number;
|
|
198
|
+
fetchedAt: string;
|
|
199
|
+
}
|
|
200
|
+
export interface CryptoBoard {
|
|
201
|
+
quotes: CryptoQuote[];
|
|
202
|
+
fetchedAt: string;
|
|
203
|
+
}
|
|
204
|
+
export interface AstronomyPictureOfTheDay {
|
|
205
|
+
date: string;
|
|
206
|
+
title: string;
|
|
207
|
+
explanation: string;
|
|
208
|
+
mediaType: "image" | "video";
|
|
209
|
+
url: string;
|
|
210
|
+
hdUrl?: string;
|
|
211
|
+
thumbnailUrl?: string;
|
|
212
|
+
copyright?: string;
|
|
213
|
+
}
|
|
148
214
|
export {};
|
|
149
215
|
//# sourceMappingURL=widget-services.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-services.types.d.ts","sourceRoot":"","sources":["../src/widget-services.types.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"widget-services.types.d.ts","sourceRoot":"","sources":["../src/widget-services.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -38,6 +38,15 @@ function normalizeGooglePlacesSettings(settings) {
|
|
|
38
38
|
}
|
|
39
39
|
return { apiKey };
|
|
40
40
|
}
|
|
41
|
+
function normalizeCoingeckoSettings(settings) {
|
|
42
|
+
const apiKey = typeof settings.apiKey === "string" ? settings.apiKey.trim() : "";
|
|
43
|
+
// The free tier needs no key; a surviving mask means no key was ever stored.
|
|
44
|
+
return apiKey && apiKey !== exports.WIDGET_SERVICE_SECRET_MASK ? { apiKey } : {};
|
|
45
|
+
}
|
|
46
|
+
function normalizeOptionalApiKey(settings) {
|
|
47
|
+
const apiKey = typeof settings.apiKey === "string" ? settings.apiKey.trim() : "";
|
|
48
|
+
return apiKey && apiKey !== exports.WIDGET_SERVICE_SECRET_MASK ? { apiKey } : {};
|
|
49
|
+
}
|
|
41
50
|
/** Single registration point for provider metadata and per-type settings schemas. */
|
|
42
51
|
exports.WIDGET_SERVICE_PROVIDER_DEFINITIONS = {
|
|
43
52
|
rss: {
|
|
@@ -89,6 +98,38 @@ exports.WIDGET_SERVICE_PROVIDER_DEFINITIONS = {
|
|
|
89
98
|
secretSettingKeys: ["apiKey"],
|
|
90
99
|
normalizeSettings: normalizeGooglePlacesSettings,
|
|
91
100
|
},
|
|
101
|
+
coingecko: {
|
|
102
|
+
type: "coingecko",
|
|
103
|
+
kind: "crypto",
|
|
104
|
+
label: "CoinGecko",
|
|
105
|
+
settingsFields: [
|
|
106
|
+
{
|
|
107
|
+
key: "apiKey",
|
|
108
|
+
label: "API key (optional)",
|
|
109
|
+
input: "password",
|
|
110
|
+
required: false,
|
|
111
|
+
maxLength: 512,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
secretSettingKeys: ["apiKey"],
|
|
115
|
+
normalizeSettings: normalizeCoingeckoSettings,
|
|
116
|
+
},
|
|
117
|
+
nasa: {
|
|
118
|
+
type: "nasa",
|
|
119
|
+
kind: "nasa",
|
|
120
|
+
label: "NASA Open APIs",
|
|
121
|
+
settingsFields: [
|
|
122
|
+
{
|
|
123
|
+
key: "apiKey",
|
|
124
|
+
label: "API key (optional; DEMO_KEY when empty)",
|
|
125
|
+
input: "password",
|
|
126
|
+
required: false,
|
|
127
|
+
maxLength: 512,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
secretSettingKeys: ["apiKey"],
|
|
131
|
+
normalizeSettings: normalizeOptionalApiKey,
|
|
132
|
+
},
|
|
92
133
|
};
|
|
93
134
|
exports.WIDGET_SERVICE_PROVIDER_TYPES = Object.freeze(Object.keys(exports.WIDGET_SERVICE_PROVIDER_DEFINITIONS));
|
|
94
135
|
function normalizeWidgetServiceProviderSettings(type, settings) {
|
|
@@ -9,9 +9,17 @@ describe("widget service contracts", () => {
|
|
|
9
9
|
value: { base: "USD", quote: "EUR", rate: 0.86, date: "2026-09-02" },
|
|
10
10
|
};
|
|
11
11
|
const weather = {};
|
|
12
|
+
const apod = {
|
|
13
|
+
date: "2026-09-04",
|
|
14
|
+
title: "The sky today",
|
|
15
|
+
explanation: "A test entry",
|
|
16
|
+
mediaType: "video",
|
|
17
|
+
url: "https://www.youtube.com/embed/example",
|
|
18
|
+
};
|
|
12
19
|
expect(news.ok).toBe(false);
|
|
13
20
|
expect(currency.ok).toBe(true);
|
|
14
21
|
expect(weather).toEqual({});
|
|
22
|
+
expect(apod.mediaType).toBe("video");
|
|
15
23
|
});
|
|
16
24
|
});
|
|
17
25
|
describe("widget service provider definitions", () => {
|
|
@@ -21,11 +29,30 @@ describe("widget service provider definitions", () => {
|
|
|
21
29
|
"open-meteo",
|
|
22
30
|
"frankfurter",
|
|
23
31
|
"google-places",
|
|
32
|
+
"coingecko",
|
|
33
|
+
"nasa",
|
|
24
34
|
]);
|
|
25
35
|
expect(widget_services_types_1.WIDGET_SERVICE_PROVIDER_DEFINITIONS["google-places"]).toMatchObject({
|
|
26
36
|
kind: "geo",
|
|
27
37
|
secretSettingKeys: ["apiKey"],
|
|
28
38
|
});
|
|
39
|
+
expect(widget_services_types_1.WIDGET_SERVICE_PROVIDER_DEFINITIONS.coingecko).toMatchObject({
|
|
40
|
+
kind: "crypto",
|
|
41
|
+
secretSettingKeys: ["apiKey"],
|
|
42
|
+
});
|
|
43
|
+
expect(widget_services_types_1.WIDGET_SERVICE_PROVIDER_DEFINITIONS.nasa).toMatchObject({
|
|
44
|
+
kind: "nasa",
|
|
45
|
+
secretSettingKeys: ["apiKey"],
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
it("keeps the CoinGecko API key optional", () => {
|
|
49
|
+
expect((0, widget_services_types_1.normalizeWidgetServiceProviderSettings)("coingecko", { apiKey: " k-1 " })).toEqual({ apiKey: "k-1" });
|
|
50
|
+
expect((0, widget_services_types_1.normalizeWidgetServiceProviderSettings)("coingecko", {})).toEqual({});
|
|
51
|
+
expect((0, widget_services_types_1.normalizeWidgetServiceProviderSettings)("coingecko", { apiKey: "" })).toEqual({});
|
|
52
|
+
});
|
|
53
|
+
it("keeps the NASA API key optional and trims configured keys", () => {
|
|
54
|
+
expect((0, widget_services_types_1.normalizeWidgetServiceProviderSettings)("nasa", {})).toEqual({});
|
|
55
|
+
expect((0, widget_services_types_1.normalizeWidgetServiceProviderSettings)("nasa", { apiKey: " nasa-key " })).toEqual({ apiKey: "nasa-key" });
|
|
29
56
|
});
|
|
30
57
|
it("normalizes settings through the selected provider schema", () => {
|
|
31
58
|
expect((0, widget_services_types_1.normalizeWidgetServiceProviderSettings)("rss", {
|