@checkcourt/sdk 0.7.0 → 0.9.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 +32 -0
- package/dist/extensions.d.ts +2 -0
- package/dist/manifest.d.ts +27 -5
- package/dist/manifest.js +6 -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
|
|
@@ -273,6 +292,19 @@ frame.navigate("/booking?date=2026-10-07");
|
|
|
273
292
|
|
|
274
293
|
Import the browser entry point `@checkcourt/sdk/iframe` only; it needs no secrets.
|
|
275
294
|
|
|
295
|
+
The frame starts at 240 pixels, a `nav.page` frame fills the page. Set `height` on the
|
|
296
|
+
manifest entry (whole pixels, `IFRAME_EXTENSION_HEIGHT_MIN` to `IFRAME_EXTENSION_HEIGHT_MAX`,
|
|
297
|
+
120 to 2000) to start at another height; `height` is for iframe extensions only.
|
|
298
|
+
Resize messages from `connectExtensionFrame` replace either value:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
{ point: "nav.page", kind: "iframe", url: "https://app.example.de/page", label: "Homepage", icon: "calendar", height: 900 }
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Every app has a signing secret (`whsec_…`) from the moment it is created, with or without a
|
|
305
|
+
webhook URL. Verify the context token with it in your backend; you can show it once and
|
|
306
|
+
rotate it in the developer portal (your app → *Webhooks* → *Signatur-Secret*).
|
|
307
|
+
|
|
276
308
|
## Entry points
|
|
277
309
|
|
|
278
310
|
| Import | Runs in | Contents |
|
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;
|
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,27 +76,45 @@ 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
|
-
/**
|
|
86
|
+
/** Bounds of an iframe extension's initial `height` in pixels. */
|
|
87
|
+
export declare const IFRAME_EXTENSION_HEIGHT_MIN = 120;
|
|
88
|
+
export declare const IFRAME_EXTENSION_HEIGHT_MAX = 2000;
|
|
89
|
+
/** lucide icon names a static action or `nav.page` may use as `icon`. */
|
|
82
90
|
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
91
|
export type AppActionIcon = (typeof APP_ACTION_ICONS)[number];
|
|
84
92
|
/** Every scope an app may request. Role, app, webhook, key, billing and AVV management are reserved for people. */
|
|
85
93
|
export declare const GRANTABLE_SCOPES: readonly ["courts:read", "courts:read_confidential", "courts:write", "bookings:read", "bookings:read_confidential", "bookings:write", "bookings:cancel", "bookings:edit", "bookings:export", "members:read", "members:read_confidential", "members:invite", "members:write", "members:delete", "teams:read", "teams:write", "policies:read", "policies:read_confidential", "policies:write", "categories:read", "categories:write", "invites:read", "invites:write", "settings:read", "settings:read_confidential", "settings:write", "guest_fees:read", "guest_fees:write", "audit:read", "announcements:read", "announcements:write", "events:read", "events:write", "posts:read", "posts:write", "posts:moderate", "court_layout:read", "court_layout:write", "work_hours:read", "work_hours:write", "work_hours:manage", "compliance:read", "kiosk:manage", "trainer_blocks:write", "analytics:read", "embeds:manage", "webhooks:read", "notifications:send"];
|
|
86
94
|
export type GrantableScope = (typeof GRANTABLE_SCOPES)[number];
|
|
95
|
+
type KindOf<K extends ExtensionKind> = K extends "iframe" ? {
|
|
96
|
+
kind: K;
|
|
97
|
+
/**
|
|
98
|
+
* Initial frame height in whole pixels, `IFRAME_EXTENSION_HEIGHT_MIN` to `IFRAME_EXTENSION_HEIGHT_MAX`.
|
|
99
|
+
* Without it the frame starts at 240 pixels, a `nav.page` fills the page. Resize messages override it.
|
|
100
|
+
*/
|
|
101
|
+
height?: number;
|
|
102
|
+
} : {
|
|
103
|
+
kind: K;
|
|
104
|
+
height?: never;
|
|
105
|
+
};
|
|
87
106
|
type ExtensionOf<P extends ExtensionPoint> = {
|
|
88
107
|
point: P;
|
|
89
|
-
kind: (typeof EXTENSION_POINTS)[P]["kinds"][number];
|
|
90
108
|
/** https; http://localhost is accepted outside production. */
|
|
91
109
|
url: string;
|
|
92
|
-
} & (P extends StaticActionPoint ? {
|
|
110
|
+
} & KindOf<(typeof EXTENSION_POINTS)[P]["kinds"][number]> & (P extends StaticActionPoint ? {
|
|
93
111
|
/** Button text, 1 to 24 characters (`STATIC_ACTION_LABEL_MAX`). */
|
|
94
112
|
label: string;
|
|
95
113
|
icon?: AppActionIcon;
|
|
114
|
+
} : P extends "nav.page" ? {
|
|
115
|
+
/** Navigation entry and page title, 1 to 24 characters (`STATIC_ACTION_LABEL_MAX`). */
|
|
116
|
+
label: string;
|
|
117
|
+
icon: AppActionIcon;
|
|
96
118
|
} : P extends "booking.action" ? {
|
|
97
119
|
label: string;
|
|
98
120
|
icon?: never;
|
|
@@ -104,8 +126,8 @@ type ExtensionOf<P extends ExtensionPoint> = {
|
|
|
104
126
|
icon?: never;
|
|
105
127
|
});
|
|
106
128
|
/**
|
|
107
|
-
* One entry of `extensions`; `kind`, `label` and `
|
|
108
|
-
* Every point may appear once, except `booking.action`.
|
|
129
|
+
* One entry of `extensions`; `kind`, `label`, `icon` and `height` are checked per point at compile time.
|
|
130
|
+
* Every point may appear once, except `booking.action`; an app has at most one `nav.page`.
|
|
109
131
|
*/
|
|
110
132
|
export type ManifestExtension = {
|
|
111
133
|
[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,16 @@ 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
|
-
/**
|
|
41
|
+
/** Bounds of an iframe extension's initial `height` in pixels. */
|
|
42
|
+
export const IFRAME_EXTENSION_HEIGHT_MIN = 120;
|
|
43
|
+
export const IFRAME_EXTENSION_HEIGHT_MAX = 2000;
|
|
44
|
+
/** lucide icon names a static action or `nav.page` may use as `icon`. */
|
|
40
45
|
export const APP_ACTION_ICONS = [
|
|
41
46
|
"bell",
|
|
42
47
|
"calendar",
|