@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
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { DmpError } from './errors.js';
|
|
2
|
+
import { compare, PreviewStore } from './preview.js';
|
|
3
|
+
import { isoDate } from './ui/format.js';
|
|
4
|
+
const FORMATS = {
|
|
5
|
+
tekst: 'text',
|
|
6
|
+
getal: 'number',
|
|
7
|
+
bedrag: 'currency',
|
|
8
|
+
datum: 'date',
|
|
9
|
+
keuze: 'text',
|
|
10
|
+
'ja/nee': 'boolean',
|
|
11
|
+
};
|
|
12
|
+
const filled = (value) => typeof value === 'string' && value !== '';
|
|
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());
|
|
16
|
+
function isGap(value) {
|
|
17
|
+
if (typeof value !== 'object' || value === null) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const entry = value;
|
|
21
|
+
const waarden = entry.waarden;
|
|
22
|
+
return typeof entry.resource === 'string'
|
|
23
|
+
&& /^[a-z][a-z0-9_]*$/.test(entry.resource)
|
|
24
|
+
&& (entry.veld === undefined || filled(entry.veld))
|
|
25
|
+
&& typeof entry.type === 'string'
|
|
26
|
+
&& own(FORMATS, entry.type)
|
|
27
|
+
&& (waarden === undefined || (Array.isArray(waarden) && waarden.every((item) => typeof item === 'string')))
|
|
28
|
+
&& (entry.type !== 'keuze' || (Array.isArray(waarden) && waarden.length > 0))
|
|
29
|
+
&& filled(entry.scherm)
|
|
30
|
+
&& filled(entry.reden);
|
|
31
|
+
}
|
|
32
|
+
/** The valid gap entries of `ontbreekt`; malformed ones are dropped with a single warning. */
|
|
33
|
+
export function readGaps(value) {
|
|
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);
|
|
39
|
+
}
|
|
40
|
+
if (dropped.length > 0) {
|
|
41
|
+
console.warn(`cube-app.json ontbreekt: ${dropped.length} malformed entries ignored`, dropped);
|
|
42
|
+
}
|
|
43
|
+
return gaps;
|
|
44
|
+
}
|
|
45
|
+
export function gapFormat(type) {
|
|
46
|
+
return FORMATS[type];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A schema with the missing fields of its resource added, so a block that takes its columns from the schema shows
|
|
50
|
+
* them, and can filter and sort on them (the page only). They are writable: a wireframe's writes stay in the browser.
|
|
51
|
+
*/
|
|
52
|
+
export function withGaps(schema, gaps) {
|
|
53
|
+
if (schema === null) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// Only these two formats are carried by a schema field; the others follow from its type.
|
|
57
|
+
const shown = (type) => {
|
|
58
|
+
const format = gapFormat(type);
|
|
59
|
+
return format === 'date' || format === 'currency' ? format : null;
|
|
60
|
+
};
|
|
61
|
+
const added = gaps.flatMap(({ resource, veld, type, waarden, reden }) => {
|
|
62
|
+
if (resource !== schema.name || veld === undefined || schema.fields.some((field) => field.name === veld)) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const format = gapFormat(type);
|
|
66
|
+
return [{
|
|
67
|
+
name: veld,
|
|
68
|
+
type: format === 'boolean' ? format : format === 'number' || format === 'currency' ? 'number' : 'string',
|
|
69
|
+
format: shown(type),
|
|
70
|
+
nullable: true,
|
|
71
|
+
readonly: false,
|
|
72
|
+
enum: waarden ?? null,
|
|
73
|
+
description: reden,
|
|
74
|
+
}];
|
|
75
|
+
});
|
|
76
|
+
const names = added.map((field) => field.name);
|
|
77
|
+
return {
|
|
78
|
+
...schema,
|
|
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.
|
|
81
|
+
fields: [...schema.fields.map((field) => {
|
|
82
|
+
const gap = gaps.find(({ resource, veld }) => resource === schema.name && veld === field.name);
|
|
83
|
+
return gap?.type === 'bedrag' && field.format == null ? { ...field, format: 'currency' } : field;
|
|
84
|
+
}), ...added],
|
|
85
|
+
// An empty summary means "every field of a row", which already holds the invented ones.
|
|
86
|
+
summary: schema.summary.length > 0 ? [...schema.summary, ...names] : schema.summary,
|
|
87
|
+
filterable: [...schema.filterable, ...names],
|
|
88
|
+
sortable: [...schema.sortable, ...names],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// FNV-1a 32-bit: a stable hash, so the same row gets the same invented value on every render and reload.
|
|
92
|
+
function fnv1a(text) {
|
|
93
|
+
let hash = 0x811c9dc5;
|
|
94
|
+
for (let i = 0; i < text.length; i++) {
|
|
95
|
+
hash = Math.imul(hash ^ text.charCodeAt(i), 0x01000193) >>> 0;
|
|
96
|
+
}
|
|
97
|
+
return hash;
|
|
98
|
+
}
|
|
99
|
+
/** An example value for a missing field of row `id`: from mock rows that carry the field, else made up by type. */
|
|
100
|
+
export function inventValue(gap, id, mockRows) {
|
|
101
|
+
const { veld, type, waarden } = gap;
|
|
102
|
+
const h = fnv1a(`${gap.resource}.${veld}.${String(id)}`);
|
|
103
|
+
if (veld !== undefined) {
|
|
104
|
+
const rowsWithField = mockRows.filter((row) => own(row, veld) && row[veld] !== undefined);
|
|
105
|
+
if (rowsWithField.length > 0) {
|
|
106
|
+
return rowsWithField[h % rowsWithField.length]?.[veld];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
switch (type) {
|
|
110
|
+
case 'tekst':
|
|
111
|
+
return `Voorbeeld ${(h % 100) + 1}`;
|
|
112
|
+
case 'getal':
|
|
113
|
+
return h % 1000;
|
|
114
|
+
case 'bedrag':
|
|
115
|
+
return (h % 100000) / 100;
|
|
116
|
+
case 'datum':
|
|
117
|
+
return isoDate(new Date(2026, 0, 1 + (h % 365)));
|
|
118
|
+
case 'keuze':
|
|
119
|
+
return waarden !== undefined && waarden.length > 0 ? waarden[h % waarden.length] : null;
|
|
120
|
+
case 'ja/nee':
|
|
121
|
+
return h % 2 === 0;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** The fields a query asks of resource `name`, or undefined for all of them. */
|
|
125
|
+
function selection(query, name) {
|
|
126
|
+
const fields = query?.fields;
|
|
127
|
+
return (Array.isArray(fields) ? fields : fields?.[name]);
|
|
128
|
+
}
|
|
129
|
+
/** The preview's own filter and sort over these rows alone, typed by value so numbers compare as numbers. */
|
|
130
|
+
async function locally(name, rows, { filter = {}, sort = [] }) {
|
|
131
|
+
const sorted = [sort].flat().join(',').split(',').map((item) => item.replace(/^[-+]/, ''));
|
|
132
|
+
const fields = Object.fromEntries([...rows.flatMap(Object.keys), ...Object.keys(filter), ...sorted]
|
|
133
|
+
.filter((key) => key !== '')
|
|
134
|
+
.map((key) => [key, { type: typeof rows.find((row) => row[key] != null)?.[key] }]));
|
|
135
|
+
const byId = new Map(rows.map((row) => [String(row.id), row]));
|
|
136
|
+
const store = new PreviewStore({ [name]: { schema: { fields }, rows } });
|
|
137
|
+
const { data } = await store.resource(name).list({ filter, sort, page: { size: 200 } });
|
|
138
|
+
return data.map((row) => byId.get(String(row.id)));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A live resource some of whose fields do not exist yet. Those fields never reach the server (naming one would be a
|
|
142
|
+
* 422): each row gets an invented value, a filter or sort on one applies to the returned page only, and an aggregate
|
|
143
|
+
* that names one is answered from the mock rows.
|
|
144
|
+
*/
|
|
145
|
+
export function mixedResource(live, gaps, mock, mockRows) {
|
|
146
|
+
const { name } = live;
|
|
147
|
+
const missing = new Map(gaps.filter((gap) => gap.resource === name && gap.veld !== undefined).map((gap) => [gap.veld, gap]));
|
|
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] ?? '');
|
|
151
|
+
const split = (query = {}) => {
|
|
152
|
+
const selected = selection(query, name);
|
|
153
|
+
const kept = selected?.filter((field) => !missing.has(field));
|
|
154
|
+
const filters = Object.entries(query.filter ?? {});
|
|
155
|
+
const sort = items(query.sort).filter((item) => item !== '');
|
|
156
|
+
// The live part names no missing field; the local part only missing ones.
|
|
157
|
+
const part = (local) => ({
|
|
158
|
+
filter: Object.fromEntries(filters.filter(([key]) => missing.has(key) === local)),
|
|
159
|
+
sort: sort.filter((item) => names(item) === local),
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
// The id is what an invented value hangs on, so a narrowed request always asks for it.
|
|
163
|
+
live: { ...query, ...(kept && { fields: kept.includes('id') ? kept : [...kept, 'id'] }), ...part(false) },
|
|
164
|
+
local: part(true),
|
|
165
|
+
shown: (field) => selected === undefined || selected.includes(field),
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
const fill = (row, shown, all = false) => {
|
|
169
|
+
for (const [field, gap] of missing) {
|
|
170
|
+
// Own properties only: a field named `constructor` must not find Object's.
|
|
171
|
+
if ((all || shown(field)) && (!own(row, field) || row[field] == null)) {
|
|
172
|
+
row[field] = inventValue(gap, row.id, mockRows);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const merge = async (page, { local, shown }) => {
|
|
177
|
+
let data = page.data;
|
|
178
|
+
data.forEach((row) => fill(row, shown, true));
|
|
179
|
+
if (data.length > 0 && (local.sort.length > 0 || Object.keys(local.filter).length > 0)) {
|
|
180
|
+
data = await locally(name, data, local);
|
|
181
|
+
}
|
|
182
|
+
for (const row of data) {
|
|
183
|
+
for (const field of missing.keys()) {
|
|
184
|
+
if (!shown(field)) {
|
|
185
|
+
delete row[field];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
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
|
+
};
|
|
198
|
+
};
|
|
199
|
+
const list = async (query, init) => {
|
|
200
|
+
const parts = split(query);
|
|
201
|
+
return merge(await live.list(parts.live, init), parts);
|
|
202
|
+
};
|
|
203
|
+
const pages = async function* (query, init) {
|
|
204
|
+
const parts = split(query);
|
|
205
|
+
for await (const page of live.pages(parts.live, init)) {
|
|
206
|
+
yield await merge(page, parts);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
return {
|
|
210
|
+
...live,
|
|
211
|
+
list,
|
|
212
|
+
pages,
|
|
213
|
+
async *all(query, init) {
|
|
214
|
+
for await (const page of pages(query, init)) {
|
|
215
|
+
yield* page.data;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
async get(id, query, init) {
|
|
219
|
+
const parts = split(query);
|
|
220
|
+
const response = await live.get(id, parts.live, init);
|
|
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
|
+
}
|
|
226
|
+
return response;
|
|
227
|
+
},
|
|
228
|
+
aggregate: (query, init) => [items(query.fn).map((fn) => fn.replace(/^\w+$/, '')), items(query.group), Object.keys(query.filter ?? {})]
|
|
229
|
+
.flat()
|
|
230
|
+
.some(names)
|
|
231
|
+
? mock.resource(name).aggregate(query)
|
|
232
|
+
: live.aggregate(query, init),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* A wireframe's writes, answered in this browser and never sent: what is created, changed or deleted lies over the
|
|
237
|
+
* wrapped client's reads until the page reloads. A created row's id is `voorbeeld-<n>`. A list shows a created or
|
|
238
|
+
* changed row only where the query's filter puts it, so a moved board card lands in its new column.
|
|
239
|
+
*/
|
|
240
|
+
export function simulatedWrites(client, name) {
|
|
241
|
+
const key = (row) => String(row.id);
|
|
242
|
+
// Created rows and whole changed copies of real ones, newest last; they stand in for the server's version.
|
|
243
|
+
const local = new Map();
|
|
244
|
+
// The server's own copy of each real row changed or deleted here: whether the server still lists it.
|
|
245
|
+
const origins = new Map();
|
|
246
|
+
const deleted = new Set();
|
|
247
|
+
let count = 0;
|
|
248
|
+
const answer = (status, data) => ({
|
|
249
|
+
status,
|
|
250
|
+
data,
|
|
251
|
+
meta: null,
|
|
252
|
+
headers: new Headers(),
|
|
253
|
+
etag: null,
|
|
254
|
+
recordVersion: null,
|
|
255
|
+
location: null,
|
|
256
|
+
replayed: false,
|
|
257
|
+
rateLimit: { limit: null, remaining: null, reset: null },
|
|
258
|
+
});
|
|
259
|
+
const pick = (row, query) => {
|
|
260
|
+
const fields = selection(query, name);
|
|
261
|
+
return Object.fromEntries(Object.entries(row).filter(([field]) => fields?.includes(field) ?? true));
|
|
262
|
+
};
|
|
263
|
+
const overlay = async (response, query = {}) => {
|
|
264
|
+
const { page } = response.meta;
|
|
265
|
+
const fitting = local.size > 0 ? (await locally(name, [...local.values()].reverse(), query)) : [];
|
|
266
|
+
const fits = new Set(fitting.map(key));
|
|
267
|
+
const served = new Set(origins.size > 0 ? (await locally(name, [...origins.values()], query)).map(key) : []);
|
|
268
|
+
const listed = new Set(response.data.map(key));
|
|
269
|
+
// A changed row keeps its place while the query still fits it; one only it fits now joins page 1.
|
|
270
|
+
const kept = response.data
|
|
271
|
+
.filter((row) => !deleted.has(key(row)) && (!local.has(key(row)) || fits.has(key(row))))
|
|
272
|
+
.map((row) => local.get(key(row)) ?? row);
|
|
273
|
+
const joined = fitting.filter((row) => !served.has(key(row)) && !listed.has(key(row)));
|
|
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
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
...response,
|
|
295
|
+
data: data.map((row) => pick(row, query)),
|
|
296
|
+
meta: { ...response.meta, page: { ...page, total } },
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
const get = async (id, query, init) => {
|
|
300
|
+
const copy = local.get(String(id));
|
|
301
|
+
if (deleted.has(String(id))) {
|
|
302
|
+
const detail = `No such ${name} record.`;
|
|
303
|
+
throw new DmpError(404, new Headers(), { status: 404, code: 'not_found', detail });
|
|
304
|
+
}
|
|
305
|
+
return copy === undefined ? client.get(id, query, init) : answer(200, pick(copy, query));
|
|
306
|
+
};
|
|
307
|
+
// The row as it is here; a real one's server copy is kept the first time it changes.
|
|
308
|
+
const current = async (id) => {
|
|
309
|
+
const { data } = await get(id);
|
|
310
|
+
if (!local.has(String(id))) {
|
|
311
|
+
origins.set(String(id), data);
|
|
312
|
+
}
|
|
313
|
+
return data;
|
|
314
|
+
};
|
|
315
|
+
const operation = async () => ({ ...answer(202, null), accepted: true });
|
|
316
|
+
const pages = async function* (query, init) {
|
|
317
|
+
for await (const page of client.pages(query, init)) {
|
|
318
|
+
yield await overlay(page, query);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
return {
|
|
322
|
+
...client,
|
|
323
|
+
get,
|
|
324
|
+
pages,
|
|
325
|
+
operation,
|
|
326
|
+
collectionOperation: operation,
|
|
327
|
+
list: async (query, init) => overlay(await client.list(query, init), query),
|
|
328
|
+
async *all(query, init) {
|
|
329
|
+
for await (const page of pages(query, init)) {
|
|
330
|
+
yield* page.data;
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
async create(body) {
|
|
334
|
+
const row = { ...body, id: `voorbeeld-${++count}` };
|
|
335
|
+
local.set(row.id, row);
|
|
336
|
+
return answer(201, { ...row });
|
|
337
|
+
},
|
|
338
|
+
async update(id, body) {
|
|
339
|
+
const data = await current(id);
|
|
340
|
+
const row = { ...data, ...body, id: data.id };
|
|
341
|
+
local.set(String(id), row);
|
|
342
|
+
return answer(200, { ...row });
|
|
343
|
+
},
|
|
344
|
+
// As the server: an id that is not there is a 404, and a delete never lowers a count for nothing.
|
|
345
|
+
async delete(id) {
|
|
346
|
+
await current(id);
|
|
347
|
+
local.delete(String(id));
|
|
348
|
+
deleted.add(String(id));
|
|
349
|
+
return answer(204, null);
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2021 The Albert Sans Project Authors (https://github.com/usted/Albert-Sans) AlbertSans-Italic[wght].ttf: Copyright 2021 The Albert Sans Project Authors (https://github.com/usted/Albert-Sans)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2019 IBM Corp. All rights reserved. IBMPlexSans-Italic[wdth,wght].ttf: Copyright 2019 IBM Corp. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) Inter-Italic[opsz,wght].ttf: Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|