@checkcourt/sdk 0.6.0 → 0.8.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 +19 -0
- package/dist/extensions.d.ts +2 -0
- package/dist/generated/schema.d.ts +10 -5
- package/dist/generated/spec-hash.d.ts +1 -1
- package/dist/generated/spec-hash.js +1 -1
- package/dist/manifest.d.ts +11 -2
- package/dist/manifest.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -210,6 +210,7 @@ with the matching builder; each throws when the answer would break CheckCourt's
|
|
|
210
210
|
| `booking_plan.action` | `ext.date`, `ext.courts` (and the day as subject) | `ui.doc([...])`, shown in a dialog after a click; `ui.hidden()` closes it |
|
|
211
211
|
| `sidebar.action` | nothing extra | `ui.doc([...])`, shown in a dialog after a click; `ui.hidden()` closes it |
|
|
212
212
|
| `member.settings.section` | nothing extra | `ui.doc([...])`, a card on the member's own settings page |
|
|
213
|
+
| `nav.page` | nothing extra | `ui.doc([...])` (or an iframe), the content of the app's own page |
|
|
213
214
|
|
|
214
215
|
```ts
|
|
215
216
|
if (ext.kind === "render" && ext.point === "court.annotation" && ext.date && ext.courts) {
|
|
@@ -227,6 +228,24 @@ if (ext.kind === "render" && ext.point === "court.annotation" && ext.date && ext
|
|
|
227
228
|
manifest and may set an `icon` from `APP_ACTION_ICONS`. In every declarative document,
|
|
228
229
|
CheckCourt places content first, then the buttons, then the links of each level.
|
|
229
230
|
|
|
231
|
+
`nav.page` gives your app an entry in the main navigation and a full page of its own at
|
|
232
|
+
`/apps/<installation id>/<index>`. It needs a `label` (at most 24 characters, also the page
|
|
233
|
+
title) and an `icon` from `APP_ACTION_ICONS`, may be declarative or an iframe, and appears at
|
|
234
|
+
most once per manifest. A club installation's entry is shown to every member of the club, a
|
|
235
|
+
member installation's entry only to that member:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
defineManifest({
|
|
239
|
+
installTargets: ["tenant"],
|
|
240
|
+
extensions: [{ point: "nav.page", kind: "declarative", url: "https://app.example.de/ext/page", label: "Trainingsplan", icon: "calendar" }],
|
|
241
|
+
dataProcessing: { categories: ["Trainingsdaten"], purpose: "Zeigt den Trainingsplan", storageLocation: "EU", avvRequired: false },
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (ext.point === "nav.page") {
|
|
245
|
+
return Response.json(ui.doc([ui.heading("Diese Woche"), ui.text("Dienstag 18 Uhr: Jugendtraining")]));
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
230
249
|
### OAuth with PKCE (member apps)
|
|
231
250
|
|
|
232
251
|
```ts
|
package/dist/extensions.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ export interface PointCapabilities {
|
|
|
53
53
|
"booking.hint": Record<string, never>;
|
|
54
54
|
"booking_plan.action": BookingPlanCapabilities;
|
|
55
55
|
"sidebar.action": Record<string, never>;
|
|
56
|
+
"nav.page": Record<string, never>;
|
|
56
57
|
}
|
|
57
58
|
export interface PointSubject {
|
|
58
59
|
"app.settings": {
|
|
@@ -83,6 +84,7 @@ export interface PointSubject {
|
|
|
83
84
|
id: string;
|
|
84
85
|
};
|
|
85
86
|
"sidebar.action": null;
|
|
87
|
+
"nav.page": null;
|
|
86
88
|
}
|
|
87
89
|
interface ContextClaimsOf<P extends ExtensionPoint> {
|
|
88
90
|
iss: typeof CONTEXT_ISSUER;
|
|
@@ -242,11 +242,11 @@ export interface paths {
|
|
|
242
242
|
* @description **Required scope:** `members:invite`
|
|
243
243
|
*
|
|
244
244
|
* Adds a member to the club. Three flows, decided by the input:
|
|
245
|
-
* 1. **Email already has an account** (in no other club): the
|
|
245
|
+
* 1. **Email already has an account** (in no other club): the account owner is invited. The membership stays pending (`awaitingAcceptance: true`) until they accept after their next login; until then the club cannot change the account's name, email, phone or password, and deleting the member only withdraws the invitation. `sendEmail` controls the invitation mail.
|
|
246
246
|
* 2. **New email**: a new account is created and, unless `sendEmail=false`, a welcome mail with a claim link is sent.
|
|
247
247
|
* 3. **`noOwnEmail=true`**: for members without their own mailbox. Creates an account with a synthetic address, stores the given email as forwarding address and requires a `password` (min 8 chars) the member uses to log in.
|
|
248
248
|
*
|
|
249
|
-
* `roleIds` (optional) assigns roles besides the base role Mitglied
|
|
249
|
+
* `roleIds` (optional) assigns roles besides the base role Mitglied and requires `roles:manage`, which only personal keys can hold: the caller must outrank each role and hold all of its scopes (403). Unknown ids fail with 404 before anything is created.
|
|
250
250
|
*
|
|
251
251
|
* Fails with 422 when the club's member limit is reached, the member number is taken, the email already belongs to this club, or the data processing agreement (AVV) has not been signed yet.
|
|
252
252
|
*/
|
|
@@ -302,7 +302,7 @@ export interface paths {
|
|
|
302
302
|
* End a membership
|
|
303
303
|
* @description **Required scope:** `members:delete`
|
|
304
304
|
*
|
|
305
|
-
* Removes the member from the club with a full cascade: future bookings are cancelled (with email notification), open guest fees are resolved according to `orphanedFeeAction`, and if this was the user's only club the account is anonymized. Members who lead a team cannot be deleted until leadership is handed over, and the club's last administrator cannot be deleted (422). The response summarizes what happened.
|
|
305
|
+
* Removes the member from the club with a full cascade: future bookings are cancelled (with email notification), open guest fees are resolved according to `orphanedFeeAction`, and if this was the user's only club the account is anonymized (not for a pending invitation, which only withdraws the invitation). The caller must outrank the member (403): only administrators may delete members holding an equal or higher role; management keys act with their creator's rank but never on administrators. Members who lead a team cannot be deleted until leadership is handed over, and the club's last administrator cannot be deleted (422). The response summarizes what happened.
|
|
306
306
|
*/
|
|
307
307
|
delete: operations["deleteMember"];
|
|
308
308
|
options?: never;
|
|
@@ -311,7 +311,7 @@ export interface paths {
|
|
|
311
311
|
* Update a member
|
|
312
312
|
* @description **Required scope:** `members:write`
|
|
313
313
|
*
|
|
314
|
-
* `name` and `email` are required (send the current values to keep them); `phone` and `memberNumber` are optional. For forwarding-only accounts the email update changes the forwarding address, not the login address. If the user is also a member of another club, name/email/phone are left untouched (they are account-level) and only the member number changes. Sessions cannot edit their own account; management keys are exempt from that rule. Member numbers are unique per club (422 on collision). Roles are not part of this endpoint: use `PUT /members/{id}/roles`. Sending `role` fails with 400.
|
|
314
|
+
* `name` and `email` are required (send the current values to keep them); `phone` and `memberNumber` are optional. For forwarding-only accounts the email update changes the forwarding address, not the login address. If the user is also a member of another club, name/email/phone are left untouched (they are account-level) and only the member number changes. Sessions cannot edit their own account; management keys are exempt from that rule. Changing the email requires outranking the member (403): only administrators may change the email of members holding an equal or higher role; management keys act with their creator's rank but never on administrators. While the member's invitation is pending, changing name, email or phone fails with 422. Member numbers are unique per club (422 on collision). Roles are not part of this endpoint: use `PUT /members/{id}/roles`. Sending `role` fails with 400.
|
|
315
315
|
*/
|
|
316
316
|
patch: operations["updateMember"];
|
|
317
317
|
trace?: never;
|
|
@@ -3586,7 +3586,12 @@ export interface operations {
|
|
|
3586
3586
|
[name: string]: unknown;
|
|
3587
3587
|
};
|
|
3588
3588
|
content: {
|
|
3589
|
-
"application/json":
|
|
3589
|
+
"application/json": {
|
|
3590
|
+
/** @constant */
|
|
3591
|
+
success: true;
|
|
3592
|
+
/** @description True when an existing account was invited and its owner still has to accept */
|
|
3593
|
+
awaitingAcceptance: boolean;
|
|
3594
|
+
};
|
|
3590
3595
|
};
|
|
3591
3596
|
};
|
|
3592
3597
|
400: components["responses"]["ValidationError"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const OPENAPI_SPEC_SHA256 = "
|
|
1
|
+
export declare const OPENAPI_SPEC_SHA256 = "838f8beb1f933593ea8eea8ea14eb78b12e0e8d61109254f5e72bec10b57e58a";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/generate.mjs from the CheckCourt OpenAPI spec. Do not edit.
|
|
2
|
-
export const OPENAPI_SPEC_SHA256 = "
|
|
2
|
+
export const OPENAPI_SPEC_SHA256 = "838f8beb1f933593ea8eea8ea14eb78b12e0e8d61109254f5e72bec10b57e58a";
|
package/dist/manifest.d.ts
CHANGED
|
@@ -56,6 +56,10 @@ export declare const EXTENSION_POINTS: {
|
|
|
56
56
|
readonly targets: readonly ["tenant", "user"];
|
|
57
57
|
readonly kinds: readonly ["declarative"];
|
|
58
58
|
};
|
|
59
|
+
readonly "nav.page": {
|
|
60
|
+
readonly targets: readonly ["tenant", "user"];
|
|
61
|
+
readonly kinds: readonly ["declarative", "iframe"];
|
|
62
|
+
};
|
|
59
63
|
};
|
|
60
64
|
export type ExtensionPoint = keyof typeof EXTENSION_POINTS;
|
|
61
65
|
/** Scope the installation needs (in `tenantScopes` or `userScopes`) before the point hands it a subject id. */
|
|
@@ -72,13 +76,14 @@ export declare const EXTENSION_POINT_SCOPE: {
|
|
|
72
76
|
readonly "booking.hint": "bookings:read";
|
|
73
77
|
readonly "booking_plan.action": "courts:read";
|
|
74
78
|
readonly "sidebar.action": null;
|
|
79
|
+
readonly "nav.page": null;
|
|
75
80
|
};
|
|
76
81
|
/** Buttons CheckCourt draws from the manifest alone, before the app is ever called. */
|
|
77
82
|
export declare const STATIC_ACTION_POINTS: readonly ["booking_plan.action", "sidebar.action"];
|
|
78
83
|
export type StaticActionPoint = (typeof STATIC_ACTION_POINTS)[number];
|
|
79
84
|
/** Longest `label` of a static action. */
|
|
80
85
|
export declare const STATIC_ACTION_LABEL_MAX = 24;
|
|
81
|
-
/** lucide icon names a static action may use as `icon`. */
|
|
86
|
+
/** lucide icon names a static action or `nav.page` may use as `icon`. */
|
|
82
87
|
export declare const APP_ACTION_ICONS: readonly ["bell", "calendar", "calendar-check", "camera", "chart-column", "circle-help", "clipboard-list", "clock", "cloud-rain", "door-open", "file-text", "flag", "heart-pulse", "info", "key-round", "lightbulb", "link", "list-checks", "lock-open", "map-pin", "megaphone", "message-square", "receipt", "send", "sparkles", "star", "sun", "thermometer", "ticket", "triangle-alert", "trophy", "user-round", "users", "wallet", "wrench"];
|
|
83
88
|
export type AppActionIcon = (typeof APP_ACTION_ICONS)[number];
|
|
84
89
|
/** Every scope an app may request. Role, app, webhook, key, billing and AVV management are reserved for people. */
|
|
@@ -93,6 +98,10 @@ type ExtensionOf<P extends ExtensionPoint> = {
|
|
|
93
98
|
/** Button text, 1 to 24 characters (`STATIC_ACTION_LABEL_MAX`). */
|
|
94
99
|
label: string;
|
|
95
100
|
icon?: AppActionIcon;
|
|
101
|
+
} : P extends "nav.page" ? {
|
|
102
|
+
/** Navigation entry and page title, 1 to 24 characters (`STATIC_ACTION_LABEL_MAX`). */
|
|
103
|
+
label: string;
|
|
104
|
+
icon: AppActionIcon;
|
|
96
105
|
} : P extends "booking.action" ? {
|
|
97
106
|
label: string;
|
|
98
107
|
icon?: never;
|
|
@@ -105,7 +114,7 @@ type ExtensionOf<P extends ExtensionPoint> = {
|
|
|
105
114
|
});
|
|
106
115
|
/**
|
|
107
116
|
* One entry of `extensions`; `kind`, `label` and `icon` are checked per point at compile time.
|
|
108
|
-
* Every point may appear once, except `booking.action`.
|
|
117
|
+
* Every point may appear once, except `booking.action`; an app has at most one `nav.page`.
|
|
109
118
|
*/
|
|
110
119
|
export type ManifestExtension = {
|
|
111
120
|
[P in ExtensionPoint]: ExtensionOf<P>;
|
package/dist/manifest.js
CHANGED
|
@@ -16,6 +16,7 @@ export const EXTENSION_POINTS = {
|
|
|
16
16
|
"booking.hint": { targets: ["tenant"], kinds: ["declarative"] },
|
|
17
17
|
"booking_plan.action": { targets: ["tenant"], kinds: ["declarative"] },
|
|
18
18
|
"sidebar.action": { targets: ["tenant", "user"], kinds: ["declarative"] },
|
|
19
|
+
"nav.page": { targets: ["tenant", "user"], kinds: ["declarative", "iframe"] },
|
|
19
20
|
};
|
|
20
21
|
/** Scope the installation needs (in `tenantScopes` or `userScopes`) before the point hands it a subject id. */
|
|
21
22
|
export const EXTENSION_POINT_SCOPE = {
|
|
@@ -31,12 +32,13 @@ export const EXTENSION_POINT_SCOPE = {
|
|
|
31
32
|
"booking.hint": "bookings:read",
|
|
32
33
|
"booking_plan.action": "courts:read",
|
|
33
34
|
"sidebar.action": null,
|
|
35
|
+
"nav.page": null,
|
|
34
36
|
};
|
|
35
37
|
/** Buttons CheckCourt draws from the manifest alone, before the app is ever called. */
|
|
36
38
|
export const STATIC_ACTION_POINTS = ["booking_plan.action", "sidebar.action"];
|
|
37
39
|
/** Longest `label` of a static action. */
|
|
38
40
|
export const STATIC_ACTION_LABEL_MAX = 24;
|
|
39
|
-
/** lucide icon names a static action may use as `icon`. */
|
|
41
|
+
/** lucide icon names a static action or `nav.page` may use as `icon`. */
|
|
40
42
|
export const APP_ACTION_ICONS = [
|
|
41
43
|
"bell",
|
|
42
44
|
"calendar",
|