@cube-nl/dmp-sdk 1.1.0-rc.1 → 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 +97 -26
- package/dist/app.d.ts +7 -4
- package/dist/app.js +89 -33
- package/dist/browser.js +8 -0
- package/dist/client.js +5 -1
- package/dist/cube-sdk.js +28 -21
- package/dist/cube-ui.manifest.json +3 -3
- package/dist/preview.d.ts +2 -0
- package/dist/preview.js +21 -6
- package/dist/size.json +3 -3
- package/dist/trial.d.ts +32 -0
- package/dist/trial.js +55 -0
- package/dist/ui/aggregate-block.js +2 -1
- package/dist/ui/author-error.d.ts +3 -0
- package/dist/ui/author-error.js +3 -0
- package/dist/ui/block.d.ts +8 -2
- package/dist/ui/block.js +14 -5
- package/dist/ui/blocks/app-shell.js +1 -1
- package/dist/ui/blocks/board.js +55 -14
- package/dist/ui/blocks/calc.js +35 -12
- package/dist/ui/blocks/kpi.js +2 -1
- package/dist/ui/blocks/table.js +82 -23
- package/dist/ui/dom.js +2 -1
- package/dist/ui/format.js +3 -2
- package/dist/ui/manifest.js +5 -3
- package/dist/ui/query.js +7 -6
- package/dist/ui/texts.d.ts +12 -0
- package/dist/ui/texts.js +16 -0
- package/dist/ui/trial-banner.d.ts +25 -0
- package/dist/ui/trial-banner.js +180 -0
- package/dist/ui/write.d.ts +2 -2
- package/dist/ui/write.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wireframe.js +53 -16
- package/package.json +5 -2
package/dist/wireframe.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DmpError } from './errors.js';
|
|
2
|
-
import { PreviewStore } from './preview.js';
|
|
2
|
+
import { compare, PreviewStore } from './preview.js';
|
|
3
3
|
import { isoDate } from './ui/format.js';
|
|
4
4
|
const FORMATS = {
|
|
5
5
|
tekst: 'text',
|
|
@@ -11,6 +11,8 @@ const FORMATS = {
|
|
|
11
11
|
};
|
|
12
12
|
const filled = (value) => typeof value === 'string' && value !== '';
|
|
13
13
|
const own = (row, key) => Object.prototype.hasOwnProperty.call(row, key);
|
|
14
|
+
// `a, -b` and ['a', '-b'] alike: the items of a comma list, as the server reads it.
|
|
15
|
+
const items = (value = []) => [value].flat().join(',').split(',').map((item) => item.trim());
|
|
14
16
|
function isGap(value) {
|
|
15
17
|
if (typeof value !== 'object' || value === null) {
|
|
16
18
|
return false;
|
|
@@ -29,11 +31,12 @@ function isGap(value) {
|
|
|
29
31
|
}
|
|
30
32
|
/** The valid gap entries of `ontbreekt`; malformed ones are dropped with a single warning. */
|
|
31
33
|
export function readGaps(value) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const gaps = [];
|
|
35
|
+
const dropped = [];
|
|
36
|
+
// Anything but a list is one malformed entry: a typo must not silently mean "nothing is missing".
|
|
37
|
+
for (const entry of Array.isArray(value) ? value : value == null ? [] : [value]) {
|
|
38
|
+
(isGap(entry) ? gaps : dropped).push(entry);
|
|
34
39
|
}
|
|
35
|
-
const gaps = value.filter(isGap);
|
|
36
|
-
const dropped = value.filter((entry) => !isGap(entry));
|
|
37
40
|
if (dropped.length > 0) {
|
|
38
41
|
console.warn(`cube-app.json ontbreekt: ${dropped.length} malformed entries ignored`, dropped);
|
|
39
42
|
}
|
|
@@ -73,10 +76,11 @@ export function withGaps(schema, gaps) {
|
|
|
73
76
|
const names = added.map((field) => field.name);
|
|
74
77
|
return {
|
|
75
78
|
...schema,
|
|
76
|
-
//
|
|
79
|
+
// Money is not recognisable by its values, dates are (inferFormat): only an amount gets its format here, and a
|
|
80
|
+
// stale `datum` gap must not drop a live date-time's time.
|
|
77
81
|
fields: [...schema.fields.map((field) => {
|
|
78
82
|
const gap = gaps.find(({ resource, veld }) => resource === schema.name && veld === field.name);
|
|
79
|
-
return gap
|
|
83
|
+
return gap?.type === 'bedrag' && field.format == null ? { ...field, format: 'currency' } : field;
|
|
80
84
|
}), ...added],
|
|
81
85
|
// An empty summary means "every field of a row", which already holds the invented ones.
|
|
82
86
|
summary: schema.summary.length > 0 ? [...schema.summary, ...names] : schema.summary,
|
|
@@ -141,13 +145,14 @@ async function locally(name, rows, { filter = {}, sort = [] }) {
|
|
|
141
145
|
export function mixedResource(live, gaps, mock, mockRows) {
|
|
142
146
|
const { name } = live;
|
|
143
147
|
const missing = new Map(gaps.filter((gap) => gap.resource === name && gap.veld !== undefined).map((gap) => [gap.veld, gap]));
|
|
144
|
-
// `-field`, `sum(field)` and `field.month`
|
|
145
|
-
|
|
148
|
+
// `-field`, `sum(field)` and `field.month` name `field`; `count` and `created_at.month` do not name a field
|
|
149
|
+
// called `count` or `month`.
|
|
150
|
+
const names = (item) => missing.has(/^[-+]?(?:\w+\()?([^.()]*)/.exec(item)?.[1] ?? '');
|
|
146
151
|
const split = (query = {}) => {
|
|
147
152
|
const selected = selection(query, name);
|
|
148
153
|
const kept = selected?.filter((field) => !missing.has(field));
|
|
149
154
|
const filters = Object.entries(query.filter ?? {});
|
|
150
|
-
const sort =
|
|
155
|
+
const sort = items(query.sort).filter((item) => item !== '');
|
|
151
156
|
// The live part names no missing field; the local part only missing ones.
|
|
152
157
|
const part = (local) => ({
|
|
153
158
|
filter: Object.fromEntries(filters.filter(([key]) => missing.has(key) === local)),
|
|
@@ -181,7 +186,15 @@ export function mixedResource(live, gaps, mock, mockRows) {
|
|
|
181
186
|
}
|
|
182
187
|
}
|
|
183
188
|
}
|
|
184
|
-
|
|
189
|
+
const { meta } = page;
|
|
190
|
+
return {
|
|
191
|
+
...page,
|
|
192
|
+
data: data,
|
|
193
|
+
// A filter on a missing field covers this page only, so the count and the last page say so too.
|
|
194
|
+
meta: Object.keys(local.filter).length > 0
|
|
195
|
+
? { ...meta, page: { ...meta.page, total: data.length, pages: meta.page.number } }
|
|
196
|
+
: meta,
|
|
197
|
+
};
|
|
185
198
|
};
|
|
186
199
|
const list = async (query, init) => {
|
|
187
200
|
const parts = split(query);
|
|
@@ -206,9 +219,15 @@ export function mixedResource(live, gaps, mock, mockRows) {
|
|
|
206
219
|
const parts = split(query);
|
|
207
220
|
const response = await live.get(id, parts.live, init);
|
|
208
221
|
fill(response.data, parts.shown);
|
|
222
|
+
// The id was only asked for the invented values.
|
|
223
|
+
if (!parts.shown('id')) {
|
|
224
|
+
delete response.data.id;
|
|
225
|
+
}
|
|
209
226
|
return response;
|
|
210
227
|
},
|
|
211
|
-
aggregate: (query, init) => [query.fn, query.group
|
|
228
|
+
aggregate: (query, init) => [items(query.fn).map((fn) => fn.replace(/^\w+$/, '')), items(query.group), Object.keys(query.filter ?? {})]
|
|
229
|
+
.flat()
|
|
230
|
+
.some(names)
|
|
212
231
|
? mock.resource(name).aggregate(query)
|
|
213
232
|
: live.aggregate(query, init),
|
|
214
233
|
};
|
|
@@ -253,9 +272,27 @@ export function simulatedWrites(client, name) {
|
|
|
253
272
|
.map((row) => local.get(key(row)) ?? row);
|
|
254
273
|
const joined = fitting.filter((row) => !served.has(key(row)) && !listed.has(key(row)));
|
|
255
274
|
const total = page.total - [...served].filter((id) => !fits.has(id)).length + joined.length;
|
|
275
|
+
let data = page.number === 1 ? [...joined, ...kept] : kept;
|
|
276
|
+
// No sort reads as one empty field, which no row has: then nothing moves.
|
|
277
|
+
const sorts = items(query.sort)
|
|
278
|
+
.map((item) => [item.replace(/^[-+]/, ''), item.startsWith('-') ? -1 : 1]);
|
|
279
|
+
// A row written here that the server did not list (created, or joining the list) or that changed in a sort
|
|
280
|
+
// field is placed by the sort; the others keep the server's order, whose collation may differ from ours.
|
|
281
|
+
const placed = (row) => local.has(key(row))
|
|
282
|
+
&& (!listed.has(key(row)) || sorts.some(([field]) => origins.get(key(row))?.[field] !== row[field]));
|
|
283
|
+
const server = data.filter((row) => !placed(row));
|
|
284
|
+
// Only when the server's rows carry every sort field to compare on; a placed row without one sorts as empty.
|
|
285
|
+
if (sorts.every(([field]) => server.every((row) => own(row, field)))) {
|
|
286
|
+
for (const row of data.filter(placed)) {
|
|
287
|
+
// Before the first row (server or placed) it sorts before; -1 >>> 0 is past the end, so it appends.
|
|
288
|
+
const at = server.findIndex((other) => sorts.reduce((result, [field, sign]) => result || sign * compare(row[field], other[field]), 0) < 0);
|
|
289
|
+
server.splice(at >>> 0, 0, row);
|
|
290
|
+
}
|
|
291
|
+
data = server;
|
|
292
|
+
}
|
|
256
293
|
return {
|
|
257
294
|
...response,
|
|
258
|
-
data:
|
|
295
|
+
data: data.map((row) => pick(row, query)),
|
|
259
296
|
meta: { ...response.meta, page: { ...page, total } },
|
|
260
297
|
};
|
|
261
298
|
};
|
|
@@ -268,7 +305,7 @@ export function simulatedWrites(client, name) {
|
|
|
268
305
|
return copy === undefined ? client.get(id, query, init) : answer(200, pick(copy, query));
|
|
269
306
|
};
|
|
270
307
|
// The row as it is here; a real one's server copy is kept the first time it changes.
|
|
271
|
-
const
|
|
308
|
+
const current = async (id) => {
|
|
272
309
|
const { data } = await get(id);
|
|
273
310
|
if (!local.has(String(id))) {
|
|
274
311
|
origins.set(String(id), data);
|
|
@@ -299,14 +336,14 @@ export function simulatedWrites(client, name) {
|
|
|
299
336
|
return answer(201, { ...row });
|
|
300
337
|
},
|
|
301
338
|
async update(id, body) {
|
|
302
|
-
const data = await
|
|
339
|
+
const data = await current(id);
|
|
303
340
|
const row = { ...data, ...body, id: data.id };
|
|
304
341
|
local.set(String(id), row);
|
|
305
342
|
return answer(200, { ...row });
|
|
306
343
|
},
|
|
307
344
|
// As the server: an id that is not there is a 404, and a delete never lowers a count for nothing.
|
|
308
345
|
async delete(id) {
|
|
309
|
-
await
|
|
346
|
+
await current(id);
|
|
310
347
|
local.delete(String(id));
|
|
311
348
|
deleted.add(String(id));
|
|
312
349
|
return answer(204, null);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cube-nl/dmp-sdk",
|
|
3
|
-
"version": "1.1.0-rc.
|
|
3
|
+
"version": "1.1.0-rc.2",
|
|
4
4
|
"description": "Dependency-free TypeScript client for the Cube DMP REST API (cube/api), with OAuth 2.1 + PKCE, and CubeApp for no-build apps (preview on mock data, live on the DMP).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
"access": "public",
|
|
26
26
|
"tag": "next"
|
|
27
27
|
},
|
|
28
|
-
"sideEffects":
|
|
28
|
+
"sideEffects": [
|
|
29
|
+
"./dist/trial.js",
|
|
30
|
+
"./dist/cube-sdk.js"
|
|
31
|
+
],
|
|
29
32
|
"engines": {
|
|
30
33
|
"node": ">=20"
|
|
31
34
|
},
|