@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 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 |
@@ -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;
@@ -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
- /** lucide icon names a static action may use as `icon`. */
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 `icon` are checked per point at compile time.
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
- /** lucide icon names a static action may use as `icon`. */
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkcourt/sdk",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Official TypeScript SDK for the CheckCourt app platform",
5
5
  "private": false,
6
6
  "license": "MIT",