@eifi1/ui-kit 0.38.1 → 0.39.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/README.md +6 -5
- package/dist/auth/auth-errors.d.ts +8 -3
- package/dist/auth/auth-errors.js +3 -1
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +25 -2
- package/dist/billing/billing-labels.js +11 -2
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/billing-overview.d.ts +50 -1
- package/dist/billing/checkout-processing.d.ts +15 -3
- package/dist/billing/checkout-processing.js +3 -1
- package/dist/billing/checkout-processing.js.map +1 -1
- package/dist/billing/pay-page.d.ts +55 -18
- package/dist/billing/pay-page.js +44 -10
- package/dist/billing/pay-page.js.map +1 -1
- package/dist/billing/plan-switch.d.ts +79 -0
- package/dist/billing/plan-switch.js +93 -0
- package/dist/billing/plan-switch.js.map +1 -0
- package/dist/bin/eifi1-pay-page.mjs +109 -21
- package/dist/eslint/index.js +1 -1
- package/dist/i18n/german.js +13 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/locales/en.js +14 -3
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/es.js +13 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +13 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +13 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +13 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +13 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +3 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/translation-review.d.ts +11 -0
- package/dist/lib/translation-review.js.map +1 -1
- package/dist/pay/pay.js +102 -40
- package/package.json +1 -1
- package/src/auth/auth-errors.ts +12 -3
- package/src/billing/billing-labels.ts +38 -3
- package/src/billing/billing-overview.ts +53 -1
- package/src/billing/checkout-processing.ts +17 -3
- package/src/billing/pay-page.ts +111 -27
- package/src/billing/plan-switch.tsx +201 -0
- package/src/i18n/german.ts +13 -1
- package/src/i18n/locales/es.ts +12 -1
- package/src/i18n/locales/fr.ts +12 -2
- package/src/i18n/locales/hu.ts +12 -1
- package/src/i18n/locales/it.ts +12 -1
- package/src/i18n/locales/zh.ts +12 -1
- package/src/i18n/review.ts +3 -0
- package/src/index.ts +8 -0
- package/src/lib/translation-review.ts +12 -0
|
@@ -5,14 +5,23 @@ import process from "process";
|
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
|
|
7
7
|
// pay-page/cli.ts
|
|
8
|
-
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
8
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
9
9
|
import { dirname, join } from "path";
|
|
10
10
|
import { parseArgs } from "util";
|
|
11
11
|
|
|
12
12
|
// src/billing/pay-page.ts
|
|
13
13
|
var PAY_CONFIG_FILE = "pay-config.json";
|
|
14
|
+
var APP_TAG = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
15
|
+
function appAt(name, site, returnPath) {
|
|
16
|
+
return { name, returnUrl: `${site}${returnPath}`, termsUrl: `${site}/terms`, privacyUrl: `${site}/privacy` };
|
|
17
|
+
}
|
|
18
|
+
var PAY_PAGE_APPS = Object.freeze({
|
|
19
|
+
keksdose: Object.freeze(appAt("Keksdose", "https://keksdose.app", "/settings/subscription")),
|
|
20
|
+
kastlan: Object.freeze(appAt("kastlan", "https://kastlan.app", "/admin/billing")),
|
|
21
|
+
kurvenschmiede: Object.freeze(appAt("Kurvenschmiede", "https://kurvenschmiede.app", "/settings/subscription"))
|
|
22
|
+
});
|
|
14
23
|
var TOKEN_PREFIX = { sandbox: "test_", live: "live_" };
|
|
15
|
-
function
|
|
24
|
+
function isPayPageUrl(value) {
|
|
16
25
|
if (typeof value !== "string") return false;
|
|
17
26
|
let url;
|
|
18
27
|
try {
|
|
@@ -23,6 +32,19 @@ function isPageUrl(value) {
|
|
|
23
32
|
if (url.protocol === "https:") return true;
|
|
24
33
|
return url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
25
34
|
}
|
|
35
|
+
function hasApp(apps, tag) {
|
|
36
|
+
return typeof tag === "string" && APP_TAG.test(tag) && typeof apps === "object" && apps !== null && Object.prototype.hasOwnProperty.call(apps, tag);
|
|
37
|
+
}
|
|
38
|
+
function appProblems(tag, app) {
|
|
39
|
+
if (typeof app !== "object" || app === null) return [`apps.${tag} is not an object`];
|
|
40
|
+
const a = app;
|
|
41
|
+
const problems = [];
|
|
42
|
+
if (typeof a.name !== "string" || a.name.trim() === "") problems.push(`apps.${tag}.name is missing`);
|
|
43
|
+
for (const key of ["returnUrl", "termsUrl", "privacyUrl"]) {
|
|
44
|
+
if (!isPayPageUrl(a[key])) problems.push(`apps.${tag}.${key} must be an https:// URL (http:// for localhost only)`);
|
|
45
|
+
}
|
|
46
|
+
return problems;
|
|
47
|
+
}
|
|
26
48
|
function payPageConfigProblems(config) {
|
|
27
49
|
if (typeof config !== "object" || config === null) return ["the configuration is not an object"];
|
|
28
50
|
const c = config;
|
|
@@ -43,10 +65,20 @@ function payPageConfigProblems(config) {
|
|
|
43
65
|
);
|
|
44
66
|
}
|
|
45
67
|
}
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
const { apps, app } = c;
|
|
69
|
+
if (typeof apps !== "object" || apps === null || Array.isArray(apps) || Object.keys(apps).length === 0) {
|
|
70
|
+
problems.push("apps must map each app's tag to its name and URLs");
|
|
71
|
+
} else {
|
|
72
|
+
for (const [tag, entry] of Object.entries(apps)) {
|
|
73
|
+
if (!APP_TAG.test(tag)) problems.push(`apps: ${JSON.stringify(tag)} is not an app tag (a-z, 0-9, _ and -)`);
|
|
74
|
+
else problems.push(...appProblems(tag, entry));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (typeof app !== "string" || app === "") problems.push("app is missing: the host's own app tag");
|
|
78
|
+
else if (!hasApp(apps, app)) {
|
|
79
|
+
const known = typeof apps === "object" && apps !== null ? Object.keys(apps).join(", ") : "";
|
|
80
|
+
problems.push(`app ${JSON.stringify(app)} is not in apps${known ? ` (${known})` : ""}`);
|
|
48
81
|
}
|
|
49
|
-
if (typeof c.appName !== "string" || c.appName.trim() === "") problems.push("appName is missing");
|
|
50
82
|
return problems;
|
|
51
83
|
}
|
|
52
84
|
|
|
@@ -69,16 +101,60 @@ var given = (value) => {
|
|
|
69
101
|
const trimmed = (value ?? "").trim();
|
|
70
102
|
return trimmed === "" ? null : trimmed;
|
|
71
103
|
};
|
|
104
|
+
function moved(url, site) {
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = new URL(url);
|
|
108
|
+
} catch {
|
|
109
|
+
return url;
|
|
110
|
+
}
|
|
111
|
+
return `${site.replace(/\/+$/, "")}${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
112
|
+
}
|
|
113
|
+
function mergedApps(over) {
|
|
114
|
+
const apps = {};
|
|
115
|
+
for (const [tag, app] of Object.entries(PAY_PAGE_APPS)) apps[tag] = { ...app };
|
|
116
|
+
for (const [tag, app] of Object.entries(over ?? {})) {
|
|
117
|
+
const base = Object.prototype.hasOwnProperty.call(apps, tag) ? apps[tag] : {};
|
|
118
|
+
Object.defineProperty(apps, tag, { value: { ...base, ...app }, enumerable: true, writable: true, configurable: true });
|
|
119
|
+
}
|
|
120
|
+
return apps;
|
|
121
|
+
}
|
|
72
122
|
function payPageConfig(options) {
|
|
123
|
+
const app = (options.app ?? "").trim();
|
|
124
|
+
const apps = mergedApps(options.apps);
|
|
125
|
+
const problems = [];
|
|
126
|
+
const site = given(options.appUrl);
|
|
127
|
+
if (site !== null) {
|
|
128
|
+
if (!isPayPageUrl(site)) problems.push("--app-url must be an https:// URL (http:// for localhost only)");
|
|
129
|
+
else if (Object.prototype.hasOwnProperty.call(apps, app)) {
|
|
130
|
+
const own = apps[app];
|
|
131
|
+
apps[app] = {
|
|
132
|
+
...own,
|
|
133
|
+
returnUrl: moved(own.returnUrl, site),
|
|
134
|
+
termsUrl: moved(own.termsUrl, site),
|
|
135
|
+
privacyUrl: moved(own.privacyUrl, site)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
73
139
|
const config = {
|
|
74
140
|
token: given(options.token),
|
|
75
141
|
environment: given(options.environment),
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
termsUrl: (options.termsUrl ?? "").trim(),
|
|
79
|
-
privacyUrl: (options.privacyUrl ?? "").trim()
|
|
142
|
+
app,
|
|
143
|
+
apps
|
|
80
144
|
};
|
|
81
|
-
return { config, problems: payPageConfigProblems(config) };
|
|
145
|
+
return { config, problems: [...problems, ...payPageConfigProblems(config)] };
|
|
146
|
+
}
|
|
147
|
+
function readAppsFile(path) {
|
|
148
|
+
let parsed;
|
|
149
|
+
try {
|
|
150
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
151
|
+
} catch (err) {
|
|
152
|
+
throw new PayPageError([`--apps ${path} can't be read as JSON: ${err.message}`]);
|
|
153
|
+
}
|
|
154
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
155
|
+
throw new PayPageError([`--apps ${path} must hold a JSON object of apps by tag`]);
|
|
156
|
+
}
|
|
157
|
+
return parsed;
|
|
82
158
|
}
|
|
83
159
|
function writePayPage(options, bundleDir) {
|
|
84
160
|
const { config, problems } = payPageConfig(options);
|
|
@@ -99,10 +175,15 @@ function writePayPage(options, bundleDir) {
|
|
|
99
175
|
return config;
|
|
100
176
|
}
|
|
101
177
|
var USAGE = `Usage: eifi1-pay-page --out <dir> [--token <test_\u2026|live_\u2026> --environment <sandbox|live>]
|
|
102
|
-
--
|
|
178
|
+
--app <tag> [--app-url <https://\u2026>] [--apps <file>]
|
|
103
179
|
|
|
104
|
-
Copies @eifi1/ui-kit's pay page into <dir> and writes its ${PAY_CONFIG_FILE}
|
|
180
|
+
Copies @eifi1/ui-kit's pay page into <dir> and writes its ${PAY_CONFIG_FILE}: the
|
|
181
|
+
token, the host's app (--app: ${Object.keys(PAY_PAGE_APPS).join(", ")}) and the kit's
|
|
182
|
+
map of apps, which the page reads each transaction's app from. --app-url moves the
|
|
183
|
+
host's app to another site; --apps merges a JSON map of apps by tag over the kit's.
|
|
105
184
|
Without a token (billing off) the page says there is nothing to pay.`;
|
|
185
|
+
var OLD_FLAGS = ["return-url", "app-name", "terms-url", "privacy-url"];
|
|
186
|
+
var OLD_FLAGS_REFUSED = "eifi1-pay-page: --return-url, --app-name, --terms-url and --privacy-url are gone (ui-kit 0.39): every pay page serves every app, from the kit's map. Pass --app <tag> instead, with --app-url <https://\u2026> for another site (your PAY_APP_URL) and --apps <file> to merge your own map.";
|
|
106
187
|
function runPayPageCli(argv, io) {
|
|
107
188
|
let values;
|
|
108
189
|
try {
|
|
@@ -114,10 +195,11 @@ function runPayPageCli(argv, io) {
|
|
|
114
195
|
out: { type: "string" },
|
|
115
196
|
token: { type: "string" },
|
|
116
197
|
environment: { type: "string" },
|
|
117
|
-
|
|
118
|
-
"app-
|
|
119
|
-
|
|
120
|
-
|
|
198
|
+
app: { type: "string" },
|
|
199
|
+
"app-url": { type: "string" },
|
|
200
|
+
apps: { type: "string" },
|
|
201
|
+
// Parsed only to be refused with a line that names the new flags.
|
|
202
|
+
...Object.fromEntries(OLD_FLAGS.map((flag) => [flag, { type: "string" }])),
|
|
121
203
|
help: { type: "boolean", short: "h" }
|
|
122
204
|
}
|
|
123
205
|
}));
|
|
@@ -131,22 +213,28 @@ ${USAGE}`);
|
|
|
131
213
|
io.log(USAGE);
|
|
132
214
|
return 0;
|
|
133
215
|
}
|
|
216
|
+
if (OLD_FLAGS.some((flag) => values[flag] !== void 0)) {
|
|
217
|
+
io.error(`${OLD_FLAGS_REFUSED}
|
|
218
|
+
|
|
219
|
+
${USAGE}`);
|
|
220
|
+
return 2;
|
|
221
|
+
}
|
|
134
222
|
const text = (key) => typeof values[key] === "string" ? values[key] : "";
|
|
135
223
|
try {
|
|
224
|
+
const appsFile = given(text("apps"));
|
|
136
225
|
const config = writePayPage(
|
|
137
226
|
{
|
|
138
227
|
out: text("out"),
|
|
139
228
|
token: text("token"),
|
|
140
229
|
environment: text("environment"),
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
privacyUrl: text("privacy-url")
|
|
230
|
+
app: text("app"),
|
|
231
|
+
appUrl: text("app-url"),
|
|
232
|
+
apps: appsFile === null ? void 0 : readAppsFile(appsFile)
|
|
145
233
|
},
|
|
146
234
|
io.bundleDir
|
|
147
235
|
);
|
|
148
236
|
io.log(
|
|
149
|
-
`eifi1-pay-page: wrote
|
|
237
|
+
`eifi1-pay-page: wrote ${config.app}'s pay page (serving ${Object.keys(config.apps).join(", ")}) to ${text("out").trim()} ` + (config.token ? `(${config.environment})` : "(no token: billing off, the page says there is nothing to pay)")
|
|
150
238
|
);
|
|
151
239
|
return 0;
|
|
152
240
|
} catch (err) {
|
package/dist/eslint/index.js
CHANGED
|
@@ -47,7 +47,7 @@ const roleUtilities = {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
|
-
const VERSION = true ? "0.
|
|
50
|
+
const VERSION = true ? "0.39.0" : "0.0.0-source";
|
|
51
51
|
const recommended = {
|
|
52
52
|
name: "@eifi1/ui-kit/recommended",
|
|
53
53
|
// Not the tests, as check:tokens skips the kit's: a test names the old class to assert it
|
package/dist/i18n/german.js
CHANGED
|
@@ -1628,6 +1628,14 @@ Die vollst\xE4ndige Postanschrift wird betroffenen Personen und Aufsichtsbeh\xF6
|
|
|
1628
1628
|
payNothing: "Hier ist nichts zu bezahlen. Um einen Tarif zu w\xE4hlen, kehren Sie zur App zur\xFCck.",
|
|
1629
1629
|
payFailed: "Der Bezahlvorgang konnte nicht ge\xF6ffnet werden. Bitte kehren Sie zur\xFCck und versuchen Sie es gleich noch einmal.",
|
|
1630
1630
|
payBack: "Der Bezahlvorgang wurde geschlossen. Sie k\xF6nnen zur\xFCckkehren oder diese Seite neu laden, um ihn wieder zu \xF6ffnen.",
|
|
1631
|
+
payDone: "Zahlung abgeschlossen. Sie k\xF6nnen diese Seite schlie\xDFen.",
|
|
1632
|
+
// 0.39 (§15.2.10): PlanSwitchConfirm.
|
|
1633
|
+
switchTitle: "Tarif \xE4ndern",
|
|
1634
|
+
switchFromTo: (from, to) => `Von ${from} zu ${to}`,
|
|
1635
|
+
switchChargedNow: "Jetzt belastet",
|
|
1636
|
+
switchCredited: "Gutschrift auf die n\xE4chsten Belastungen",
|
|
1637
|
+
switchNextCharge: (date) => `N\xE4chste Belastung am ${date}`,
|
|
1638
|
+
switchConfirm: "Tarif \xE4ndern",
|
|
1631
1639
|
waitingChanges: (count) => count === 1 ? `${n(count)} \xC4nderung wartet auf einen Tarif` : `${n(count)} \xC4nderungen warten auf einen Tarif`,
|
|
1632
1640
|
// "Verwerfen", as `wizard.cancelConfirmLabel`.
|
|
1633
1641
|
discardWaiting: "Wartende \xC4nderungen verwerfen",
|
|
@@ -1635,8 +1643,11 @@ Die vollst\xE4ndige Postanschrift wird betroffenen Personen und Aufsichtsbeh\xF6
|
|
|
1635
1643
|
disabled: "Abonnements sind hier nicht verf\xFCgbar.",
|
|
1636
1644
|
// §14.2: server-kit 0.7's refusals. "Zahlung und Rechnungen", as `manage`.
|
|
1637
1645
|
providerUnavailable: "Der Zahlungsanbieter ist gerade nicht erreichbar. Bitte versuchen Sie es gleich noch einmal.",
|
|
1638
|
-
alreadySubscribed: "Sie haben bereits ein Abonnement.
|
|
1639
|
-
planNotSold: "Dieser Tarif ist f\xFCr diesen Abrechnungszeitraum oder diese W\xE4hrung nicht erh\xE4ltlich. Bitte w\xE4hlen Sie einen anderen."
|
|
1646
|
+
alreadySubscribed: "Sie haben bereits ein Abonnement. \xC4ndern Sie Ihren Tarif oben.",
|
|
1647
|
+
planNotSold: "Dieser Tarif ist f\xFCr diesen Abrechnungszeitraum oder diese W\xE4hrung nicht erh\xE4ltlich. Bitte w\xE4hlen Sie einen anderen.",
|
|
1648
|
+
// 0.39 (§15.2.10): the plan switch's refusals.
|
|
1649
|
+
notSubscribed: "Es l\xE4uft kein Abonnement, das sich \xE4ndern lie\xDFe. W\xE4hlen Sie einen Tarif, um eines abzuschlie\xDFen.",
|
|
1650
|
+
planUnchanged: "Das ist bereits Ihr Tarif. W\xE4hlen Sie einen anderen Tarif oder Abrechnungszeitraum."
|
|
1640
1651
|
},
|
|
1641
1652
|
// 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "Tarif" as
|
|
1642
1653
|
// in `billing`; keksdose's reviewed admin wording where it had one.
|