@cube-nl/dmp-sdk 1.0.0-rc.4 → 1.1.0-rc.2
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 +372 -9
- package/dist/app.d.ts +41 -4
- package/dist/app.js +252 -14
- package/dist/browser.js +14 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.js +5 -1
- package/dist/cube-sdk.js +186 -2
- package/dist/cube-ui.manifest.json +971 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/preview.d.ts +11 -0
- package/dist/preview.js +60 -7
- package/dist/size.json +6 -0
- package/dist/trial.d.ts +32 -0
- package/dist/trial.js +55 -0
- package/dist/ui/aggregate-block.d.ts +73 -0
- package/dist/ui/aggregate-block.js +139 -0
- package/dist/ui/attributes.d.ts +5 -0
- package/dist/ui/attributes.js +72 -0
- package/dist/ui/author-error.d.ts +3 -0
- package/dist/ui/author-error.js +3 -0
- package/dist/ui/block.d.ts +69 -0
- package/dist/ui/block.js +238 -0
- package/dist/ui/blocks/app-shell.d.ts +4 -0
- package/dist/ui/blocks/app-shell.js +167 -0
- package/dist/ui/blocks/board.d.ts +8 -0
- package/dist/ui/blocks/board.js +333 -0
- package/dist/ui/blocks/calc.d.ts +9 -0
- package/dist/ui/blocks/calc.js +263 -0
- package/dist/ui/blocks/chart.d.ts +7 -0
- package/dist/ui/blocks/chart.js +166 -0
- package/dist/ui/blocks/detail.d.ts +8 -0
- package/dist/ui/blocks/detail.js +270 -0
- package/dist/ui/blocks/field.d.ts +8 -0
- package/dist/ui/blocks/field.js +16 -0
- package/dist/ui/blocks/form.d.ts +8 -0
- package/dist/ui/blocks/form.js +153 -0
- package/dist/ui/blocks/gap-list.d.ts +4 -0
- package/dist/ui/blocks/gap-list.js +36 -0
- package/dist/ui/blocks/kpi.d.ts +4 -0
- package/dist/ui/blocks/kpi.js +75 -0
- package/dist/ui/blocks/period-picker.d.ts +4 -0
- package/dist/ui/blocks/period-picker.js +155 -0
- package/dist/ui/blocks/state.d.ts +4 -0
- package/dist/ui/blocks/state.js +135 -0
- package/dist/ui/blocks/table.d.ts +8 -0
- package/dist/ui/blocks/table.js +666 -0
- package/dist/ui/blocks/top.d.ts +4 -0
- package/dist/ui/blocks/top.js +67 -0
- package/dist/ui/chart-svg.d.ts +28 -0
- package/dist/ui/chart-svg.js +111 -0
- package/dist/ui/csv.d.ts +9 -0
- package/dist/ui/csv.js +55 -0
- package/dist/ui/define.d.ts +24 -0
- package/dist/ui/define.js +107 -0
- package/dist/ui/dom.d.ts +12 -0
- package/dist/ui/dom.js +58 -0
- package/dist/ui/fields.d.ts +39 -0
- package/dist/ui/fields.js +73 -0
- package/dist/ui/format.d.ts +44 -0
- package/dist/ui/format.js +215 -0
- package/dist/ui/inputs.d.ts +35 -0
- package/dist/ui/inputs.js +161 -0
- package/dist/ui/manifest.d.ts +40 -0
- package/dist/ui/manifest.js +880 -0
- package/dist/ui/period.d.ts +18 -0
- package/dist/ui/period.js +52 -0
- package/dist/ui/query.d.ts +26 -0
- package/dist/ui/query.js +99 -0
- package/dist/ui/schema.d.ts +23 -0
- package/dist/ui/schema.js +21 -0
- package/dist/ui/styles.d.ts +8 -0
- package/dist/ui/styles.js +51 -0
- package/dist/ui/texts.d.ts +146 -0
- package/dist/ui/texts.js +138 -0
- package/dist/ui/theme.d.ts +58 -0
- package/dist/ui/theme.js +311 -0
- package/dist/ui/trial-banner.d.ts +25 -0
- package/dist/ui/trial-banner.js +180 -0
- package/dist/ui/write.d.ts +20 -0
- package/dist/ui/write.js +54 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wireframe.d.ts +37 -0
- package/dist/wireframe.js +352 -0
- package/fonts/OFL-albert-sans.txt +93 -0
- package/fonts/OFL-ibm-plex-sans.txt +93 -0
- package/fonts/OFL-inter.txt +93 -0
- package/fonts/OFL-nunito-sans.txt +93 -0
- package/fonts/OFL-source-sans-3.txt +93 -0
- package/fonts/albert-sans.woff2 +0 -0
- package/fonts/ibm-plex-sans.woff2 +0 -0
- package/fonts/inter.woff2 +0 -0
- package/fonts/nunito-sans.woff2 +0 -0
- package/fonts/source-sans-3.woff2 +0 -0
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ The rest of this document is in English.
|
|
|
29
29
|
- [Errors](#errors)
|
|
30
30
|
- [Voor AI-tools](#voor-ai-tools)
|
|
31
31
|
- [Route B: apps without a build step](#route-b-apps-without-a-build-step)
|
|
32
|
+
- [Bouwstenen (UI)](#bouwstenen-ui)
|
|
32
33
|
- [What this SDK deliberately does not do](#what-this-sdk-deliberately-does-not-do)
|
|
33
34
|
- [Development](#development)
|
|
34
35
|
- [Integration tests](#integration-tests)
|
|
@@ -49,9 +50,9 @@ For an app without a build step, the single-file browser build is on the npm CDN
|
|
|
49
50
|
version:
|
|
50
51
|
|
|
51
52
|
```html
|
|
52
|
-
<script src="https://cdn.jsdelivr.net/npm/@cube-nl/dmp-sdk@1.
|
|
53
|
+
<script src="https://cdn.jsdelivr.net/npm/@cube-nl/dmp-sdk@1.1.0-rc.2/dist/cube-sdk.js"></script>
|
|
53
54
|
<!-- or -->
|
|
54
|
-
<script src="https://unpkg.com/@cube-nl/dmp-sdk@1.
|
|
55
|
+
<script src="https://unpkg.com/@cube-nl/dmp-sdk@1.1.0-rc.2/dist/cube-sdk.js"></script>
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
Fallback, for an unreleased commit: `npm install
|
|
@@ -577,6 +578,63 @@ What the preview cannot do: mock data names no relations, so an `include` or `wi
|
|
|
577
578
|
in `meta.omitted` (show it as "not visible", as live) and `has` is not applied; custom operations
|
|
578
579
|
are `404 not_found`; `location` and `etag` are null.
|
|
579
580
|
|
|
581
|
+
### Trial (proefversie)
|
|
582
|
+
|
|
583
|
+
A maker can try an app on their own data before it is approved: the DMP serves the app's files in a sandboxed page
|
|
584
|
+
(CSP `sandbox`, an opaque origin) and writes a read-only token into that page. The SDK then runs the app as a third
|
|
585
|
+
mode, `'trial'`, with the same code as preview and live.
|
|
586
|
+
|
|
587
|
+
The DMP inserts one data block as the first child of `<head>` (a JSON data block, not a script, so the CSP does not
|
|
588
|
+
apply to it):
|
|
589
|
+
|
|
590
|
+
```html
|
|
591
|
+
<script type="application/json" id="cube-trial-config">{"mode":"trial","dmpUrl":"https://dmp.example.nl",
|
|
592
|
+
"apiUrl":"https://dmp.example.nl/apps/api/v1","token":"cubeproef_…","expiresAt":"2026-10-20T15:42:00+02:00",
|
|
593
|
+
"statusUrl":"https://dmp.example.nl/apps/api/v1/_trial","version":"1.0.3","soort":null,"userId":"42",
|
|
594
|
+
"leest":["orders","klanten"]}</script>
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
| Member | |
|
|
598
|
+
|---|---|
|
|
599
|
+
| `mode` | Must be `"trial"` |
|
|
600
|
+
| `dmpUrl`, `apiUrl`, `token`, `expiresAt`, `statusUrl` | Required non-empty strings. `expiresAt` is ISO 8601 |
|
|
601
|
+
| `version`, `soort`, `userId` | A string or `null` |
|
|
602
|
+
| `leest` | The resources the trial reads, for the banner; default `[]` |
|
|
603
|
+
|
|
604
|
+
- **Read once, then removed.** When `cube-sdk.js` (or the ES module, in a browser) evaluates, it reads the element,
|
|
605
|
+
removes it from the DOM and keeps the config in memory. A config that is not JSON, not `mode: "trial"` or misses a
|
|
606
|
+
member gives one `console.warn` (without its content) and the app starts as it would without it.
|
|
607
|
+
- **The session.** `connect()` checks the trial config first: it does not fetch `cube-app.config.json`, does no OAuth
|
|
608
|
+
(no discovery, no PKCE, no callback) and touches neither `sessionStorage` nor `localStorage`. Every request carries
|
|
609
|
+
`Authorization: Bearer cubeproef_…` to `apiUrl`. `user()` is `{ id: userId, name: null, scopes: ['api:read'] }`;
|
|
610
|
+
`signIn()` reloads the page (the DMP writes a token on every load); `signOut()` makes calls from the app stop carrying
|
|
611
|
+
the token (they answer `401`); the banner's status poll keeps it. The SDK refuses a write in a trial before it
|
|
612
|
+
leaves the page (the browser would block it at the CORS preflight): it fails as the DMP's `403` would, a `DmpError`
|
|
613
|
+
with status 403 that blocks show as "no access", never faked; only a
|
|
614
|
+
wireframe (`soort: "wireframe"`) runs in the same mixed mode as live, with simulated writes.
|
|
615
|
+
- **The token is never stored and never logged.** It lives in a variable of the page for as long as the page. This
|
|
616
|
+
keeps it out of storage, logs and an innocent `innerHTML` dump; it is no defence against hostile code in the page,
|
|
617
|
+
which the DMP bounds instead (read-only, bound to the maker's IP and `Origin: null`, at most 60 minutes).
|
|
618
|
+
- **The banner.** `cube-sdk.js` puts `<cube-trial-banner>` first in `<body>` (shadow DOM, constructable stylesheet,
|
|
619
|
+
house-style tokens): *"Proefversie · alleen jij · alleen lezen · verloopt om 15:42 · leest: orders, klanten · Plak
|
|
620
|
+
geen gegevens uit deze proefversie in je AI-gesprek."* (the time in nl-NL local time).
|
|
621
|
+
- **The poll.** It asks `GET statusUrl` (with the token, `cache: 'no-store'`, a 10 s timeout) every 5 s while the
|
|
622
|
+
page is visible and every 60 s while hidden, and right away when the page becomes visible again. When `expiresAt`
|
|
623
|
+
passes it shows the expired state without a request.
|
|
624
|
+
|
|
625
|
+
| Answer | Banner |
|
|
626
|
+
|---|---|
|
|
627
|
+
| `200` | Nothing changes, polling goes on |
|
|
628
|
+
| Network error, timeout, `429`, `5xx` | Polling goes on |
|
|
629
|
+
| `401 trial_replaced`, `wider: false` | *"Nieuwe versie wordt geladen…"*, then `location.assign(newer)` |
|
|
630
|
+
| `401 trial_replaced`, otherwise | *"Er is een nieuwe versie die meer gegevens leest."* with **Laden**: only the maker's click navigates |
|
|
631
|
+
| `401 trial_elsewhere` | *"Laad de pagina opnieuw."* with **Opnieuw laden** |
|
|
632
|
+
| `401 trial_expired` | *"Deze proefversie is verlopen. Vraag in je gesprek om een nieuwe."* |
|
|
633
|
+
| `401 trial_closed`, any other answer | *"Deze proefversie is gesloten."* |
|
|
634
|
+
|
|
635
|
+
`newer` is followed only when it has `dmpUrl`'s origin and a path under `/apps/proef/`; any other URL counts as
|
|
636
|
+
closed. Every state except `200` and the transient ones stops the polling.
|
|
637
|
+
|
|
580
638
|
### Mock data
|
|
581
639
|
|
|
582
640
|
The shape is `cube.mock_data`'s output as is: per REST resource the `dmp.describe` payload and the
|
|
@@ -616,14 +674,19 @@ Without them the preview is permissive: every field, every function; only a fiel
|
|
|
616
674
|
|---|---|
|
|
617
675
|
| `CubeApp.connect(options?)` | `Promise<CubeAppSession>`. Options: `mock`, `manifest` (an object, or `false` to check nothing), `pageUrl` (default the page's URL), `fetch`, `tokenStorage` (`'session'`, the default, or `'memory'`), `autoSignIn` (default `false`) |
|
|
618
676
|
| `CubeApp.mock(data)` | Registers preview data for the next `connect()`; `null` forgets it |
|
|
619
|
-
| `CubeApp.version` | This SDK's version, e.g. `'1.
|
|
677
|
+
| `CubeApp.version` | This SDK's version, e.g. `'1.1.0-rc.2'` |
|
|
620
678
|
| `CubeApp.sdk` | The manifest `"sdk"` this build serves, `'1'` |
|
|
621
|
-
| `session.mode` | `'preview'` or `'
|
|
679
|
+
| `session.mode` | `'preview'`, `'live'` or `'trial'` (a proefversie the DMP serves: the maker's own data, read-only, with a token written into the page) |
|
|
622
680
|
| `session.user()` | `{ id, name, scopes }` or `null` |
|
|
623
|
-
| `session.signIn()` | Live: navigates to the DMP's sign-in. Preview: signs the stand-in in |
|
|
624
|
-
| `session.signOut()` | Forgets the tokens in this browser (not a revocation). Preview: signs the stand-in out |
|
|
681
|
+
| `session.signIn()` | Live: navigates to the DMP's sign-in. Preview: signs the stand-in in. Trial: reloads the page |
|
|
682
|
+
| `session.signOut()` | Forgets the tokens in this browser (not a revocation). Preview: signs the stand-in out. Trial: forgets the token, calls then answer 401 |
|
|
625
683
|
| `session.resource(name)` | A `ResourceClient`: `list`, `pages`, `all`, `get`, `aggregate`, `create`, `update`, `delete`, `operation`, `collectionOperation` |
|
|
626
684
|
| `session.signInError` | The error the last sign-in came back with (a refused consent: `code` `access_denied`), else `null`; `connect()` does not reject for it |
|
|
685
|
+
| `session.schema(name)` | `Promise<ResourceSchema \| null>`: one resource as a block sees it (fields, actions, what may be filtered, sorted, grouped); `null` when unknown. In a wireframe it includes the missing fields |
|
|
686
|
+
| `session.soort` | What the app is: live the config's `soort`, in the preview the manifest's (`'wireframe'`, say); `null` when neither says |
|
|
687
|
+
| `session.gaps` | A wireframe's `ontbreekt` entries (see [Wireframe](#wireframe)); `[]` for any other app |
|
|
688
|
+
| `session.isExample(resource, field?)` | Whether a wireframe shows invented data there: the whole resource, or that field of it, does not exist yet |
|
|
689
|
+
| `session.simulatesWrites` | `true` in a wireframe: writes are answered in the browser and never saved |
|
|
627
690
|
|
|
628
691
|
Live, `connect()` handles the OAuth callback itself (on the `redirectUri`), so the redirect URI can
|
|
629
692
|
be the app's own `index.html`. The tokens go to `sessionStorage` by default, unlike `createOAuth`'s
|
|
@@ -639,13 +702,313 @@ app with a build (route A).
|
|
|
639
702
|
| | |
|
|
640
703
|
|---|---|
|
|
641
704
|
| File | `dist/cube-sdk.js` in the package, `@cube-nl/dmp-sdk/cube-sdk.js` as an export; on jsDelivr/unpkg as `@cube-nl/dmp-sdk@<version>/dist/cube-sdk.js`; the CI's `pack` job also keeps it as an artifact |
|
|
642
|
-
| Size |
|
|
643
|
-
| Version | The banner's first line, `/*! @cube-nl/dmp-sdk 1.
|
|
705
|
+
| Size | 134.7 KiB minified, 45.4 KiB gzipped (17.7 KiB core + 27.6 KiB blocks and the trial banner) in 1.1.0-rc.2; `dist/size.json` has the exact numbers |
|
|
706
|
+
| Version | The banner's first line, `/*! @cube-nl/dmp-sdk 1.1.0-rc.2 cube-sdk.js … */`, and `CubeApp.version` |
|
|
644
707
|
|
|
645
708
|
Pin it by copying the built file of a known version into the host, rather than fetching it at
|
|
646
709
|
runtime: `npm pack @cube-nl/dmp-sdk@<version>` downloads the tarball, `package/dist/cube-sdk.js` is
|
|
647
710
|
the file. Nothing from a CDN, nothing at request time.
|
|
648
711
|
|
|
712
|
+
## Bouwstenen (UI)
|
|
713
|
+
|
|
714
|
+
**Nederlands.** `cube-sdk.js` bevat ook kant-en-klare bouwstenen: custom elements met het voorvoegsel `cube-`
|
|
715
|
+
die zelf hun gegevens ophalen, de huisstijl van de klant volgen en onder de CSP van een gehost DMP-app
|
|
716
|
+
werken. De eerste set is `cube-app-shell` (kader met kop, navigatie en inloggen), `cube-page` (één pagina van
|
|
717
|
+
het kader) en `cube-state` (laden, leeg, fout, geen toegang, niet ingelogd). Alle teksten zijn Nederlands en
|
|
718
|
+
getallen, bedragen en datums zijn `nl-NL`.
|
|
719
|
+
|
|
720
|
+
```html
|
|
721
|
+
<script src="./cube-sdk.js"></script>
|
|
722
|
+
<script type="module" src="./app.js"></script>
|
|
723
|
+
|
|
724
|
+
<cube-app-shell name="Verkoop">
|
|
725
|
+
<cube-page name="overzicht" label="Overzicht">
|
|
726
|
+
<cube-state kind="empty" message="Nog geen bestellingen deze week."></cube-state>
|
|
727
|
+
</cube-page>
|
|
728
|
+
<cube-page name="details" label="Details">...</cube-page>
|
|
729
|
+
</cube-app-shell>
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
How the blocks find the session: a block never signs in by itself. It takes the session the app made with
|
|
733
|
+
`CubeApp.connect()` (`CubeApp.ready`), so a block that sits in the page before the script runs still uses the app's
|
|
734
|
+
session. **Call `CubeApp.connect()` before any `await`** in your script: start it first and await it after.
|
|
735
|
+
Set the house style in the same call, `CubeApp.connect({ theme })`, or call `CubeApp.theme()` before `connect()`: a
|
|
736
|
+
`CubeApp.theme()` call made after `connect()` can be painted over by the theme the start-up applies.
|
|
737
|
+
|
|
738
|
+
```js
|
|
739
|
+
const session = CubeApp.connect(); // first, so the blocks take this session
|
|
740
|
+
const extra = await (await fetch('./extra.json')).json();
|
|
741
|
+
await session;
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
An app that never connects gets a session with the defaults, but only once the page has fully loaded (the window's
|
|
745
|
+
`load` event, plus one task), so an app that connects a little later still gets there first and the page signs in
|
|
746
|
+
once. If the app connects after that default session exists, its own session replaces it: `CubeApp.connect()`
|
|
747
|
+
dispatches `cube-session-change` on the document and every block takes the new one. That is a safety net, not a
|
|
748
|
+
way to start: in a live app the default start-up has then handled the sign-in callback with the default options.
|
|
749
|
+
A 401 shows "niet ingelogd", a 403 "geen toegang" (with the right ARIA role), never the raw message. After signing
|
|
750
|
+
in or out the shell dispatches `cube-session-change` on the document and every block loads again.
|
|
751
|
+
|
|
752
|
+
With the classic script the blocks are defined when `cube-sdk.js` loads, and `window.CubeApp` also has
|
|
753
|
+
`CubeApp.defineBlocks(options?)`. On a page without `customElements` (an old engine, a server) it does nothing and
|
|
754
|
+
`CubeApp` still works. Through npm the blocks are not defined on import: call `defineBlocks` yourself.
|
|
755
|
+
|
|
756
|
+
```js
|
|
757
|
+
import { CubeApp, defineBlocks } from '@cube-nl/dmp-sdk';
|
|
758
|
+
|
|
759
|
+
defineBlocks(); // the ESM `CubeApp` has no defineBlocks; import it from the package
|
|
760
|
+
await CubeApp.connect();
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
`defineBlocks({ session })` takes a function returning the session the blocks use; the default is `CubeApp.ready`.
|
|
764
|
+
|
|
765
|
+
The manifest `dist/cube-ui.manifest.json` (export `@cube-nl/dmp-sdk/cube-ui.manifest.json`) lists every element
|
|
766
|
+
with its attributes, events, an example and the version it appeared in (`since`), plus the 27 theme tokens and
|
|
767
|
+
the fonts. Its shape is final for 1.1. It is what Claude reads to compose an app. The build also writes `dist/size.json`
|
|
768
|
+
(`core_gzip`, `total_gzip`, `ui_gzip`, `budget_gzip`): the blocks may add at most 40 KiB gzipped to the core and the
|
|
769
|
+
build fails above it.
|
|
770
|
+
|
|
771
|
+
### Huisstijl
|
|
772
|
+
|
|
773
|
+
The house style is one file next to `index.html`: `cube-theme.json`. Its shape below is final for 1.1. The DMP writes it when an app is published;
|
|
774
|
+
without it (or with a broken one) the neutral theme applies and the app starts anyway. Read once by
|
|
775
|
+
`CubeApp.connect()`, applied to every block through `adoptedStyleSheets`.
|
|
776
|
+
|
|
777
|
+
```json
|
|
778
|
+
{
|
|
779
|
+
"theme": 1,
|
|
780
|
+
"captured_at": "2026-10-19T09:00:00Z",
|
|
781
|
+
"source": {
|
|
782
|
+
"primary_colour": "#0b5fff",
|
|
783
|
+
"accent_colour": null,
|
|
784
|
+
"font": "inter",
|
|
785
|
+
"corners": "normaal",
|
|
786
|
+
"logo": "cube-theme/logo.svg",
|
|
787
|
+
"logo_dark": null
|
|
788
|
+
},
|
|
789
|
+
"tokens": { "color-danger": "#b42318" },
|
|
790
|
+
"font_files": [{ "src": "cube-theme/inter.woff2", "weight": "100 900", "style": "normal" }]
|
|
791
|
+
}
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
| Key | Meaning |
|
|
795
|
+
|---|---|
|
|
796
|
+
| `source.primary_colour` | `#rrggbb`; everything else is derived from it. An invalid value falls back to the neutral `#475467` |
|
|
797
|
+
| `source.accent_colour` | `#rrggbb` or `null` (then the hue of the primary colour turned 330 degrees) |
|
|
798
|
+
| `source.font` | `system`, `inter`, `albert-sans`, `source-sans-3`, `nunito-sans` or `ibm-plex-sans` |
|
|
799
|
+
| `source.corners` | `scherp` (0 px), `normaal` (6 px) or `rond` (12 px) |
|
|
800
|
+
| `source.logo`, `source.logo_dark` | `cube-theme/logo.<ext>` next to the app, or `null` |
|
|
801
|
+
| `tokens` | Optional per-token overrides; only a valid value is used, anything else keeps the derived one |
|
|
802
|
+
| `font_files` | Optional; default the files of the chosen font as `cube-theme/<file>` |
|
|
803
|
+
|
|
804
|
+
The tokens are the CSS custom properties `--cube-<name>` on every block: `color-primary`, `color-primary-hover`,
|
|
805
|
+
`color-primary-subtle`, `color-primary-text`, `color-on-primary`, `color-accent`, `color-on-accent`, `color-text`,
|
|
806
|
+
`color-text-muted`, `color-bg`, `color-surface`, `color-border`, `color-focus`, `color-danger`, `color-success`,
|
|
807
|
+
`color-warning`, `color-example-bg`, `color-example-text`, `color-series-1` to `color-series-6`, `radius`,
|
|
808
|
+
`radius-small` and `font-family`. Text colours are checked for WCAG 2.1 AA contrast: `color-on-primary` and
|
|
809
|
+
`color-on-accent` on their colours, and `color-primary-text` (also the focus ring) at 4.5:1 on both the surface
|
|
810
|
+
`#f6f8fa` and `color-primary-subtle`; a primary colour that falls short is darkened for text in 5% steps (warning
|
|
811
|
+
`primary-darkened-for-text`).
|
|
812
|
+
|
|
813
|
+
A value that could break out of the stylesheet never reaches it: colours must be `#rrggbb`, radii a number of `px`
|
|
814
|
+
or `rem`, and `font-family` a comma-separated list of names (quoted names allowed, but no unbalanced quote, no
|
|
815
|
+
semicolon, brace, backslash or comment). Logo and font addresses are kept only when they are on the page's own
|
|
816
|
+
origin (or a base64 `data:` image) and hold no quote, parenthesis or whitespace. A `data:` logo shows in the chat
|
|
817
|
+
preview, but a hosted app's CSP (`img-src 'self'`) blocks it: a published app uses `cube-theme/logo.<ext>`.
|
|
818
|
+
|
|
819
|
+
The theme sheet sets the tokens on `:where(:root)`, which has no specificity, so an app's own stylesheet can
|
|
820
|
+
override any token in `:root`, for example `:root { --cube-color-primary: #0b5fff; }`.
|
|
821
|
+
|
|
822
|
+
`CubeApp.theme(fileOrSource)` registers a theme from code (a `cube-theme.json` object, or just its `source`) and
|
|
823
|
+
applies it at once; `CubeApp.theme(null)` forgets it. `connect({ theme })` takes the same value, or `false` for
|
|
824
|
+
the neutral theme without reading `cube-theme.json`. The font files ship in the package under `fonts/`; a host
|
|
825
|
+
serves them as `cube-theme/<file>` (for example `fonts/inter.woff2` as `cube-theme/inter.woff2`). Each font has
|
|
826
|
+
its own OFL licence text next to it.
|
|
827
|
+
|
|
828
|
+
### Blokken
|
|
829
|
+
|
|
830
|
+
Every block is a custom element; the full list with attributes lives in the manifest. Row data is only
|
|
831
|
+
ever rendered as text, never as HTML. Every control works with the keyboard and shows a focus ring, and the
|
|
832
|
+
blocks work from 320 px wide.
|
|
833
|
+
|
|
834
|
+
| Element | Use |
|
|
835
|
+
|---|---|
|
|
836
|
+
| `cube-app-shell` | `name` (required), `logo`, `logo-alt`. Header, user with Inloggen/Uitloggen, page navigation that follows `#/<name>` |
|
|
837
|
+
| `cube-page` | `name` (id in the address), `label` (text in the navigation). A configuration element: it only holds blocks |
|
|
838
|
+
| `cube-state` | `kind`: `loading`, `empty`, `error`, `forbidden`, `signed-out`; `message` replaces the standard text; `retry="false"` leaves out the retry button. Event `cube-retry`. A block's error state shows the Dutch reason for a wrong attribute; any other failure shows the general text, and a network error or the SDK's own `TypeError` is also logged in the console |
|
|
839
|
+
| `cube-period-picker` | `default` (`this-month`, `12m`, `this-year`, `custom`), `label`, `today`. Feeds other blocks; `value` is `{ key, from, to, previous, today }`, event `cube-change` |
|
|
840
|
+
| `cube-kpi` | `resource`, `measure` (required), `label`, `format`, `currency`, `filter`, `period`, `date-field`, `compare`. One figure; `compare` adds the change against the previous period (`nieuw` when that was zero) |
|
|
841
|
+
| `cube-chart` | `resource`, `measure`, `by` (required), `kind` (`line`/`bar`), `label`, `format`, `currency`, `filter`, `period`, `date-field`, `lookup`, `height`, `limit`. Own SVG, no chart library; a table for screen readers |
|
|
842
|
+
| `cube-top` | `resource`, `measure`, `by` (required), `limit`, `order`, `label`, `format`, `currency`, `filter`, `period`, `date-field`, `lookup`. Sorted and cut in the browser (an aggregate cannot be sorted by the API). Over 1000 groups the DMP refuses the aggregate (`422 cardinality_exceeded`, reason `too_many_groups`); the block then asks for a shorter period or a filter, without a retry. Event `cube-select` |
|
|
843
|
+
| `cube-field` | `name` (required), `label`, `format` (`text`, `number`, `integer`, `currency`, `percent`, `date`, `datetime`, `boolean`), `multiline`, `options` (`value=Label,...`), `hidden`. A configuration element, a direct child of `cube-table`, `cube-detail`, `cube-form` or `cube-board`; it is never rendered. Live `_capabilities` carries no `format` or `enum`, so a date column or a choice filter there comes from `format`/`options` here or from the values themselves, never from a guess at the type |
|
|
844
|
+
| `cube-table` | `resource` (required), `fields`, `search` (one field, box "Zoeken"), `filters` (a control per field: choices, Alle/Ja/Nee, from-to dates or numbers, else contains), `sort` (`-` for descending), `page-size` (default 25, at most 200), `export`, `detail` (selector of a `cube-detail`), `filter`, `period`, `date-field`. Needs `list`. Search waits 300 ms and the last answer wins. The rows are one Tab stop: arrow keys, Home and End move between rows; Enter or Space chooses one, which stays marked (`aria-current`). Event `cube-select` |
|
|
845
|
+
| `cube-detail` | `resource` (required), `record-id`, `fields`, `editable`. Needs `get`, and `update` for `editable`. Events `cube-saved`, `cube-data-change` |
|
|
846
|
+
| `cube-form` | `resource` (required), `fields`, `submit-label` (default "Opslaan"), `success-text` (default "Opgeslagen. Dank je wel!"). Needs `create`. Events `cube-created`, `cube-data-change` |
|
|
847
|
+
| `cube-calc` | `label`. A calculator that needs no data: an editor per `cube-input`, an output per `cube-result`, recalculated on every keystroke by the `formula` property. Read-only `values` and `results`, event `cube-change` (`{ values, results }`) |
|
|
848
|
+
| `cube-input` | `name`, `label` (required), `type` (`number`, `integer`, `currency`, `percent`, `text`, `select`, `checkbox`), `value`, `min`, `max`, `step`, `options` (a select needs it; `value=Label,...` as in `cube-field`). A configuration element, a direct child of `cube-calc`; it is never rendered. Numbers are typed the Dutch way (`7,5`) |
|
|
849
|
+
| `cube-result` | `name`, `label` (required), `format` (`number`, `integer`, `currency`, `percent`, `text`), `currency` (default `EUR`), `emphasis` (the large figure). A configuration element, a direct child of `cube-calc`; it is never rendered |
|
|
850
|
+
| `cube-board` | `resource`, `status-field`, `title-field` (required), `fields` (extra lines on a card), `sort`, `filter`, `page-size` (default 50, at most 200; a column with more says "en nog N"). `cube-field` children label and format the `fields` of a card. Cards in a column per status value. `status-field` must be filterable and `sort` a sortable field. Needs `list`; `update` is optional (without it the board is read-only). Event `cube-move` (`{ resource, id, from, to }`), followed by `cube-data-change` |
|
|
851
|
+
| `cube-column` | `value` (required), `label`. A configuration element, a direct child of `cube-board`, in order; it is never rendered |
|
|
852
|
+
|
|
853
|
+
`cube-table`, `cube-detail` and `cube-form` need the matching actions in the app's `uses`
|
|
854
|
+
(`"orders": ["list", "get", "create", "update"]`). A table sends the row it chose to the `cube-detail` named by
|
|
855
|
+
`detail="#bestelling"`; after a save or a create every block on that resource loads again (`cube-data-change`).
|
|
856
|
+
|
|
857
|
+
- **CSV export** (`export`): the filtered rows, at most 10 000, with `;` as the separator, a UTF-8 BOM and CRLF line
|
|
858
|
+
ends, so a Dutch spreadsheet opens it with the right columns. A text value that starts with `=`, `+`, `-`, `@`, a
|
|
859
|
+
tab or a carriage return gets a `'` in front (formula injection); a genuine number in a numeric column, such as
|
|
860
|
+
`-5` or `12,5`, is not prefixed (in a text column `-5` becomes `'-5`). Pressing the button again while it runs
|
|
861
|
+
cancels.
|
|
862
|
+
- **Filters**: `filter` and the followed period are always applied; the controls can only narrow them. A bound
|
|
863
|
+
takes the stricter of the two (a "Vanaf" before the period start keeps the period start), and a field that
|
|
864
|
+
`filter` fixes to one value gets no control. A `search` box or a contains filter on a field that `filter` fixes or
|
|
865
|
+
gives an `ilike`, or a choice filter on a field that `filter` limits with an operator (such as `in`), changes
|
|
866
|
+
nothing (the base wins); the console warns once. A `filters` or `search` field the resource cannot be filtered on
|
|
867
|
+
(not in the schema's `filterable`) shows an error naming that field instead of a table.
|
|
868
|
+
- **Required fields**: a field the DMP requires shows " (verplicht)" after its label, besides `aria-required`.
|
|
869
|
+
- **Writes**: every submit attempt carries an `Idempotency-Key` (`newIdempotencyKey()`), the button is disabled
|
|
870
|
+
while saving, and a retry after a network error reuses the key, so a double click creates one row. `cube-detail`
|
|
871
|
+
sends only the changed fields with `ifMatch` set to the `recordVersion` it loaded. When someone else changed the
|
|
872
|
+
row meanwhile (409 or 412) it shows "Iemand anders heeft deze rij intussen gewijzigd" with "Opnieuw laden", keeps
|
|
873
|
+
what you typed and does not retry by itself. Read-only fields are never shown as an input and never sent.
|
|
874
|
+
- **Numbers**: a number field takes Dutch input. `12,5` is sent as `12.5`, `1.234` is one thousand two hundred
|
|
875
|
+
thirty-four (the dot is a thousands separator), `12.5` also works, and `abc` is refused in the browser with
|
|
876
|
+
"Vul een getal in."
|
|
877
|
+
|
|
878
|
+
```html
|
|
879
|
+
<cube-table resource="orders" fields="reference,placed_on,total,status" search="reference" filters="status,placed_on"
|
|
880
|
+
export detail="#bestelling">
|
|
881
|
+
<cube-field name="total" label="Bedrag" format="currency"></cube-field>
|
|
882
|
+
</cube-table>
|
|
883
|
+
<cube-detail id="bestelling" resource="orders" editable></cube-detail>
|
|
884
|
+
<cube-form resource="orders" fields="reference,placed_on,total,status"></cube-form>
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
The three data blocks (`cube-kpi`, `cube-chart`, `cube-top`) read aggregates, so the app's `cube-app.json` needs
|
|
888
|
+
`aggregate` in `uses` for each resource they read; `lookup` also needs `list` on the lookup resource
|
|
889
|
+
(`"orders": ["list", "aggregate"], "customers": ["list"]`). The `cube-period-picker` reads nothing itself: it only
|
|
890
|
+
feeds them. `period="#periode"` points at a picker by CSS selector and needs `date-field`, the date the period
|
|
891
|
+
filters on, except on a `cube-chart` whose `by` is a truncated date (`placed_on.month`): that field is used.
|
|
892
|
+
`compare` needs `period`. The period filter is always `{ gte: from, lt: dayAfterTo }` on that field, never
|
|
893
|
+
`between`, so it is right for dates and datetimes alike. A chart leaves the buckets after the picker's `today` empty
|
|
894
|
+
(a gap, not a zero). An aggregate over more than 1000 groups is refused by the DMP; `cube-chart` and `cube-top` then
|
|
895
|
+
say so ("Te veel groepen…") and ask for a shorter period or a filter, without a retry button.
|
|
896
|
+
|
|
897
|
+
```html
|
|
898
|
+
<cube-period-picker id="periode" default="12m"></cube-period-picker>
|
|
899
|
+
<cube-kpi resource="orders" measure="sum(total)" label="Omzet" format="currency"
|
|
900
|
+
period="#periode" date-field="placed_on" compare></cube-kpi>
|
|
901
|
+
<cube-chart resource="orders" measure="sum(total)" by="placed_on.month" period="#periode"></cube-chart>
|
|
902
|
+
<cube-top resource="orders" measure="sum(total)" by="customer_id" lookup="customers.name"
|
|
903
|
+
period="#periode" date-field="placed_on"></cube-top>
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
#### Calculator and board
|
|
907
|
+
|
|
908
|
+
- **Formula** (`cube-calc`): `formula` is a synchronous JavaScript function property, set from a script, never an
|
|
909
|
+
attribute (an `async` one is refused with an error). A string is refused and nothing is evaluated, so it
|
|
910
|
+
works under the CSP. It gets `values` by `cube-input` name and returns an object by `cube-result` name (or one
|
|
911
|
+
number or text when there is a single result):
|
|
912
|
+
|
|
913
|
+
```js
|
|
914
|
+
document.querySelector('cube-calc').formula = ({ uren, tarief, btw }) => ({
|
|
915
|
+
totaal: uren * tarief * (1 + btw),
|
|
916
|
+
});
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
A `percent` input is typed as `21` and arrives as the fraction `0.21`; an empty or invalid input arrives as
|
|
920
|
+
`null` (also below `min`, above `max` or off `step`). A result that is `null` or `undefined` just shows "—". A
|
|
921
|
+
formula that throws, or a result that is `NaN`, `Infinity` or an object (for a number, integer, currency or percent
|
|
922
|
+
result), shows "—" and one alert that stays in place; the inputs keep working. A formula that throws logs
|
|
923
|
+
one `console.warn` per failure streak. Two or more `cube-result`s need an object back: a single number or text then
|
|
924
|
+
shows an error instead of the calculator on draw, but while typing it is a failed calculation ("—" and the alert),
|
|
925
|
+
as the formula may just have taken another branch. The `name` of each `cube-input`, and of each `cube-result`,
|
|
926
|
+
must be unique: a duplicate is refused.
|
|
927
|
+
`cube-change` follows a recalculation by typing or by a new formula on a drawn calculator; not the first draw.
|
|
928
|
+
- **Columns** (`cube-board`): the columns come from the `cube-column` children, in that order. Without them the
|
|
929
|
+
board takes the `enum` of the status field, but live `_capabilities` carries no `enum`, so a starter must always
|
|
930
|
+
write the `cube-column`s (live without any shows an error). Each column loads its own cards, filtered on the
|
|
931
|
+
status field, so that field must be filterable on the resource (and `sort` a sortable field), else the board
|
|
932
|
+
shows an error; a status value without a column is not shown.
|
|
933
|
+
- **Moving**: drag and drop, or a compact button per other column on each card, so it works with the keyboard too.
|
|
934
|
+
A button shows an arrow and the column name ("→ Bezig"); its accessible name is "Verplaats naar Bezig". The card
|
|
935
|
+
moves at once, then is saved (`update` of only the status field); a refusal puts it back with an alert. The card
|
|
936
|
+
is placed in the new column by `sort` until the next load, which shows the server's order (on top without
|
|
937
|
+
`sort`). A status field of type integer or number
|
|
938
|
+
gets the column value as a number.
|
|
939
|
+
- **Read-only**: without `update` in the app's `uses`, when the status field is read-only, or when the resource has no
|
|
940
|
+
`id` field (the board then does not request `id`), cards cannot move and show no buttons.
|
|
941
|
+
Columns stack on a narrow screen (from 320 px).
|
|
942
|
+
|
|
943
|
+
### Wireframe
|
|
944
|
+
|
|
945
|
+
A wireframe is a proposal that shows what an app would look like, partly on real data and partly on invented data.
|
|
946
|
+
The kind is `soort`: `"wireframe"` in `cube-app.json`, copied by the DMP into `cube-app.config.json`. Only a
|
|
947
|
+
wireframe gets any of what follows; a live app without `soort: "wireframe"` never reads `cube-mock.json` and never
|
|
948
|
+
answers from invented data, and a broken server stays an error.
|
|
949
|
+
|
|
950
|
+
What the DMP does not have yet is listed in `ontbreekt` in `cube-app.json`, one entry per missing field or per
|
|
951
|
+
missing resource:
|
|
952
|
+
|
|
953
|
+
```json
|
|
954
|
+
{
|
|
955
|
+
"soort": "wireframe",
|
|
956
|
+
"ontbreekt": [
|
|
957
|
+
{ "resource": "orders", "veld": "delivery_note", "type": "tekst", "scherm": "Lijst",
|
|
958
|
+
"reden": "Een opmerking voor de bezorger staat nog niet bij een bestelling." },
|
|
959
|
+
{ "resource": "visits", "type": "tekst", "scherm": "Wat ontbreekt",
|
|
960
|
+
"reden": "Bezoeken aan de winkel worden nog nergens vastgelegd." }
|
|
961
|
+
]
|
|
962
|
+
}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
`resource` is a REST resource name, `veld` the missing field (left out when the whole resource is missing), `type`
|
|
966
|
+
one of `tekst`, `getal`, `bedrag`, `datum`, `keuze`, `ja/nee`, `waarden` the choices (required for `keuze`), `scherm`
|
|
967
|
+
the screen that shows it and `reden` why it is needed. The SDK only reads this; `cube/apps` validates it. A
|
|
968
|
+
malformed entry, or an `ontbreekt` that is not a list, is dropped with one `console.warn`.
|
|
969
|
+
|
|
970
|
+
- **Mixed mode.** What exists runs live. A missing field is added to the live rows with a value invented from the row's
|
|
971
|
+
`id` (the same row shows the same value every time); a missing resource is answered from `cube-mock.json`. The live
|
|
972
|
+
request never names a missing field: a filter or sort on it applies to the returned page only (the page's count then
|
|
973
|
+
counts only the rows it shows), and an aggregate that groups by or sums it is answered from the mock rows. The preview
|
|
974
|
+
does the same; there, an aggregate on a missing field needs that field in the rows of `cube-mock.json`. A gap of type
|
|
975
|
+
`bedrag` shows as money also when the mock data has the field; other types follow the field's values.
|
|
976
|
+
- **Marking.** Blocks mark what is invented: `Voorbeeld — bestaat nog niet`. A table or detail marks the column or
|
|
977
|
+
field; the other blocks mark the whole block.
|
|
978
|
+
- **Simulated writes.** `create`, `update`, `delete`, `operation` and `collectionOperation` are answered in the
|
|
979
|
+
browser, in live and in preview, and forgotten on reload. A block that writes says
|
|
980
|
+
`Voorbeeld — wordt niet opgeslagen`. A sorted list places created rows, rows that join it and rows changed in a
|
|
981
|
+
sort field into the server's order when its rows carry the sort field; without a sort, created rows come first on
|
|
982
|
+
page 1. They also work signed out, and the preview does not check them against `uses`: a wireframe shows the idea,
|
|
983
|
+
not the access rules. A wireframe never writes real data: that covers everything that
|
|
984
|
+
goes through the session (`session.resource()` and the blocks). App code that builds its own client with the exported
|
|
985
|
+
`createClient` bypasses it.
|
|
986
|
+
- **Numbers.** KPI and chart numbers do not reflect simulated writes: they come from the rows the session holds
|
|
987
|
+
without them.
|
|
988
|
+
- **`cube-gap-list`.** One card per gap, for the people who build the real thing: what is missing
|
|
989
|
+
(`resource.veld`) as its heading, then Scherm, Soort gegeven and Waarom. It stays readable at 320 px.
|
|
990
|
+
`scherm="Lijst"` shows only that screen's gaps; without gaps it says nothing is missing.
|
|
991
|
+
|
|
992
|
+
```html
|
|
993
|
+
<cube-page name="wat-ontbreekt" label="Wat ontbreekt">
|
|
994
|
+
<cube-gap-list></cube-gap-list>
|
|
995
|
+
</cube-page>
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
`npm run visual` serves the pages with the hosted-app CSP; add `?wireframe=1` to see a wireframe with two gaps.
|
|
999
|
+
|
|
1000
|
+
### CSP
|
|
1001
|
+
|
|
1002
|
+
The blocks run under `default-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; script-src 'self'`,
|
|
1003
|
+
the policy of a hosted app. Therefore: no inline `<script>` or `<style>`, no `style` attribute, no `innerHTML`;
|
|
1004
|
+
styles are made with `CSSStyleSheet.replaceSync` and `adoptedStyleSheets`. The test suite guards this (a grep of
|
|
1005
|
+
the bundle and a walk through the rendered blocks). Your own page must follow the same rule: load `cube-sdk.js`
|
|
1006
|
+
and your module with `src`, and keep the logo, the fonts and the mock data on the same origin.
|
|
1007
|
+
|
|
1008
|
+
To look at the blocks under that policy, run `npm run visual` and open `http://127.0.0.1:4173/`,
|
|
1009
|
+
`/?huisstijl=b` and `/?huisstijl=geen`. The server (`scripts/serve-visual.mjs`, bound to 127.0.0.1) sends the
|
|
1010
|
+
policy above on every response and serves only `test/visual/`, `dist/cube-sdk.js`, the test mock and the fonts.
|
|
1011
|
+
|
|
649
1012
|
## What this SDK deliberately does not do
|
|
650
1013
|
|
|
651
1014
|
- **No per-resource generated types.** `_capabilities` is per token and at runtime, and OpenAPI
|
|
@@ -679,7 +1042,7 @@ dev dependencies; there are no runtime dependencies, and there should not be.
|
|
|
679
1042
|
### Releasing
|
|
680
1043
|
|
|
681
1044
|
Bump `version` in `package.json` and `src/version.ts` (a test holds them equal), add the CHANGELOG
|
|
682
|
-
entry, merge, then push a tag that equals the version (e.g. `1.
|
|
1045
|
+
entry, merge, then push a tag that equals the version (e.g. `1.1.0-rc.1`). The tag pipeline's
|
|
683
1046
|
`publish` job checks that, builds, and runs `npm publish`: a version with a `-` goes to the `next`
|
|
684
1047
|
dist-tag, any other to `latest`. It needs the masked, protected CI variable `NPM_TOKEN` (an npm
|
|
685
1048
|
granular access token with write access to the `cube-nl` org).
|
package/dist/app.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { type AuthorizationServerMetadata } from './auth.js';
|
|
|
2
2
|
import { type ResourceClient } from './client.js';
|
|
3
3
|
import { type AppManifest, type MockData } from './preview.js';
|
|
4
4
|
import type { DmpRecord } from './types.js';
|
|
5
|
+
import { type ResourceSchema } from './ui/schema.js';
|
|
6
|
+
import { type ThemeFile, type ThemeSource } from './ui/theme.js';
|
|
7
|
+
import { type GapEntry } from './wireframe.js';
|
|
5
8
|
/**
|
|
6
9
|
* `cube-app.config.json`, which the DMP writes next to the app only when it publishes an approved
|
|
7
10
|
* release. Never part of what the maker or an AI sends: its presence is what makes an app live.
|
|
@@ -21,6 +24,8 @@ export interface CubeAppConfig {
|
|
|
21
24
|
issuer?: string;
|
|
22
25
|
/** Passed to `createOAuth`: endpoints to use instead of (or on top of) discovery. */
|
|
23
26
|
metadata?: Partial<AuthorizationServerMetadata>;
|
|
27
|
+
/** `'wireframe'`: what exists runs live, what `ontbreekt` in cube-app.json lists comes from `cube-mock.json`. */
|
|
28
|
+
soort?: string;
|
|
24
29
|
}
|
|
25
30
|
export interface ConnectOptions {
|
|
26
31
|
/** Preview data: `cube.mock_data`'s output. Default: what `CubeApp.mock()` registered, else `./cube-mock.json`. */
|
|
@@ -40,6 +45,11 @@ export interface ConnectOptions {
|
|
|
40
45
|
tokenStorage?: 'session' | 'memory';
|
|
41
46
|
/** Live only: go to the DMP's sign-in right away when nobody is signed in. Default false. */
|
|
42
47
|
autoSignIn?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The house style. Default: what `CubeApp.theme()` registered, else `./cube-theme.json`, else the
|
|
50
|
+
* neutral theme. `false`: neutral, and `./cube-theme.json` is not read.
|
|
51
|
+
*/
|
|
52
|
+
theme?: ThemeFile | ThemeSource | false;
|
|
43
53
|
}
|
|
44
54
|
export interface CubeAppUser {
|
|
45
55
|
/** The DMP's user id (the token's `sub`), or `'preview'`. Null when the token does not say. */
|
|
@@ -50,8 +60,11 @@ export interface CubeAppUser {
|
|
|
50
60
|
scopes: string[];
|
|
51
61
|
}
|
|
52
62
|
export interface CubeAppSession {
|
|
53
|
-
/**
|
|
54
|
-
|
|
63
|
+
/**
|
|
64
|
+
* `'preview'`: mock data in this browser, no DMP. `'live'`: the real DMP, as the signed-in person. `'trial'`: the
|
|
65
|
+
* maker's own data, read-only, with the token the DMP wrote into the page.
|
|
66
|
+
*/
|
|
67
|
+
readonly mode: 'preview' | 'live' | 'trial';
|
|
55
68
|
/** Who is signed in, or null. In the preview a stand-in user is signed in from the start. */
|
|
56
69
|
user(): CubeAppUser | null;
|
|
57
70
|
/** Live: navigates to the DMP's sign-in (the page is gone after this). Preview: signs the stand-in in again. */
|
|
@@ -62,6 +75,16 @@ export interface CubeAppSession {
|
|
|
62
75
|
resource<T extends DmpRecord = DmpRecord>(name: string): ResourceClient<T>;
|
|
63
76
|
/** Why the last sign-in came back without a session (consent refused, say); null otherwise. */
|
|
64
77
|
readonly signInError: Error | null;
|
|
78
|
+
/** One resource as a block sees it: fields, actions, what may be filtered, sorted, grouped. Null when unknown. */
|
|
79
|
+
schema(resource: string): Promise<ResourceSchema | null>;
|
|
80
|
+
/** What the app is: live the config's `soort`, in the preview the manifest's. Null when neither says. */
|
|
81
|
+
readonly soort: string | null;
|
|
82
|
+
/** A wireframe's gap entries: what it shows that the DMP does not have yet. Empty for any other app. */
|
|
83
|
+
readonly gaps: readonly GapEntry[];
|
|
84
|
+
/** Whether a wireframe shows invented data here: the whole resource, or this field of it, does not exist yet. */
|
|
85
|
+
isExample(resource: string, field?: string): boolean;
|
|
86
|
+
/** A wireframe: writes are answered in the browser and never saved. */
|
|
87
|
+
readonly simulatesWrites: boolean;
|
|
65
88
|
}
|
|
66
89
|
/**
|
|
67
90
|
* Starts the app. Reads `./cube-app.config.json` next to the page: absent, the app runs in the
|
|
@@ -69,17 +92,31 @@ export interface CubeAppSession {
|
|
|
69
92
|
* OAuth (authorization code + PKCE) and talks to the DMP's REST face. The app's code is the same.
|
|
70
93
|
*/
|
|
71
94
|
declare function connect(options?: ConnectOptions): Promise<CubeAppSession>;
|
|
95
|
+
/**
|
|
96
|
+
* The session `connect()` made for this page, or one with the defaults when the app never connected. The default one
|
|
97
|
+
* starts only once the page has loaded (and one task later), so an app that awaits something before it calls
|
|
98
|
+
* `connect()` still gets there first and the page signs in once.
|
|
99
|
+
*/
|
|
100
|
+
declare function ready(): Promise<CubeAppSession>;
|
|
72
101
|
/**
|
|
73
102
|
* Registers preview data before `connect()`, for an app that embeds its mock data in a script tag
|
|
74
103
|
* instead of shipping `cube-mock.json`. Has no effect on a live app. `null` forgets it again.
|
|
75
104
|
*/
|
|
76
105
|
declare function mock(data: MockData | null): void;
|
|
106
|
+
/**
|
|
107
|
+
* Registers the house style before `connect()`, for an app that holds it in code instead of shipping
|
|
108
|
+
* `cube-theme.json`, and applies it right away. `null` forgets it again (neutral).
|
|
109
|
+
*/
|
|
110
|
+
declare function theme(input: ThemeFile | ThemeSource | null): void;
|
|
77
111
|
/** What `cube-sdk.js` puts on `window`, and what this package exports as `CubeApp`. */
|
|
78
112
|
export declare const CubeApp: Readonly<{
|
|
79
113
|
connect: typeof connect;
|
|
114
|
+
/** The session the app connected, or one connected with the defaults. What blocks use. */
|
|
115
|
+
ready: typeof ready;
|
|
80
116
|
mock: typeof mock;
|
|
81
|
-
|
|
82
|
-
version
|
|
117
|
+
theme: typeof theme;
|
|
118
|
+
/** This SDK's version, e.g. `'1.1.0-rc.2'`. */
|
|
119
|
+
version: "1.1.0-rc.2";
|
|
83
120
|
/** The manifest `"sdk"` value this build serves. */
|
|
84
121
|
sdk: "1";
|
|
85
122
|
}>;
|