@preprio/toolkit 0.5.0-rc.11 → 0.5.0-rc.12
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 +7 -0
- package/dist/astro/index.cjs +31 -2
- package/dist/astro/index.js +31 -2
- package/dist/{chunk-KD3GK6TO.js → chunk-CABGJW5X.js} +30 -1
- package/dist/{chunk-URXSDJOU.js → chunk-H3AFUPYD.js} +9 -0
- package/dist/{chunk-AOHA5INA.js → chunk-HVTNUCF7.js} +1 -1
- package/dist/{chunk-IPMB32AZ.js → chunk-LWUSRWFE.js} +2 -2
- package/dist/{chunk-6423Q744.js → chunk-M7FT7XXV.js} +63 -16
- package/dist/{chunk-QSXIHRH4.js → chunk-TJ7VALNA.js} +1 -1
- package/dist/index.cjs +101 -16
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -6
- package/dist/{live-preview-DLXGXRE5.js → live-preview-SFBGATWC.js} +3 -3
- package/dist/nextjs/components.cjs +71 -15
- package/dist/nextjs/components.js +3 -3
- package/dist/nextjs/index.cjs +31 -2
- package/dist/nextjs/index.js +31 -2
- package/dist/nextjs/live-action.js +3 -3
- package/dist/nextjs/live.cjs +30 -1
- package/dist/nextjs/live.js +1 -1
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/nuxt/{live-preview-2RCU35W5.js → live-preview-XMQQZHMZ.js} +30 -1
- package/dist/nuxt/live.cjs +30 -1
- package/dist/nuxt/live.js +1 -1
- package/dist/react/components.cjs +71 -15
- package/dist/react/components.js +71 -15
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/dist/sveltekit/{live-preview-2RCU35W5.js → live-preview-XMQQZHMZ.js} +30 -1
- package/dist/sveltekit/live.cjs +30 -1
- package/dist/sveltekit/live.js +1 -1
- package/package.json +1 -1
package/dist/react/components.js
CHANGED
|
@@ -371,6 +371,7 @@ function createIframeBridge(store, options = {}) {
|
|
|
371
371
|
const onReferences = options.onReferences;
|
|
372
372
|
const onAssets = options.onAssets;
|
|
373
373
|
const onAllowNavigation = options.onAllowNavigation;
|
|
374
|
+
const onItemChanged = options.onItemChanged;
|
|
374
375
|
let parentOrigin = null;
|
|
375
376
|
const onKeyDown = (event) => {
|
|
376
377
|
const key = event.key.toLowerCase();
|
|
@@ -461,6 +462,14 @@ function createIframeBridge(store, options = {}) {
|
|
|
461
462
|
debug.warn("allowNavigation handler failed", error);
|
|
462
463
|
}
|
|
463
464
|
}
|
|
465
|
+
if (data?.event === "prepr:itemChanged") {
|
|
466
|
+
if (typeof data.itemId !== "string" || !data.itemId) return;
|
|
467
|
+
try {
|
|
468
|
+
onItemChanged?.(data.itemId);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
debug.warn("itemChanged handler failed", error);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
464
473
|
};
|
|
465
474
|
return {
|
|
466
475
|
start() {
|
|
@@ -2604,6 +2613,7 @@ var ABSOLUTE_HTTP = /^https?:\/\//i;
|
|
|
2604
2613
|
var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
|
|
2605
2614
|
var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
|
|
2606
2615
|
var ASSET_ATTR = "data-prepr-asset";
|
|
2616
|
+
var REMOVED_ATTR = "data-prepr-removed";
|
|
2607
2617
|
var ASSET_SOURCE_ATTRIBUTES = [
|
|
2608
2618
|
...SOURCE_URL_ATTRIBUTES,
|
|
2609
2619
|
...SOURCE_SET_ATTRIBUTES
|
|
@@ -2613,7 +2623,8 @@ var TAG_ATTRIBUTES = [
|
|
|
2613
2623
|
"data-prepr-id",
|
|
2614
2624
|
"data-prepr-field",
|
|
2615
2625
|
"data-prepr-entry-id",
|
|
2616
|
-
ASSET_ATTR
|
|
2626
|
+
ASSET_ATTR,
|
|
2627
|
+
REMOVED_ATTR
|
|
2617
2628
|
];
|
|
2618
2629
|
function decodeSegment(segment) {
|
|
2619
2630
|
try {
|
|
@@ -2732,11 +2743,22 @@ function besideOtherItem(img, isCurrentItem) {
|
|
|
2732
2743
|
return false;
|
|
2733
2744
|
}
|
|
2734
2745
|
function tagAssetImages(index, itemId, keys, options = {}) {
|
|
2735
|
-
const { isCurrentItem } = options;
|
|
2746
|
+
const { isCurrentItem, removed } = options;
|
|
2747
|
+
let showsRemoved = false;
|
|
2736
2748
|
for (const img of document.getElementsByTagName("img")) {
|
|
2737
2749
|
const ours = img.hasAttribute(ASSET_ATTR);
|
|
2738
|
-
|
|
2739
|
-
|
|
2750
|
+
let match = index.size ? matchAsset(img, index, keys) : void 0;
|
|
2751
|
+
let isRemoved = false;
|
|
2752
|
+
if (!match && removed?.size) {
|
|
2753
|
+
match = matchAsset(img, removed, keys);
|
|
2754
|
+
isRemoved = match !== void 0;
|
|
2755
|
+
}
|
|
2756
|
+
if (!ours && img.hasAttribute("data-prepr-encoded")) {
|
|
2757
|
+
const mark = isRemoved && (!isCurrentItem || isCurrentItem(img));
|
|
2758
|
+
setIfChanged(img, REMOVED_ATTR, mark ? "" : null);
|
|
2759
|
+
showsRemoved || (showsRemoved = mark);
|
|
2760
|
+
continue;
|
|
2761
|
+
}
|
|
2740
2762
|
const asset = match && isCurrentItem && besideOtherItem(img, isCurrentItem) ? void 0 : match;
|
|
2741
2763
|
if (!asset) {
|
|
2742
2764
|
if (ours) untagAssetImage(img);
|
|
@@ -2747,10 +2769,14 @@ function tagAssetImages(index, itemId, keys, options = {}) {
|
|
|
2747
2769
|
setIfChanged(img, "data-prepr-id", itemId);
|
|
2748
2770
|
setIfChanged(img, "data-prepr-field", asset.field);
|
|
2749
2771
|
setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
|
|
2772
|
+
setIfChanged(img, REMOVED_ATTR, isRemoved ? "" : null);
|
|
2773
|
+
showsRemoved || (showsRemoved = isRemoved);
|
|
2750
2774
|
}
|
|
2775
|
+
return showsRemoved;
|
|
2751
2776
|
}
|
|
2752
2777
|
function untagAssetImages() {
|
|
2753
2778
|
document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
|
|
2779
|
+
document.querySelectorAll(`img[${REMOVED_ATTR}]`).forEach((img) => img.removeAttribute(REMOVED_ATTR));
|
|
2754
2780
|
}
|
|
2755
2781
|
|
|
2756
2782
|
// src/core/visual-editor/navigation-guard.ts
|
|
@@ -2926,11 +2952,13 @@ var CHROME_SELECTOR = "header, footer, nav, [role=banner], [role=contentinfo], [
|
|
|
2926
2952
|
var SECTIONING_SELECTOR = "article, aside, main, nav, section";
|
|
2927
2953
|
var PLAIN_HEADER_FOOTER = "header:not([role]), footer:not([role])";
|
|
2928
2954
|
var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
|
|
2929
|
-
var
|
|
2955
|
+
var REMOVED_STYLES = `img[${REMOVED_ATTR}]{opacity:.2!important;transition:opacity .2s ease}@media (prefers-reduced-motion:reduce){img[${REMOVED_ATTR}]{transition:none}}`;
|
|
2956
|
+
var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}${REMOVED_STYLES}`;
|
|
2930
2957
|
var SUSPENDED_ATTR = "data-prepr-ve-suspended";
|
|
2931
2958
|
var FIND_RETRY_MS = 150;
|
|
2932
2959
|
var FIND_RETRIES = 6;
|
|
2933
2960
|
var MAX_CACHED_KEYS = 2e3;
|
|
2961
|
+
var SAVED_FADE_MS = 1e4;
|
|
2934
2962
|
var LAYOUT_MUTATIONS = {
|
|
2935
2963
|
childList: true,
|
|
2936
2964
|
subtree: true,
|
|
@@ -2973,6 +3001,9 @@ function createVisualEditor(opts) {
|
|
|
2973
3001
|
let guardEnabled = false;
|
|
2974
3002
|
let assetIndex = /* @__PURE__ */ new Map();
|
|
2975
3003
|
const assetKeys = /* @__PURE__ */ new Map();
|
|
3004
|
+
const removedAssets = /* @__PURE__ */ new Map();
|
|
3005
|
+
let saved = false;
|
|
3006
|
+
let savedTimeout = null;
|
|
2976
3007
|
const guard = createNavigationGuard({
|
|
2977
3008
|
onBlocked: (href) => opts.onNavigationBlocked?.(href),
|
|
2978
3009
|
showsEditedContent: () => showsEditedContent()
|
|
@@ -3171,15 +3202,32 @@ function createVisualEditor(opts) {
|
|
|
3171
3202
|
const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
|
|
3172
3203
|
select(el, found, block);
|
|
3173
3204
|
}
|
|
3205
|
+
const hasAssets = () => assetIndex.size > 0 || removedAssets.size > 0;
|
|
3174
3206
|
function tagAssets() {
|
|
3175
3207
|
if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
|
|
3176
|
-
tagAssetImages(assetIndex, currentItemId, assetKeys, {
|
|
3177
|
-
isCurrentItem: (el) => isCurrentItem(el)
|
|
3208
|
+
const showsRemoved = tagAssetImages(assetIndex, currentItemId, assetKeys, {
|
|
3209
|
+
isCurrentItem: (el) => isCurrentItem(el),
|
|
3210
|
+
removed: removedAssets
|
|
3178
3211
|
});
|
|
3212
|
+
if (saved && !showsRemoved) forgetRemoved();
|
|
3213
|
+
}
|
|
3214
|
+
function forgetRemoved() {
|
|
3215
|
+
saved = false;
|
|
3216
|
+
if (savedTimeout) clearTimeout(savedTimeout);
|
|
3217
|
+
savedTimeout = null;
|
|
3218
|
+
if (!removedAssets.size) return;
|
|
3219
|
+
removedAssets.clear();
|
|
3220
|
+
if (running) tagAssets();
|
|
3221
|
+
}
|
|
3222
|
+
function itemSaved() {
|
|
3223
|
+
if (!removedAssets.size) return;
|
|
3224
|
+
saved = true;
|
|
3225
|
+
if (savedTimeout) clearTimeout(savedTimeout);
|
|
3226
|
+
savedTimeout = setTimeout(forgetRemoved, SAVED_FADE_MS);
|
|
3179
3227
|
}
|
|
3180
3228
|
const handleLayoutFrame = () => {
|
|
3181
3229
|
mutationFrame = null;
|
|
3182
|
-
if (
|
|
3230
|
+
if (hasAssets()) tagAssets();
|
|
3183
3231
|
reselect();
|
|
3184
3232
|
const hovered = overlay.getHoverTarget();
|
|
3185
3233
|
if (hovered && !hovered.isConnected) {
|
|
@@ -3191,7 +3239,7 @@ function createVisualEditor(opts) {
|
|
|
3191
3239
|
};
|
|
3192
3240
|
const handleMutations = () => {
|
|
3193
3241
|
if (mutationFrame !== null) return;
|
|
3194
|
-
if (!selection && !overlay.getHoverTarget() && !
|
|
3242
|
+
if (!selection && !overlay.getHoverTarget() && !hasAssets()) return;
|
|
3195
3243
|
mutationFrame = requestAnimationFrame(handleLayoutFrame);
|
|
3196
3244
|
};
|
|
3197
3245
|
function injectCursorStyles() {
|
|
@@ -3232,7 +3280,7 @@ function createVisualEditor(opts) {
|
|
|
3232
3280
|
});
|
|
3233
3281
|
mutationObserver = new MutationObserver(handleMutations);
|
|
3234
3282
|
mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
|
|
3235
|
-
if (
|
|
3283
|
+
if (hasAssets()) tagAssets();
|
|
3236
3284
|
syncGuard();
|
|
3237
3285
|
debug8.log("visual editor started");
|
|
3238
3286
|
}
|
|
@@ -3308,18 +3356,24 @@ function createVisualEditor(opts) {
|
|
|
3308
3356
|
});
|
|
3309
3357
|
}
|
|
3310
3358
|
function setCurrentItem(itemId, referenceList, componentIds) {
|
|
3359
|
+
if (itemId !== currentItemId) forgetRemoved();
|
|
3311
3360
|
currentItemId = itemId;
|
|
3312
3361
|
if (referenceList) setReferences(referenceList, componentIds);
|
|
3313
3362
|
else if (componentIds) ownComponents = new Set(componentIds);
|
|
3314
|
-
if (running &&
|
|
3363
|
+
if (running && hasAssets()) tagAssets();
|
|
3315
3364
|
const selected = overlay.getSelectedTarget();
|
|
3316
3365
|
const owner = selected ? encodedFrom(selected) : null;
|
|
3317
3366
|
if (owner && !isCurrentItem(owner)) clearSelection();
|
|
3318
3367
|
}
|
|
3319
3368
|
function setAssets(assets) {
|
|
3320
|
-
const hadAssets =
|
|
3321
|
-
|
|
3322
|
-
|
|
3369
|
+
const hadAssets = hasAssets();
|
|
3370
|
+
const next = createAssetIndex(assets);
|
|
3371
|
+
for (const [key, asset] of assetIndex) {
|
|
3372
|
+
if (!next.has(key)) removedAssets.set(key, asset);
|
|
3373
|
+
}
|
|
3374
|
+
for (const key of next.keys()) removedAssets.delete(key);
|
|
3375
|
+
assetIndex = next;
|
|
3376
|
+
if (running && (hadAssets || hasAssets())) tagAssets();
|
|
3323
3377
|
}
|
|
3324
3378
|
function setReferences(referenceList, componentIds) {
|
|
3325
3379
|
if (componentIds) ownComponents = new Set(componentIds);
|
|
@@ -3345,6 +3399,7 @@ function createVisualEditor(opts) {
|
|
|
3345
3399
|
setCurrentItem,
|
|
3346
3400
|
setReferences,
|
|
3347
3401
|
setAssets,
|
|
3402
|
+
itemSaved,
|
|
3348
3403
|
setLocale,
|
|
3349
3404
|
focusField,
|
|
3350
3405
|
setNavigationGuard,
|
|
@@ -3502,6 +3557,7 @@ function createPreprPreview(opts = {}) {
|
|
|
3502
3557
|
},
|
|
3503
3558
|
onAssets: (assets) => visualEditor?.setAssets(assets),
|
|
3504
3559
|
onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
|
|
3560
|
+
onItemChanged: () => visualEditor?.itemSaved(),
|
|
3505
3561
|
onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
|
|
3506
3562
|
// The editor selecting a field scrolls the matching element into view. The
|
|
3507
3563
|
// lookup relies on the attributes the stega scan writes, so it finds
|
|
@@ -3542,7 +3598,7 @@ function createPreprPreview(opts = {}) {
|
|
|
3542
3598
|
}
|
|
3543
3599
|
|
|
3544
3600
|
// src/version.ts
|
|
3545
|
-
var VERSION = "0.5.0-rc.
|
|
3601
|
+
var VERSION = "0.5.0-rc.12";
|
|
3546
3602
|
|
|
3547
3603
|
// src/core/pixel.ts
|
|
3548
3604
|
var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
|
package/dist/sveltekit/index.cjs
CHANGED
package/dist/sveltekit/index.js
CHANGED
|
@@ -160,8 +160,32 @@ function shapeLike(template, incoming) {
|
|
|
160
160
|
}
|
|
161
161
|
return out;
|
|
162
162
|
}
|
|
163
|
+
function isAsset(value) {
|
|
164
|
+
return isPlainObject(value) && (typeof value.url === "string" || value.__typename === "Asset");
|
|
165
|
+
}
|
|
166
|
+
function hasId(list, entry) {
|
|
167
|
+
return entry._id !== void 0 && list.some((c) => isPlainObject(c) && c._id === entry._id);
|
|
168
|
+
}
|
|
169
|
+
function isAssetValue(value) {
|
|
170
|
+
return Array.isArray(value) ? value.length > 0 && value.every(isAsset) : isAsset(value);
|
|
171
|
+
}
|
|
172
|
+
function keepRemovedAssets(current, next) {
|
|
173
|
+
const listed = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const entry of next) {
|
|
175
|
+
if (isPlainObject(entry) && entry._id !== void 0) listed.add(entry._id);
|
|
176
|
+
}
|
|
177
|
+
let out = next;
|
|
178
|
+
current.forEach((entry, position) => {
|
|
179
|
+
if (!isPlainObject(entry) || entry._id === void 0) return;
|
|
180
|
+
if (listed.has(entry._id)) return;
|
|
181
|
+
if (out === next) out = [...next];
|
|
182
|
+
out.splice(Math.min(position, out.length), 0, entry);
|
|
183
|
+
});
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
163
186
|
function mergeStructured(current, incoming) {
|
|
164
187
|
if (incoming === null || Array.isArray(incoming) && !incoming.length) {
|
|
188
|
+
if (isAssetValue(current)) return current;
|
|
165
189
|
if (Array.isArray(current)) return current.length ? [] : current;
|
|
166
190
|
return null;
|
|
167
191
|
}
|
|
@@ -169,13 +193,18 @@ function mergeStructured(current, incoming) {
|
|
|
169
193
|
if (current === null || current === void 0) return incoming;
|
|
170
194
|
if (Array.isArray(current)) {
|
|
171
195
|
const template = current.find(isPlainObject);
|
|
172
|
-
|
|
196
|
+
const next = list.map((entry) => {
|
|
173
197
|
const match = current.find(
|
|
174
198
|
(c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
|
|
175
199
|
);
|
|
176
200
|
if (isPlainObject(match)) return mergeObject(match, entry);
|
|
177
201
|
return isPlainObject(template) ? shapeLike(template, entry) : entry;
|
|
178
202
|
});
|
|
203
|
+
if (!isAssetValue(current) || list.some((entry) => !hasId(current, entry))) {
|
|
204
|
+
return next;
|
|
205
|
+
}
|
|
206
|
+
const kept = keepRemovedAssets(current, next);
|
|
207
|
+
return kept.length === current.length && kept.every((entry, i) => entry === current[i]) ? current : kept;
|
|
179
208
|
}
|
|
180
209
|
if (isPlainObject(current)) return mergeObject(current, list[0]);
|
|
181
210
|
return current;
|
package/dist/sveltekit/live.cjs
CHANGED
|
@@ -536,8 +536,32 @@ function shapeLike(template, incoming) {
|
|
|
536
536
|
}
|
|
537
537
|
return out;
|
|
538
538
|
}
|
|
539
|
+
function isAsset(value) {
|
|
540
|
+
return isPlainObject(value) && (typeof value.url === "string" || value.__typename === "Asset");
|
|
541
|
+
}
|
|
542
|
+
function hasId(list, entry) {
|
|
543
|
+
return entry._id !== void 0 && list.some((c) => isPlainObject(c) && c._id === entry._id);
|
|
544
|
+
}
|
|
545
|
+
function isAssetValue(value) {
|
|
546
|
+
return Array.isArray(value) ? value.length > 0 && value.every(isAsset) : isAsset(value);
|
|
547
|
+
}
|
|
548
|
+
function keepRemovedAssets(current, next) {
|
|
549
|
+
const listed = /* @__PURE__ */ new Set();
|
|
550
|
+
for (const entry of next) {
|
|
551
|
+
if (isPlainObject(entry) && entry._id !== void 0) listed.add(entry._id);
|
|
552
|
+
}
|
|
553
|
+
let out = next;
|
|
554
|
+
current.forEach((entry, position) => {
|
|
555
|
+
if (!isPlainObject(entry) || entry._id === void 0) return;
|
|
556
|
+
if (listed.has(entry._id)) return;
|
|
557
|
+
if (out === next) out = [...next];
|
|
558
|
+
out.splice(Math.min(position, out.length), 0, entry);
|
|
559
|
+
});
|
|
560
|
+
return out;
|
|
561
|
+
}
|
|
539
562
|
function mergeStructured(current, incoming) {
|
|
540
563
|
if (incoming === null || Array.isArray(incoming) && !incoming.length) {
|
|
564
|
+
if (isAssetValue(current)) return current;
|
|
541
565
|
if (Array.isArray(current)) return current.length ? [] : current;
|
|
542
566
|
return null;
|
|
543
567
|
}
|
|
@@ -545,13 +569,18 @@ function mergeStructured(current, incoming) {
|
|
|
545
569
|
if (current === null || current === void 0) return incoming;
|
|
546
570
|
if (Array.isArray(current)) {
|
|
547
571
|
const template = current.find(isPlainObject);
|
|
548
|
-
|
|
572
|
+
const next = list.map((entry) => {
|
|
549
573
|
const match = current.find(
|
|
550
574
|
(c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
|
|
551
575
|
);
|
|
552
576
|
if (isPlainObject(match)) return mergeObject(match, entry);
|
|
553
577
|
return isPlainObject(template) ? shapeLike(template, entry) : entry;
|
|
554
578
|
});
|
|
579
|
+
if (!isAssetValue(current) || list.some((entry) => !hasId(current, entry))) {
|
|
580
|
+
return next;
|
|
581
|
+
}
|
|
582
|
+
const kept = keepRemovedAssets(current, next);
|
|
583
|
+
return kept.length === current.length && kept.every((entry, i) => entry === current[i]) ? current : kept;
|
|
555
584
|
}
|
|
556
585
|
if (isPlainObject(current)) return mergeObject(current, list[0]);
|
|
557
586
|
return current;
|
package/dist/sveltekit/live.js
CHANGED
|
@@ -131,7 +131,7 @@ function isFramed() {
|
|
|
131
131
|
return typeof window !== "undefined" && window.self !== window.top;
|
|
132
132
|
}
|
|
133
133
|
function startLive() {
|
|
134
|
-
starting ?? (starting = import("./live-preview-
|
|
134
|
+
starting ?? (starting = import("./live-preview-XMQQZHMZ.js").then(
|
|
135
135
|
({ applyPatchLog, createLiveBridge, createLiveStore }) => {
|
|
136
136
|
applyLog = applyPatchLog;
|
|
137
137
|
const started = createLiveStore();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preprio/toolkit",
|
|
3
|
-
"version": "0.5.0-rc.
|
|
3
|
+
"version": "0.5.0-rc.12",
|
|
4
4
|
"description": "Prepr personalization, A/B testing and preview toolkit for React, Next.js, Nuxt, Astro, SvelteKit and Express",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Prepr B.V. (https://prepr.io)",
|