@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/dist/app.js
CHANGED
|
@@ -2,34 +2,171 @@ import { createOAuth, memoryTokenStore, } from './auth.js';
|
|
|
2
2
|
import { createClient } from './client.js';
|
|
3
3
|
import { DmpError } from './errors.js';
|
|
4
4
|
import { PreviewStore } from './preview.js';
|
|
5
|
+
import { schemaFromCapability } from './ui/schema.js';
|
|
6
|
+
import { PREVIEW_USER_NAME } from './ui/texts.js';
|
|
7
|
+
import { resolveTheme, setTheme } from './ui/theme.js';
|
|
8
|
+
import { trialConfig } from './trial.js';
|
|
5
9
|
import { MANIFEST_SDK, VERSION } from './version.js';
|
|
10
|
+
import { mixedResource, readGaps, simulatedWrites, withGaps } from './wireframe.js';
|
|
6
11
|
const CONFIG_FILE = './cube-app.config.json';
|
|
7
12
|
const MOCK_FILE = './cube-mock.json';
|
|
8
13
|
const MANIFEST_FILE = './cube-app.json';
|
|
9
|
-
const
|
|
14
|
+
const THEME_FILE = './cube-theme.json';
|
|
15
|
+
const PREVIEW_USER = { id: 'preview', name: PREVIEW_USER_NAME, scopes: [] };
|
|
10
16
|
let registeredMock = null;
|
|
17
|
+
let registeredTheme = null;
|
|
18
|
+
let pageSession = null;
|
|
19
|
+
/** The session `ready()` connected with the defaults, while it is the page session. */
|
|
20
|
+
let fallbackSession = null;
|
|
21
|
+
/** A `ready()` waiting for the page to load before it connects with the defaults. */
|
|
22
|
+
let waitingSession = null;
|
|
23
|
+
/** Counts start-ups: only the newest may apply its theme, so a slower, replaced one cannot overwrite it. */
|
|
24
|
+
let starts = 0;
|
|
11
25
|
/**
|
|
12
26
|
* Starts the app. Reads `./cube-app.config.json` next to the page: absent, the app runs in the
|
|
13
27
|
* preview on mock data and makes no request to any DMP; present, it signs the end user in with
|
|
14
28
|
* OAuth (authorization code + PKCE) and talks to the DMP's REST face. The app's code is the same.
|
|
15
29
|
*/
|
|
16
|
-
|
|
30
|
+
function connect(options = {}) {
|
|
31
|
+
const replacesFallback = pageSession !== null && pageSession === fallbackSession;
|
|
32
|
+
const generation = ++starts;
|
|
33
|
+
const session = start(options, () => generation === starts);
|
|
34
|
+
pageSession = session;
|
|
35
|
+
fallbackSession = null;
|
|
36
|
+
// A failed start is not the page session: a later ready() tries again.
|
|
37
|
+
session.then(() => {
|
|
38
|
+
// Blocks took the default session already: tell them to take the app's own.
|
|
39
|
+
if (replacesFallback && pageSession === session)
|
|
40
|
+
announceSessionChange();
|
|
41
|
+
}, () => {
|
|
42
|
+
if (pageSession === session) {
|
|
43
|
+
pageSession = null;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return session;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The session `connect()` made for this page, or one with the defaults when the app never connected. The default one
|
|
50
|
+
* starts only once the page has loaded (and one task later), so an app that awaits something before it calls
|
|
51
|
+
* `connect()` still gets there first and the page signs in once.
|
|
52
|
+
*/
|
|
53
|
+
function ready() {
|
|
54
|
+
if (pageSession !== null) {
|
|
55
|
+
return pageSession;
|
|
56
|
+
}
|
|
57
|
+
waitingSession ??= pageLoaded().then(() => {
|
|
58
|
+
waitingSession = null;
|
|
59
|
+
if (pageSession !== null) {
|
|
60
|
+
return pageSession;
|
|
61
|
+
}
|
|
62
|
+
const session = connect();
|
|
63
|
+
fallbackSession = session;
|
|
64
|
+
return session;
|
|
65
|
+
});
|
|
66
|
+
return waitingSession;
|
|
67
|
+
}
|
|
68
|
+
/** Resolves one task after the window's `load` (right away outside a page). */
|
|
69
|
+
function pageLoaded() {
|
|
70
|
+
const page = globalThis;
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
const later = () => {
|
|
73
|
+
setTimeout(resolve, 0);
|
|
74
|
+
};
|
|
75
|
+
const loaded = page.document === undefined || page.document.readyState === 'complete';
|
|
76
|
+
if (loaded || page.addEventListener === undefined) {
|
|
77
|
+
later();
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
page.addEventListener('load', later, { once: true });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function announceSessionChange() {
|
|
85
|
+
const doc = globalThis.document;
|
|
86
|
+
if (doc === undefined) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const View = doc.defaultView?.CustomEvent ?? CustomEvent;
|
|
90
|
+
doc.dispatchEvent(new View('cube-session-change'));
|
|
91
|
+
}
|
|
92
|
+
async function start(options, isCurrent) {
|
|
17
93
|
const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
18
94
|
const pageUrl = options.pageUrl ?? currentPage();
|
|
19
|
-
|
|
95
|
+
// A trial page never has cube-app.config.json and never signs in with OAuth: the DMP wrote the token into it.
|
|
96
|
+
const trial = trialConfig();
|
|
97
|
+
const [config, theme] = await Promise.all([
|
|
98
|
+
trial === null ? readJson(doFetch, resolve(CONFIG_FILE, pageUrl)) : null,
|
|
99
|
+
loadTheme(options, doFetch, pageUrl),
|
|
100
|
+
]);
|
|
101
|
+
if (isCurrent()) {
|
|
102
|
+
setTheme(theme);
|
|
103
|
+
}
|
|
104
|
+
if (trial !== null) {
|
|
105
|
+
return trialSession(trial, options, doFetch, pageUrl);
|
|
106
|
+
}
|
|
20
107
|
if (config === null) {
|
|
21
108
|
return preview(options, doFetch, pageUrl);
|
|
22
109
|
}
|
|
23
110
|
return live(asConfig(config), options, doFetch, pageUrl);
|
|
24
111
|
}
|
|
25
|
-
|
|
26
|
-
|
|
112
|
+
/** Never rejects: a broken theme file must not stop the app, it only costs the house style. */
|
|
113
|
+
async function loadTheme(options, doFetch, pageUrl) {
|
|
114
|
+
try {
|
|
115
|
+
if (options.theme === false) {
|
|
116
|
+
return resolveTheme(null, pageUrl);
|
|
117
|
+
}
|
|
118
|
+
const given = options.theme ?? registeredTheme;
|
|
119
|
+
if (given !== null) {
|
|
120
|
+
return resolveTheme(given, pageUrl);
|
|
121
|
+
}
|
|
122
|
+
const file = await readJson(doFetch, resolve(THEME_FILE, pageUrl));
|
|
123
|
+
if (file !== null && (file.theme !== 1 || typeof file.source !== 'object' || file.source === null)) {
|
|
124
|
+
throw new TypeError('cube-theme.json is not a theme file.');
|
|
125
|
+
}
|
|
126
|
+
return resolveTheme(file, pageUrl);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.warn(`cube-theme.json could not be used, the neutral theme applies: ${String(error)}`);
|
|
130
|
+
return resolveTheme(null, pageUrl);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** The mock data and the manifest, from the options, `CubeApp.mock()` or the files next to the page. */
|
|
134
|
+
async function appFiles(options, doFetch, pageUrl) {
|
|
135
|
+
const mock = options.mock
|
|
136
|
+
?? registeredMock
|
|
137
|
+
?? (await readJson(doFetch, resolve(MOCK_FILE, pageUrl)))
|
|
138
|
+
?? {};
|
|
27
139
|
const manifest = options.manifest === false
|
|
28
140
|
? null
|
|
29
141
|
: options.manifest ?? (await readJson(doFetch, resolve(MANIFEST_FILE, pageUrl)));
|
|
142
|
+
return [mock, manifest];
|
|
143
|
+
}
|
|
144
|
+
/** Only a wireframe has gaps: any other app marks nothing, whatever its manifest lists. */
|
|
145
|
+
function marks(soort, manifest) {
|
|
146
|
+
const wireframe = soort === 'wireframe';
|
|
147
|
+
const gaps = wireframe ? readGaps(manifest?.ontbreekt) : [];
|
|
148
|
+
return {
|
|
149
|
+
soort: soort ?? null,
|
|
150
|
+
gaps,
|
|
151
|
+
simulatesWrites: wireframe,
|
|
152
|
+
isExample: (resource, field) => gaps.some((gap) => gap.resource === resource && (gap.veld === undefined || gap.veld === field)),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/** A wireframe answers every write in the browser: one simulating client per resource, kept as long as the session. */
|
|
156
|
+
function writes(on, open) {
|
|
157
|
+
const clients = new Map();
|
|
158
|
+
const simulating = (name) => {
|
|
159
|
+
const client = clients.get(name) ?? simulatedWrites(open(name), name);
|
|
160
|
+
clients.set(name, client);
|
|
161
|
+
return client;
|
|
162
|
+
};
|
|
163
|
+
return (on ? simulating : open);
|
|
164
|
+
}
|
|
165
|
+
async function preview(options, doFetch, pageUrl) {
|
|
166
|
+
const [mock, manifest] = await appFiles(options, doFetch, pageUrl);
|
|
30
167
|
let signedIn = true;
|
|
31
168
|
// Signed out, the preview refuses as the API refuses a request without a token.
|
|
32
|
-
const
|
|
169
|
+
const guard = () => {
|
|
33
170
|
if (!signedIn) {
|
|
34
171
|
throw new DmpError(401, new Headers({ 'Content-Type': 'application/problem+json' }), {
|
|
35
172
|
type: 'https://docs.cube.nl/dmp-api/problems/token_invalid',
|
|
@@ -39,8 +176,14 @@ async function preview(options, doFetch, pageUrl) {
|
|
|
39
176
|
detail: 'Unauthenticated.',
|
|
40
177
|
});
|
|
41
178
|
}
|
|
42
|
-
}
|
|
179
|
+
};
|
|
180
|
+
const marked = marks(manifest?.soort, manifest);
|
|
181
|
+
const ceiling = new PreviewStore(mock, manifest, guard);
|
|
182
|
+
// As live: a resource the DMP does not have yet is not in `uses`, so its rows come without that ceiling.
|
|
183
|
+
const examples = new PreviewStore(mock, null, guard);
|
|
184
|
+
const store = (name) => (marked.isExample(name) ? examples : ceiling);
|
|
43
185
|
return {
|
|
186
|
+
...marked,
|
|
44
187
|
mode: 'preview',
|
|
45
188
|
signInError: null,
|
|
46
189
|
user: () => (signedIn ? { ...PREVIEW_USER } : null),
|
|
@@ -50,7 +193,56 @@ async function preview(options, doFetch, pageUrl) {
|
|
|
50
193
|
async signOut() {
|
|
51
194
|
signedIn = false;
|
|
52
195
|
},
|
|
53
|
-
resource: (name) =>
|
|
196
|
+
resource: writes(marked.simulatesWrites, (name) => {
|
|
197
|
+
const own = store(name);
|
|
198
|
+
const client = own.resource(name);
|
|
199
|
+
const known = own.describe(name)?.fields ?? [];
|
|
200
|
+
// A missing field the mock data does not hold either is invented, as live: naming it would be a 422.
|
|
201
|
+
const absent = marked.gaps.filter(({ resource, veld }) => resource === name && veld !== undefined && !known.some((field) => field.name === veld));
|
|
202
|
+
return absent.length > 0 ? mixedResource(client, absent, examples, examples.rows(name)) : client;
|
|
203
|
+
}),
|
|
204
|
+
schema: async (name) => withGaps(store(name).describe(name), marked.gaps),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** A wireframe's marks and its mock data. Only a wireframe reads the mock: an ordinary app never fetches it. */
|
|
208
|
+
async function wireframeParts(soort, options, doFetch, pageUrl) {
|
|
209
|
+
const [mock, manifest] = soort === 'wireframe' ? await appFiles(options, doFetch, pageUrl) : [{}, null];
|
|
210
|
+
// The manifest's ceiling is the live client's: a missing resource is not in it, so the store checks none.
|
|
211
|
+
return { marked: marks(soort, manifest), examples: new PreviewStore(mock) };
|
|
212
|
+
}
|
|
213
|
+
/** The session on the DMP, live or trial: a wireframe mixes in its gaps, everything else goes to the client. */
|
|
214
|
+
function onDmp(connection, { marked, examples }) {
|
|
215
|
+
const { client } = connection;
|
|
216
|
+
let capabilities = null;
|
|
217
|
+
return {
|
|
218
|
+
...marked,
|
|
219
|
+
mode: connection.mode,
|
|
220
|
+
get signInError() {
|
|
221
|
+
return connection.signInError();
|
|
222
|
+
},
|
|
223
|
+
user: connection.user,
|
|
224
|
+
signIn: connection.signIn,
|
|
225
|
+
signOut: connection.signOut,
|
|
226
|
+
resource: writes(marked.simulatesWrites, (name) => marked.isExample(name)
|
|
227
|
+
? examples.resource(name)
|
|
228
|
+
: marked.gaps.some((gap) => gap.resource === name)
|
|
229
|
+
? mixedResource(client.resources(name), marked.gaps, examples, examples.rows(name))
|
|
230
|
+
: client.resources(name)),
|
|
231
|
+
async schema(name) {
|
|
232
|
+
if (marked.isExample(name)) {
|
|
233
|
+
return withGaps(examples.describe(name), marked.gaps);
|
|
234
|
+
}
|
|
235
|
+
capabilities ??= client.capabilities();
|
|
236
|
+
try {
|
|
237
|
+
const found = (await capabilities).resources.find((resource) => resource.name === name);
|
|
238
|
+
return found === undefined ? null : withGaps(schemaFromCapability(found), marked.gaps);
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
// A failed call (signed out, offline) must not stay cached for the whole session.
|
|
242
|
+
capabilities = null;
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
},
|
|
54
246
|
};
|
|
55
247
|
}
|
|
56
248
|
async function live(config, options, doFetch, pageUrl) {
|
|
@@ -70,6 +262,7 @@ async function live(config, options, doFetch, pageUrl) {
|
|
|
70
262
|
});
|
|
71
263
|
const client = createClient({ baseUrl: config.apiUrl ?? `${dmpUrl}/apps/api/v1`, auth: oauth, fetch: doFetch });
|
|
72
264
|
let signInError = null;
|
|
265
|
+
const parts = await wireframeParts(config.soort, options, doFetch, pageUrl);
|
|
73
266
|
tokens = await store.get();
|
|
74
267
|
try {
|
|
75
268
|
await oauth.handleCallback();
|
|
@@ -81,11 +274,10 @@ async function live(config, options, doFetch, pageUrl) {
|
|
|
81
274
|
if (tokens === null && signInError === null && options.autoSignIn === true) {
|
|
82
275
|
await oauth.signIn();
|
|
83
276
|
}
|
|
84
|
-
return {
|
|
277
|
+
return onDmp({
|
|
85
278
|
mode: 'live',
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
279
|
+
client,
|
|
280
|
+
signInError: () => signInError,
|
|
89
281
|
user: () => (tokens === null ? null : userFrom(tokens)),
|
|
90
282
|
async signIn() {
|
|
91
283
|
signInError = null;
|
|
@@ -94,9 +286,44 @@ async function live(config, options, doFetch, pageUrl) {
|
|
|
94
286
|
async signOut() {
|
|
95
287
|
await oauth.signOut();
|
|
96
288
|
},
|
|
97
|
-
|
|
289
|
+
}, parts);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* A trial reads only. A write never leaves the page (the browser would block it at the CORS preflight and the app would
|
|
293
|
+
* see a TypeError): it answers the 403 the DMP would, so it surfaces as a DmpError like any refusal.
|
|
294
|
+
*/
|
|
295
|
+
function readOnly(doFetch) {
|
|
296
|
+
return async (input, init) => {
|
|
297
|
+
const method = (init?.method ?? 'GET').toUpperCase();
|
|
298
|
+
if (method === 'GET' || method === 'HEAD') {
|
|
299
|
+
return doFetch(input, init);
|
|
300
|
+
}
|
|
301
|
+
return new Response(JSON.stringify({ status: 403, code: 'insufficient_scope' }), {
|
|
302
|
+
status: 403,
|
|
303
|
+
headers: { 'Content-Type': 'application/problem+json' },
|
|
304
|
+
});
|
|
98
305
|
};
|
|
99
306
|
}
|
|
307
|
+
/** The live session with the injected token instead of OAuth: no discovery, no callback, no storage. */
|
|
308
|
+
async function trialSession(config, options, doFetch, pageUrl) {
|
|
309
|
+
let token = config.token;
|
|
310
|
+
// Read on every request: once signed out no token goes out, so the DMP answers 401.
|
|
311
|
+
const client = createClient({ baseUrl: config.apiUrl, auth: () => token, fetch: readOnly(doFetch) });
|
|
312
|
+
const parts = await wireframeParts(config.soort ?? undefined, options, doFetch, pageUrl);
|
|
313
|
+
return onDmp({
|
|
314
|
+
mode: 'trial',
|
|
315
|
+
client,
|
|
316
|
+
signInError: () => null,
|
|
317
|
+
user: () => (token === null ? null : { id: config.userId, name: null, scopes: ['api:read'] }),
|
|
318
|
+
// The DMP writes a token into the page on every load: a reload is the sign-in.
|
|
319
|
+
async signIn() {
|
|
320
|
+
globalThis.location?.reload();
|
|
321
|
+
},
|
|
322
|
+
async signOut() {
|
|
323
|
+
token = null;
|
|
324
|
+
},
|
|
325
|
+
}, parts);
|
|
326
|
+
}
|
|
100
327
|
/**
|
|
101
328
|
* Who the token belongs to, read from the access token itself (cube/oauth-server issues JWTs).
|
|
102
329
|
* For display only: the DMP checks the token on every request, this SDK does not verify it.
|
|
@@ -234,11 +461,22 @@ function resolve(path, pageUrl) {
|
|
|
234
461
|
function mock(data) {
|
|
235
462
|
registeredMock = data;
|
|
236
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Registers the house style before `connect()`, for an app that holds it in code instead of shipping
|
|
466
|
+
* `cube-theme.json`, and applies it right away. `null` forgets it again (neutral).
|
|
467
|
+
*/
|
|
468
|
+
function theme(input) {
|
|
469
|
+
registeredTheme = input;
|
|
470
|
+
setTheme(resolveTheme(input, currentPage()));
|
|
471
|
+
}
|
|
237
472
|
/** What `cube-sdk.js` puts on `window`, and what this package exports as `CubeApp`. */
|
|
238
473
|
export const CubeApp = Object.freeze({
|
|
239
474
|
connect,
|
|
475
|
+
/** The session the app connected, or one connected with the defaults. What blocks use. */
|
|
476
|
+
ready,
|
|
240
477
|
mock,
|
|
241
|
-
|
|
478
|
+
theme,
|
|
479
|
+
/** This SDK's version, e.g. `'1.1.0-rc.2'`. */
|
|
242
480
|
version: VERSION,
|
|
243
481
|
/** The manifest `"sdk"` value this build serves. */
|
|
244
482
|
sdk: MANIFEST_SDK,
|
package/dist/browser.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
import { CubeApp } from './app.js';
|
|
1
|
+
import { CubeApp as Core } from './app.js';
|
|
2
|
+
import { trialConfig } from './trial.js';
|
|
3
|
+
import { defineBlocks } from './ui/define.js';
|
|
4
|
+
import { showTrialBanner } from './ui/trial-banner.js';
|
|
2
5
|
// The entry of dist/cube-sdk.js: one classic script, no imports, `window.CubeApp` for a page without a build step.
|
|
6
|
+
const CubeApp = Object.freeze({ ...Core, defineBlocks });
|
|
3
7
|
globalThis.CubeApp = CubeApp;
|
|
8
|
+
// An engine without custom elements (an old browser, a script run outside a page) keeps the plain CubeApp.
|
|
9
|
+
if (typeof customElements !== 'undefined')
|
|
10
|
+
defineBlocks();
|
|
11
|
+
// A trial page (the config was read when trial.ts evaluated): say so at the top, and follow the trial's status.
|
|
12
|
+
const trial = trialConfig();
|
|
13
|
+
const page = globalThis;
|
|
14
|
+
if (trial !== null && page.document !== undefined && page.CSSStyleSheet !== undefined) {
|
|
15
|
+
showTrialBanner(page, trial);
|
|
16
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface CreateOptions {
|
|
|
37
37
|
export interface WriteOptions {
|
|
38
38
|
/** A `recordVersion` from an earlier response (never the ETag). A stale one answers 409 `conflict`. */
|
|
39
39
|
ifMatch?: string;
|
|
40
|
+
/** One key per save attempt (`newIdempotencyKey()`); send it again only to retry that same body. */
|
|
41
|
+
idempotencyKey?: string;
|
|
40
42
|
fields?: readonly string[];
|
|
41
43
|
}
|
|
42
44
|
export interface OperationOptions {
|
package/dist/client.js
CHANGED
|
@@ -99,7 +99,11 @@ export function createClient(options) {
|
|
|
99
99
|
body: input ?? {},
|
|
100
100
|
headers: writeHeaders(opts),
|
|
101
101
|
});
|
|
102
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
...response,
|
|
104
|
+
data: response.status === 204 ? null : response.data,
|
|
105
|
+
accepted: response.status === 202,
|
|
106
|
+
};
|
|
103
107
|
};
|
|
104
108
|
return {
|
|
105
109
|
name,
|