@hartl-services/medusa-base 0.2.0 → 0.4.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.
Files changed (97) hide show
  1. package/.medusa/server/src/admin/index.js +741 -36
  2. package/.medusa/server/src/admin/index.mjs +743 -38
  3. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/actions/[action_key]/route.js +31 -0
  4. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/actions/[action_key]/runs/route.js +16 -0
  5. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/route.js +14 -0
  6. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/settings/route.js +22 -0
  7. package/.medusa/server/src/api/admin/plugin-features/route.js +6 -4
  8. package/.medusa/server/src/api/middlewares.js +22 -8
  9. package/.medusa/server/src/api/plugin-actions.js +221 -0
  10. package/.medusa/server/src/api/plugin-features-http.js +67 -8
  11. package/.medusa/server/src/modules/plugin-features/domain/plugin-action-runs.js +92 -0
  12. package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +213 -0
  13. package/.medusa/server/src/modules/plugin-features/domain/secret-box.js +56 -0
  14. package/.medusa/server/src/modules/plugin-features/migrations/Migration20260928205031.js +16 -0
  15. package/.medusa/server/src/modules/plugin-features/migrations/Migration20260928215428.js +17 -0
  16. package/.medusa/server/src/modules/plugin-features/models/plugin-action-run.js +43 -0
  17. package/.medusa/server/src/modules/plugin-features/models/plugin-setting-state.js +32 -0
  18. package/.medusa/server/src/modules/plugin-features/service.js +419 -17
  19. package/.medusa/server/src/public/action-progress.js +67 -0
  20. package/.medusa/server/src/public/descriptor.js +199 -2
  21. package/.medusa/server/src/public/effective-features.js +9 -5
  22. package/.medusa/server/src/public/enforcement.js +18 -8
  23. package/.medusa/server/src/public/index.js +10 -2
  24. package/.medusa/server/src/public/module-access.js +14 -0
  25. package/.medusa/server/src/public/sentry-logger.js +82 -0
  26. package/.medusa/server/src/public/settings.js +17 -0
  27. package/.medusa/server/src/public/shop-master-data.js +95 -0
  28. package/.medusa/server/src/utils/discover-complete-cart-participants.js +9 -2
  29. package/.medusa/server/src/utils/discover-plugin-features.js +47 -9
  30. package/.medusa/server/src/workflows/hooks/complete-cart-participants.js +28 -16
  31. package/README.md +416 -12
  32. package/dist/admin-components/features/plugin-features/components/PluginActionsSection.d.ts +6 -0
  33. package/dist/admin-components/features/plugin-features/components/PluginActionsSection.js +66 -0
  34. package/dist/admin-components/features/plugin-features/components/PluginDetailPage.d.ts +2 -0
  35. package/dist/admin-components/features/plugin-features/components/PluginDetailPage.js +9 -0
  36. package/dist/admin-components/features/plugin-features/components/PluginFeatureGate.d.ts +22 -0
  37. package/dist/admin-components/features/plugin-features/components/PluginFeatureGate.js +18 -0
  38. package/dist/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +11 -0
  39. package/dist/admin-components/features/plugin-features/components/PluginFeatureSections.js +19 -0
  40. package/dist/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +12 -0
  41. package/dist/admin-components/features/plugin-features/components/PluginFeaturesCard.js +34 -0
  42. package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +5 -0
  43. package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +16 -0
  44. package/dist/admin-components/features/plugin-features/components/PluginHubCard.d.ts +7 -0
  45. package/dist/admin-components/features/plugin-features/components/PluginHubCard.js +21 -0
  46. package/dist/admin-components/features/plugin-features/components/PluginPageShell.d.ts +13 -0
  47. package/dist/admin-components/features/plugin-features/components/PluginPageShell.js +20 -0
  48. package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +7 -0
  49. package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.js +39 -0
  50. package/dist/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -0
  51. package/dist/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -0
  52. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +114 -0
  53. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.js +40 -0
  54. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +392 -0
  55. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.js +128 -0
  56. package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +93 -0
  57. package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.js +44 -0
  58. package/dist/admin-components/features/plugin-features/lib/action-runs.d.ts +19 -0
  59. package/dist/admin-components/features/plugin-features/lib/action-runs.js +74 -0
  60. package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +363 -0
  61. package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.js +9 -0
  62. package/dist/admin-components/features/plugin-features/lib/plugin-paths.d.ts +4 -0
  63. package/dist/admin-components/features/plugin-features/lib/plugin-paths.js +4 -0
  64. package/dist/admin-components/features/plugin-features/lib/settings-draft.d.ts +28 -0
  65. package/dist/admin-components/features/plugin-features/lib/settings-draft.js +86 -0
  66. package/dist/admin-components/features/plugin-features/queries/pluginFeatureKeys.d.ts +7 -0
  67. package/dist/admin-components/features/plugin-features/queries/pluginFeatureKeys.js +7 -0
  68. package/dist/admin-components/i18n/texts.d.ts +90 -0
  69. package/dist/admin-components/i18n/texts.js +90 -0
  70. package/dist/admin-components/index.d.ts +14 -0
  71. package/dist/admin-components/index.js +14 -0
  72. package/dist/admin-components/lib/errors.d.ts +6 -0
  73. package/dist/admin-components/lib/errors.js +27 -0
  74. package/dist/admin-components/lib/sdk.d.ts +2 -0
  75. package/dist/admin-components/lib/sdk.js +13 -0
  76. package/dist/public/action-progress.d.ts +25 -0
  77. package/dist/public/action-progress.js +66 -0
  78. package/dist/public/descriptor.d.ts +3 -2
  79. package/dist/public/descriptor.js +198 -1
  80. package/dist/public/effective-features.d.ts +2 -1
  81. package/dist/public/effective-features.js +8 -4
  82. package/dist/public/enforcement.d.ts +7 -0
  83. package/dist/public/enforcement.js +17 -7
  84. package/dist/public/index.d.ts +5 -2
  85. package/dist/public/index.js +9 -1
  86. package/dist/public/module-access.d.ts +3 -0
  87. package/dist/public/module-access.js +13 -0
  88. package/dist/public/sentry-logger.d.ts +11 -0
  89. package/dist/public/sentry-logger.js +81 -0
  90. package/dist/public/settings.d.ts +11 -0
  91. package/dist/public/settings.js +16 -0
  92. package/dist/public/shop-master-data.d.ts +25 -0
  93. package/dist/public/shop-master-data.js +94 -0
  94. package/dist/public/types.d.ts +97 -1
  95. package/dist/utils/discover-plugin-features.d.ts +28 -0
  96. package/dist/utils/discover-plugin-features.js +96 -0
  97. package/package.json +20 -4
@@ -0,0 +1,74 @@
1
+ const SUMMARY_MAX_LENGTH = 200;
2
+ const cut = (text) => text.length > SUMMARY_MAX_LENGTH
3
+ ? `${text.slice(0, SUMMARY_MAX_LENGTH - 1)}…`
4
+ : text;
5
+ const isPrimitive = (value) => typeof value === "string" ||
6
+ typeof value === "number" ||
7
+ typeof value === "boolean";
8
+ /**
9
+ * One line for a finished run's result: a string result, else its `summary`
10
+ * or `message`, else its primitive top-level fields (`key: value, …`).
11
+ * `null` when nothing readable is there.
12
+ */
13
+ export const summarizeActionResult = (result) => {
14
+ if (result === null || result === undefined) {
15
+ return null;
16
+ }
17
+ if (isPrimitive(result)) {
18
+ return cut(String(result));
19
+ }
20
+ if (typeof result !== "object" || Array.isArray(result)) {
21
+ return null;
22
+ }
23
+ const record = result;
24
+ for (const key of ["summary", "message"]) {
25
+ const value = record[key];
26
+ if (typeof value === "string" && value.trim()) {
27
+ return cut(value.trim());
28
+ }
29
+ }
30
+ const parts = Object.entries(record)
31
+ .filter((entry) => isPrimitive(entry[1]))
32
+ .map(([key, value]) => `${key}: ${String(value)}`);
33
+ return parts.length > 0 ? cut(parts.join(", ")) : null;
34
+ };
35
+ /** Share done in percent (0–100); `null` when the total is unknown. */
36
+ export const progressPercent = (progress) => {
37
+ if (!progress || progress.total === null || progress.total <= 0) {
38
+ return null;
39
+ }
40
+ return Math.max(0, Math.min(100, Math.round((progress.current / progress.total) * 100)));
41
+ };
42
+ /** "3 / 10" or "3" (unknown total); `null` without progress. */
43
+ export const progressCount = (progress) => {
44
+ if (!progress) {
45
+ return null;
46
+ }
47
+ return progress.total === null
48
+ ? String(progress.current)
49
+ : `${progress.current} / ${progress.total}`;
50
+ };
51
+ /** Short German date and time. */
52
+ export const formatRunTime = (iso) => {
53
+ if (!iso) {
54
+ return "";
55
+ }
56
+ const date = new Date(iso);
57
+ return Number.isNaN(date.getTime())
58
+ ? iso
59
+ : date.toLocaleString("de-AT", { dateStyle: "short", timeStyle: "short" });
60
+ };
61
+ /**
62
+ * Whether the latest run just finished while this page watched it: the same
63
+ * run was `running` before and is `completed`/`failed` now.
64
+ */
65
+ export const runTransition = (previous, current) => {
66
+ if (!previous ||
67
+ !current ||
68
+ previous.id !== current.id ||
69
+ previous.status !== "running" ||
70
+ current.status === "running") {
71
+ return null;
72
+ }
73
+ return current.status;
74
+ };
@@ -0,0 +1,363 @@
1
+ import type { UpdatePluginFeaturesPayload, UpdatePluginSettingsPayload } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
2
+ export declare const listPluginFeatures: () => Promise<{
3
+ plugins: {
4
+ plugin_key: string;
5
+ label: string;
6
+ description: string | null;
7
+ default_enabled: boolean;
8
+ switchable: boolean;
9
+ enabled: boolean;
10
+ version: number;
11
+ features: {
12
+ key: string;
13
+ label: string;
14
+ description: string | null;
15
+ parent_key: string | null;
16
+ default_enabled: boolean;
17
+ enabled: boolean;
18
+ effective_enabled: boolean;
19
+ }[];
20
+ settings: ({
21
+ type: "secret";
22
+ key: string;
23
+ label: string;
24
+ description: string | null;
25
+ required: boolean;
26
+ is_set: boolean;
27
+ source: "default" | "stored" | "config" | "none";
28
+ invalidates: string[];
29
+ } | {
30
+ type: "select";
31
+ options: {
32
+ value: string;
33
+ label: string;
34
+ }[];
35
+ default: string | null;
36
+ value: string | null;
37
+ key: string;
38
+ label: string;
39
+ description: string | null;
40
+ required: boolean;
41
+ is_set: boolean;
42
+ source: "default" | "stored" | "config" | "none";
43
+ invalidates: string[];
44
+ } | {
45
+ type: "number";
46
+ min: number | null;
47
+ max: number | null;
48
+ default: number | null;
49
+ value: number | null;
50
+ key: string;
51
+ label: string;
52
+ description: string | null;
53
+ required: boolean;
54
+ is_set: boolean;
55
+ source: "default" | "stored" | "config" | "none";
56
+ invalidates: string[];
57
+ } | {
58
+ type: "string" | "boolean" | "url";
59
+ default: string | number | boolean | null;
60
+ value: string | number | boolean | null;
61
+ key: string;
62
+ label: string;
63
+ description: string | null;
64
+ required: boolean;
65
+ is_set: boolean;
66
+ source: "default" | "stored" | "config" | "none";
67
+ invalidates: string[];
68
+ })[];
69
+ configured: boolean;
70
+ missing: string[];
71
+ settings_version: number;
72
+ actions: {
73
+ key: string;
74
+ label: string;
75
+ description: string | null;
76
+ mode: "sync" | "background";
77
+ confirm: string | null;
78
+ }[];
79
+ admin_page: string | null;
80
+ }[];
81
+ }>;
82
+ export declare const retrievePluginFeatures: (pluginKey: string) => Promise<{
83
+ plugin: {
84
+ plugin_key: string;
85
+ label: string;
86
+ description: string | null;
87
+ default_enabled: boolean;
88
+ switchable: boolean;
89
+ enabled: boolean;
90
+ version: number;
91
+ features: {
92
+ key: string;
93
+ label: string;
94
+ description: string | null;
95
+ parent_key: string | null;
96
+ default_enabled: boolean;
97
+ enabled: boolean;
98
+ effective_enabled: boolean;
99
+ }[];
100
+ settings: ({
101
+ type: "secret";
102
+ key: string;
103
+ label: string;
104
+ description: string | null;
105
+ required: boolean;
106
+ is_set: boolean;
107
+ source: "default" | "stored" | "config" | "none";
108
+ invalidates: string[];
109
+ } | {
110
+ type: "select";
111
+ options: {
112
+ value: string;
113
+ label: string;
114
+ }[];
115
+ default: string | null;
116
+ value: string | null;
117
+ key: string;
118
+ label: string;
119
+ description: string | null;
120
+ required: boolean;
121
+ is_set: boolean;
122
+ source: "default" | "stored" | "config" | "none";
123
+ invalidates: string[];
124
+ } | {
125
+ type: "number";
126
+ min: number | null;
127
+ max: number | null;
128
+ default: number | null;
129
+ value: number | null;
130
+ key: string;
131
+ label: string;
132
+ description: string | null;
133
+ required: boolean;
134
+ is_set: boolean;
135
+ source: "default" | "stored" | "config" | "none";
136
+ invalidates: string[];
137
+ } | {
138
+ type: "string" | "boolean" | "url";
139
+ default: string | number | boolean | null;
140
+ value: string | number | boolean | null;
141
+ key: string;
142
+ label: string;
143
+ description: string | null;
144
+ required: boolean;
145
+ is_set: boolean;
146
+ source: "default" | "stored" | "config" | "none";
147
+ invalidates: string[];
148
+ })[];
149
+ configured: boolean;
150
+ missing: string[];
151
+ settings_version: number;
152
+ actions: {
153
+ key: string;
154
+ label: string;
155
+ description: string | null;
156
+ mode: "sync" | "background";
157
+ confirm: string | null;
158
+ }[];
159
+ admin_page: string | null;
160
+ };
161
+ }>;
162
+ export declare const updatePluginFeatures: (payload: UpdatePluginFeaturesPayload) => Promise<{
163
+ plugin: {
164
+ plugin_key: string;
165
+ label: string;
166
+ description: string | null;
167
+ default_enabled: boolean;
168
+ switchable: boolean;
169
+ enabled: boolean;
170
+ version: number;
171
+ features: {
172
+ key: string;
173
+ label: string;
174
+ description: string | null;
175
+ parent_key: string | null;
176
+ default_enabled: boolean;
177
+ enabled: boolean;
178
+ effective_enabled: boolean;
179
+ }[];
180
+ settings: ({
181
+ type: "secret";
182
+ key: string;
183
+ label: string;
184
+ description: string | null;
185
+ required: boolean;
186
+ is_set: boolean;
187
+ source: "default" | "stored" | "config" | "none";
188
+ invalidates: string[];
189
+ } | {
190
+ type: "select";
191
+ options: {
192
+ value: string;
193
+ label: string;
194
+ }[];
195
+ default: string | null;
196
+ value: string | null;
197
+ key: string;
198
+ label: string;
199
+ description: string | null;
200
+ required: boolean;
201
+ is_set: boolean;
202
+ source: "default" | "stored" | "config" | "none";
203
+ invalidates: string[];
204
+ } | {
205
+ type: "number";
206
+ min: number | null;
207
+ max: number | null;
208
+ default: number | null;
209
+ value: number | null;
210
+ key: string;
211
+ label: string;
212
+ description: string | null;
213
+ required: boolean;
214
+ is_set: boolean;
215
+ source: "default" | "stored" | "config" | "none";
216
+ invalidates: string[];
217
+ } | {
218
+ type: "string" | "boolean" | "url";
219
+ default: string | number | boolean | null;
220
+ value: string | number | boolean | null;
221
+ key: string;
222
+ label: string;
223
+ description: string | null;
224
+ required: boolean;
225
+ is_set: boolean;
226
+ source: "default" | "stored" | "config" | "none";
227
+ invalidates: string[];
228
+ })[];
229
+ configured: boolean;
230
+ missing: string[];
231
+ settings_version: number;
232
+ actions: {
233
+ key: string;
234
+ label: string;
235
+ description: string | null;
236
+ mode: "sync" | "background";
237
+ confirm: string | null;
238
+ }[];
239
+ admin_page: string | null;
240
+ };
241
+ }>;
242
+ export declare const updatePluginSettings: (pluginKey: string, payload: UpdatePluginSettingsPayload) => Promise<{
243
+ plugin: {
244
+ plugin_key: string;
245
+ label: string;
246
+ description: string | null;
247
+ default_enabled: boolean;
248
+ switchable: boolean;
249
+ enabled: boolean;
250
+ version: number;
251
+ features: {
252
+ key: string;
253
+ label: string;
254
+ description: string | null;
255
+ parent_key: string | null;
256
+ default_enabled: boolean;
257
+ enabled: boolean;
258
+ effective_enabled: boolean;
259
+ }[];
260
+ settings: ({
261
+ type: "secret";
262
+ key: string;
263
+ label: string;
264
+ description: string | null;
265
+ required: boolean;
266
+ is_set: boolean;
267
+ source: "default" | "stored" | "config" | "none";
268
+ invalidates: string[];
269
+ } | {
270
+ type: "select";
271
+ options: {
272
+ value: string;
273
+ label: string;
274
+ }[];
275
+ default: string | null;
276
+ value: string | null;
277
+ key: string;
278
+ label: string;
279
+ description: string | null;
280
+ required: boolean;
281
+ is_set: boolean;
282
+ source: "default" | "stored" | "config" | "none";
283
+ invalidates: string[];
284
+ } | {
285
+ type: "number";
286
+ min: number | null;
287
+ max: number | null;
288
+ default: number | null;
289
+ value: number | null;
290
+ key: string;
291
+ label: string;
292
+ description: string | null;
293
+ required: boolean;
294
+ is_set: boolean;
295
+ source: "default" | "stored" | "config" | "none";
296
+ invalidates: string[];
297
+ } | {
298
+ type: "string" | "boolean" | "url";
299
+ default: string | number | boolean | null;
300
+ value: string | number | boolean | null;
301
+ key: string;
302
+ label: string;
303
+ description: string | null;
304
+ required: boolean;
305
+ is_set: boolean;
306
+ source: "default" | "stored" | "config" | "none";
307
+ invalidates: string[];
308
+ })[];
309
+ configured: boolean;
310
+ missing: string[];
311
+ settings_version: number;
312
+ actions: {
313
+ key: string;
314
+ label: string;
315
+ description: string | null;
316
+ mode: "sync" | "background";
317
+ confirm: string | null;
318
+ }[];
319
+ admin_page: string | null;
320
+ };
321
+ }>;
322
+ export declare const runPluginAction: (pluginKey: string, actionKey: string) => Promise<{
323
+ action: {
324
+ key: string;
325
+ status: "completed" | "started";
326
+ run: {
327
+ id: string;
328
+ plugin_key: string;
329
+ action_key: string;
330
+ status: "failed" | "completed" | "running";
331
+ started_at: string;
332
+ finished_at: string | null;
333
+ triggered_by: string | null;
334
+ message: string | null;
335
+ progress: {
336
+ current: number;
337
+ total: number | null;
338
+ label: string | null;
339
+ } | null;
340
+ result: unknown;
341
+ };
342
+ result?: unknown;
343
+ };
344
+ }>;
345
+ /** Latest runs of one action, newest first. */
346
+ export declare const listPluginActionRuns: (pluginKey: string, actionKey: string, limit?: number) => Promise<{
347
+ runs: {
348
+ id: string;
349
+ plugin_key: string;
350
+ action_key: string;
351
+ status: "failed" | "completed" | "running";
352
+ started_at: string;
353
+ finished_at: string | null;
354
+ triggered_by: string | null;
355
+ message: string | null;
356
+ progress: {
357
+ current: number;
358
+ total: number | null;
359
+ label: string | null;
360
+ } | null;
361
+ result: unknown;
362
+ }[];
363
+ }>;
@@ -0,0 +1,9 @@
1
+ import { pluginFeaturesAdminPaths } from "@hartl-services/medusa-food-supplements-contracts/paths/plugin-features";
2
+ import { sdk } from "../../../lib/sdk";
3
+ export const listPluginFeatures = () => sdk.client.fetch(pluginFeaturesAdminPaths.get.list, { method: "GET" });
4
+ export const retrievePluginFeatures = (pluginKey) => sdk.client.fetch(pluginFeaturesAdminPaths.get.retrieve(pluginKey), { method: "GET" });
5
+ export const updatePluginFeatures = (payload) => sdk.client.fetch(pluginFeaturesAdminPaths.post.update, { method: "POST", body: payload });
6
+ export const updatePluginSettings = (pluginKey, payload) => sdk.client.fetch(pluginFeaturesAdminPaths.post.updateSettings(pluginKey), { method: "POST", body: payload });
7
+ export const runPluginAction = (pluginKey, actionKey) => sdk.client.fetch(pluginFeaturesAdminPaths.post.runAction(pluginKey, actionKey), { method: "POST", body: {} });
8
+ /** Latest runs of one action, newest first. */
9
+ export const listPluginActionRuns = (pluginKey, actionKey, limit = 1) => sdk.client.fetch(pluginFeaturesAdminPaths.get.actionRuns(pluginKey, actionKey), { method: "GET", query: { limit } });
@@ -0,0 +1,4 @@
1
+ /** Admin path of the hub; plugin pages live below it. */
2
+ export declare const PLUGIN_HUB_PATH = "/settings/hartl-services";
3
+ /** The base's page for one plugin (static `plugin` segment, see README). */
4
+ export declare const pluginDetailPath: (pluginKey: string) => string;
@@ -0,0 +1,4 @@
1
+ /** Admin path of the hub; plugin pages live below it. */
2
+ export const PLUGIN_HUB_PATH = "/settings/hartl-services";
3
+ /** The base's page for one plugin (static `plugin` segment, see README). */
4
+ export const pluginDetailPath = (pluginKey) => `${PLUGIN_HUB_PATH}/plugin/${encodeURIComponent(pluginKey)}`;
@@ -0,0 +1,28 @@
1
+ import type { AdminPluginSetting, UpdatePluginSettingsPayload } from "@hartl-services/medusa-food-supplements-contracts/types/plugin-features-admin";
2
+ /**
3
+ * Form state per setting: text for string/url/number inputs and the chosen
4
+ * option of a select (`""` = none), a boolean for
5
+ * switches, text for a new secret (`""` = keep) or `null` = clear the
6
+ * stored secret on save.
7
+ */
8
+ export type SettingsDraft = Record<string, string | boolean | null>;
9
+ type SettingValues = UpdatePluginSettingsPayload["values"];
10
+ export declare const initialSettingsDraft: (settings: readonly AdminPluginSetting[]) => SettingsDraft;
11
+ /**
12
+ * Keys of number fields whose input the browser could not parse (e.g. "1e"):
13
+ * the input then reports `""`, which must not be read as "cleared".
14
+ */
15
+ export type UnparsableKeys = readonly string[];
16
+ /**
17
+ * Keys whose draft value cannot be sent: unparsable, not a number or
18
+ * outside min/max. Saving is disabled while any key is listed.
19
+ */
20
+ export declare const invalidDraftKeys: (settings: readonly AdminPluginSetting[], draft: SettingsDraft, unparsable?: UnparsableKeys) => string[];
21
+ /**
22
+ * Only what the user changed: an emptied field clears a STORED value
23
+ * (`null`); emptying a value that only comes from the configuration or the
24
+ * default sends nothing. A secret is sent only when typed or cleared. An
25
+ * unparsable number is no change (the form blocks saving meanwhile).
26
+ */
27
+ export declare const changedSettingValues: (settings: readonly AdminPluginSetting[], draft: SettingsDraft, unparsable?: UnparsableKeys) => SettingValues;
28
+ export {};
@@ -0,0 +1,86 @@
1
+ const initialEntry = (setting) => {
2
+ if (setting.type === "secret") {
3
+ return "";
4
+ }
5
+ if (setting.type === "boolean") {
6
+ return setting.value === true;
7
+ }
8
+ return setting.value === null ? "" : String(setting.value);
9
+ };
10
+ export const initialSettingsDraft = (settings) => Object.fromEntries(settings.map((setting) => [setting.key, initialEntry(setting)]));
11
+ const outsideBounds = (setting, value) => (setting.min !== null && value < setting.min) ||
12
+ (setting.max !== null && value > setting.max);
13
+ const numberEntryValue = (entry) => {
14
+ const value = Number(entry.trim());
15
+ return Number.isFinite(value) ? value : null;
16
+ };
17
+ /**
18
+ * Keys whose draft value cannot be sent: unparsable, not a number or
19
+ * outside min/max. Saving is disabled while any key is listed.
20
+ */
21
+ export const invalidDraftKeys = (settings, draft, unparsable = []) => settings
22
+ .filter((setting) => {
23
+ if (setting.type !== "number") {
24
+ return false;
25
+ }
26
+ if (unparsable.includes(setting.key)) {
27
+ return true;
28
+ }
29
+ const entry = draft[setting.key];
30
+ if (typeof entry !== "string" || entry.trim() === "") {
31
+ return false;
32
+ }
33
+ const value = numberEntryValue(entry);
34
+ return value === null || outsideBounds(setting, value);
35
+ })
36
+ .map((setting) => setting.key);
37
+ /**
38
+ * Only what the user changed: an emptied field clears a STORED value
39
+ * (`null`); emptying a value that only comes from the configuration or the
40
+ * default sends nothing. A secret is sent only when typed or cleared. An
41
+ * unparsable number is no change (the form blocks saving meanwhile).
42
+ */
43
+ export const changedSettingValues = (settings, draft, unparsable = []) => {
44
+ const initial = initialSettingsDraft(settings);
45
+ const values = {};
46
+ for (const setting of settings) {
47
+ const entry = draft[setting.key];
48
+ if (entry === undefined || entry === initial[setting.key]) {
49
+ continue;
50
+ }
51
+ if (setting.type === "secret") {
52
+ if (entry === null) {
53
+ if (setting.source === "stored") {
54
+ values[setting.key] = null;
55
+ }
56
+ }
57
+ else if (typeof entry === "string" && entry !== "") {
58
+ values[setting.key] = entry;
59
+ }
60
+ continue;
61
+ }
62
+ if (setting.type === "boolean") {
63
+ values[setting.key] = entry === true;
64
+ continue;
65
+ }
66
+ if (unparsable.includes(setting.key)) {
67
+ continue;
68
+ }
69
+ const text = typeof entry === "string" ? entry.trim() : "";
70
+ if (text === "") {
71
+ if (setting.source === "stored") {
72
+ values[setting.key] = null;
73
+ }
74
+ }
75
+ else if (setting.type === "number") {
76
+ const value = numberEntryValue(text);
77
+ if (value !== null) {
78
+ values[setting.key] = value;
79
+ }
80
+ }
81
+ else {
82
+ values[setting.key] = text;
83
+ }
84
+ }
85
+ return values;
86
+ };
@@ -0,0 +1,7 @@
1
+ export declare const pluginFeatureKeys: {
2
+ all: readonly ["plugin-features"];
3
+ list: () => readonly ["plugin-features", "list"];
4
+ details: () => readonly ["plugin-features", "detail"];
5
+ detail: (pluginKey: string) => readonly ["plugin-features", "detail", string];
6
+ actionRuns: (pluginKey: string, actionKey: string) => readonly ["plugin-features", "action-runs", string, string];
7
+ };
@@ -0,0 +1,7 @@
1
+ export const pluginFeatureKeys = {
2
+ all: ["plugin-features"],
3
+ list: () => [...pluginFeatureKeys.all, "list"],
4
+ details: () => [...pluginFeatureKeys.all, "detail"],
5
+ detail: (pluginKey) => [...pluginFeatureKeys.details(), pluginKey],
6
+ actionRuns: (pluginKey, actionKey) => [...pluginFeatureKeys.all, "action-runs", pluginKey, actionKey],
7
+ };
@@ -0,0 +1,90 @@
1
+ export declare const texts: {
2
+ menu: {
3
+ label: string;
4
+ };
5
+ page: {
6
+ title: string;
7
+ description: string;
8
+ };
9
+ states: {
10
+ loading: string;
11
+ error: string;
12
+ empty: string;
13
+ retry: string;
14
+ notFound: string;
15
+ };
16
+ plugin: {
17
+ switchLabel: (label: string) => string;
18
+ open: string;
19
+ openLabel: (label: string) => string;
20
+ };
21
+ chips: {
22
+ enabled: string;
23
+ disabled: string;
24
+ configured: string;
25
+ notConfigured: (labels: string) => string;
26
+ };
27
+ detail: {
28
+ back: string;
29
+ };
30
+ sections: {
31
+ features: string;
32
+ settings: string;
33
+ actions: string;
34
+ };
35
+ hints: {
36
+ pluginDisabled: string;
37
+ parentDisabled: (parentLabel: string) => string;
38
+ saving: string;
39
+ };
40
+ gate: {
41
+ disabled: string;
42
+ disabledNamed: (label: string) => string;
43
+ openHub: string;
44
+ };
45
+ settings: {
46
+ missing: (labels: string) => string;
47
+ save: string;
48
+ secretPlaceholder: string;
49
+ secretClear: string;
50
+ secretClearPending: string;
51
+ secretClearUndo: string;
52
+ secretClearLabel: (label: string) => string;
53
+ secretClearUndoLabel: (label: string) => string;
54
+ invalidates: (labels: string) => string;
55
+ required: string;
56
+ invalidNumber: string;
57
+ outOfRange: string;
58
+ numberMin: (min: number) => string;
59
+ numberMax: (max: number) => string;
60
+ numberRange: (min: number, max: number) => string;
61
+ selectPlaceholder: string;
62
+ clearHint: string;
63
+ source: {
64
+ stored: string;
65
+ config: string;
66
+ default: string;
67
+ none: string;
68
+ };
69
+ };
70
+ actions: {
71
+ confirmTitle: (label: string) => string;
72
+ confirm: string;
73
+ cancel: string;
74
+ runningSince: (time: string) => string;
75
+ lastCompleted: (time: string) => string;
76
+ lastFailed: (time: string) => string;
77
+ };
78
+ toasts: {
79
+ saved: string;
80
+ saveFailed: string;
81
+ conflict: string;
82
+ actionCompleted: (label: string) => string;
83
+ actionStarted: (label: string) => string;
84
+ actionFailed: (label: string) => string;
85
+ actionAlreadyRunning: (label: string) => string;
86
+ };
87
+ errors: {
88
+ unknown: string;
89
+ };
90
+ };