@submitium/badges 1.1.0 → 1.2.0
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 +3 -2
- package/{client-x4bD7iXW.js → client-H97YsZf9.js} +8 -2
- package/{client-iCo9PkAU.d.ts → client-gGbOYZbN.d.ts} +8 -2
- package/index.d.ts +2 -2
- package/index.js +3 -3
- package/{layout-DOI7sqk6.js → layout-cMiQ5rSW.js} +2 -2
- package/package.json +1 -1
- package/react.d.ts +1 -1
- package/react.js +2 -2
- package/vue.js +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ export function Footer() {
|
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
`<SubmitiumBadges />` is an async Server Component. It reads `SUBMITIUM_API_KEY`, and
|
|
37
|
+
`<SubmitiumBadges />` is an async Server Component. It reads `SUBMITIUM_API_KEY`, and keeps the wall for a minute (`revalidate`), so new badges show within a few minutes of an order.
|
|
38
38
|
|
|
39
39
|
## Nuxt
|
|
40
40
|
|
|
@@ -118,7 +118,7 @@ const html = wall ? wallToHtml(layoutWall(wall)) : "";
|
|
|
118
118
|
|---|---|---|
|
|
119
119
|
| `apiKey` | `SUBMITIUM_API_KEY` | Your project's API key. |
|
|
120
120
|
| `timeout` | `3000` | Milliseconds to wait for the API. Past that, no wall, and your page carries on. |
|
|
121
|
-
| `revalidate` | `
|
|
121
|
+
| `revalidate` | `60` | Seconds a wall is kept before asking again (in memory, and as Next.js `revalidate`). A static Next.js page is rebuilt as often when visited: raise it to rebuild less, and new badges take as long to show. |
|
|
122
122
|
| `apiUrl` | `https://api.submitium.com` | For local development only. |
|
|
123
123
|
|
|
124
124
|
The format (`ribbon`: one gliding line in the footer bar; `window`: a small gliding window under your logo), the theme and the attribution link are settings you change in the dashboard. Your code stays the same.
|
|
@@ -129,6 +129,7 @@ The format (`ribbon`: one gliding line in the footer bar; `window`: a small glid
|
|
|
129
129
|
- **Checked.** The wall must match a strict contract (`parseWall`): https links that point to the directory's own domain, https images, a closed list of `rel` values, plain text with a length limit, and a wall that hasn't expired (a week at most). Anything else and the whole wall is refused, not repaired.
|
|
130
130
|
- **Plain elements only.** The wall becomes links, images, a few spans and one scoped `<style>`, all written by this package from numbers (no CSS comes from the API). React, Vue, Svelte and Astro write each element themselves and escape every value.
|
|
131
131
|
- **In your HTML.** The badges are in the HTML your server sends, which is what directory checkers read. No client JavaScript: motion is pure CSS. It pauses on hover and keyboard focus, and stands still under `prefers-reduced-motion`.
|
|
132
|
+
- **Up to date within minutes.** Your server keeps the wall for a minute, then asks again: badges a new order adds show within a few minutes, and a busy site asks us once a minute at most, per server. A static build (Astro, `next export`) shows them at its next build.
|
|
132
133
|
- **Visible from day one.** Before your first order the wall has no badge yet: it shows one gliding line, "Directory badges will appear here as this product gets listed.", where the badges will be. Badges replace it as directories are ordered.
|
|
133
134
|
- **Never breaks your page.** If the API is slow, down or answers anything unexpected, the last good wall is kept, or nothing renders.
|
|
134
135
|
- **Fair to directories.** Each badge links to its directory with the directory's official `rel`, the image loads from the directory's own URL, and no badge is hidden or duplicated. Badges a directory wants in view at all times never move.
|
|
@@ -191,6 +191,12 @@ async function verifySignedWall(envelope, keys = SUBMITIUM_PUBLIC_KEYS) {
|
|
|
191
191
|
//#endregion
|
|
192
192
|
//#region src/client.ts
|
|
193
193
|
const DEFAULT_API_URL = "https://api.submitium.com";
|
|
194
|
+
/**
|
|
195
|
+
* Seconds a wall is kept before asking again: new badges show within a few minutes of an order,
|
|
196
|
+
* and a busy site asks us at most once a minute per server. Next.js rebuilds a static page as often
|
|
197
|
+
* (the lowest `revalidate` of a route sets its own); pass a higher value to rebuild less often.
|
|
198
|
+
*/
|
|
199
|
+
const DEFAULT_REVALIDATE = 60;
|
|
194
200
|
const API_KEY = /^sbk_(live|test)_[A-Za-z0-9]{24,64}$/;
|
|
195
201
|
function envKey() {
|
|
196
202
|
return typeof process !== "undefined" ? process.env?.SUBMITIUM_API_KEY : void 0;
|
|
@@ -209,7 +215,7 @@ async function fetchWall(options = {}) {
|
|
|
209
215
|
const apiKey = options.apiKey ?? envKey();
|
|
210
216
|
if (!apiKey || !API_KEY.test(apiKey)) return null;
|
|
211
217
|
const base = (options.apiUrl ?? "https://api.submitium.com").replace(/\/+$/, "");
|
|
212
|
-
const revalidate = options.revalidate ??
|
|
218
|
+
const revalidate = options.revalidate ?? 60;
|
|
213
219
|
const slot = `${base} ${apiKey}`;
|
|
214
220
|
const kept = memory.get(slot);
|
|
215
221
|
if (kept && alive(kept.wall) && Date.now() - kept.at < revalidate * 1e3) return kept.wall;
|
|
@@ -243,4 +249,4 @@ async function request(base, apiKey, revalidate, options) {
|
|
|
243
249
|
}
|
|
244
250
|
}
|
|
245
251
|
//#endregion
|
|
246
|
-
export {
|
|
252
|
+
export { isSignedWall as a, LIMITS as c, VARIANTS as d, WALL_VERSION as f, SUBMITIUM_PUBLIC_KEYS as i, REL_TOKENS as l, DEFAULT_REVALIDATE as n, verifySignedWall as o, parseWall as p, fetchWall as r, ATTRIBUTIONS as s, DEFAULT_API_URL as t, THEMES as u };
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { d as Wall } from "./contract-KBbC2LBR.js";
|
|
2
2
|
//#region src/client.d.ts
|
|
3
3
|
declare const DEFAULT_API_URL = "https://api.submitium.com";
|
|
4
|
+
/**
|
|
5
|
+
* Seconds a wall is kept before asking again: new badges show within a few minutes of an order,
|
|
6
|
+
* and a busy site asks us at most once a minute per server. Next.js rebuilds a static page as often
|
|
7
|
+
* (the lowest `revalidate` of a route sets its own); pass a higher value to rebuild less often.
|
|
8
|
+
*/
|
|
9
|
+
declare const DEFAULT_REVALIDATE = 60;
|
|
4
10
|
type FetchWallOptions = {
|
|
5
11
|
/** Your project's API key. Defaults to the SUBMITIUM_API_KEY environment variable. */
|
|
6
12
|
apiKey?: string;
|
|
7
13
|
/** Milliseconds before giving up: the page renders without the wall. Default 3000. */
|
|
8
14
|
timeout?: number;
|
|
9
|
-
/** Seconds the wall is kept before asking again (in memory, and as Next.js `revalidate`). Default
|
|
15
|
+
/** Seconds the wall is kept before asking again (in memory, and as Next.js `revalidate`). Default 60. */
|
|
10
16
|
revalidate?: number;
|
|
11
17
|
/** Testing against a local API only: its URL and its public key. */
|
|
12
18
|
apiUrl?: string;
|
|
@@ -18,4 +24,4 @@ type FetchWallOptions = {
|
|
|
18
24
|
*/
|
|
19
25
|
declare function fetchWall(options?: FetchWallOptions): Promise<Wall | null>;
|
|
20
26
|
//#endregion
|
|
21
|
-
export {
|
|
27
|
+
export { fetchWall as i, DEFAULT_REVALIDATE as n, FetchWallOptions as r, DEFAULT_API_URL as t };
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as RelToken, c as VARIANTS, d as Wall, f as WallBadge, i as REL_TOKENS, l as Variant, n as Attribution, o as THEMES, p as parseWall, r as LIMITS, s as Theme, t as ATTRIBUTIONS, u as WALL_VERSION } from "./contract-KBbC2LBR.js";
|
|
2
|
-
import { n as
|
|
2
|
+
import { i as fetchWall, n as DEFAULT_REVALIDATE, r as FetchWallOptions, t as DEFAULT_API_URL } from "./client-gGbOYZbN.js";
|
|
3
3
|
//#region src/layout.d.ts
|
|
4
4
|
type WallTag = "div" | "ul" | "li" | "a" | "img" | "picture" | "source" | "span" | "b" | "style" | "svg" | "path";
|
|
5
5
|
type WallAttrs = {
|
|
@@ -82,4 +82,4 @@ export declare function styleText(style: Record<string, string>): string;
|
|
|
82
82
|
/** The CSS of a `style` node, refused if it could close the element (it never can: it's ours). */
|
|
83
83
|
export declare function cssOf(node: WallNode): string;
|
|
84
84
|
//#endregion
|
|
85
|
-
export { ATTRIBUTIONS, type Attribution, DEFAULT_API_URL, type FetchWallOptions, LIMITS, REL_TOKENS, type RelToken, type SignedWall, THEMES, type Theme, VARIANTS, type Variant, WALL_VERSION, type Wall, type WallAttrs, type WallBadge, type WallNode, type WallTag, fetchWall, parseWall };
|
|
85
|
+
export { ATTRIBUTIONS, type Attribution, DEFAULT_API_URL, DEFAULT_REVALIDATE, type FetchWallOptions, LIMITS, REL_TOKENS, type RelToken, type SignedWall, THEMES, type Theme, VARIANTS, type Variant, WALL_VERSION, type Wall, type WallAttrs, type WallBadge, type WallNode, type WallTag, fetchWall, parseWall };
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as TAGS, c as styleText, i as ATTRS, n as EMPTY_TEXT, o as attrsOf, r as layoutWall, s as cssOf, t as ATTRIBUTION_URL } from "./layout-
|
|
1
|
+
import { a as isSignedWall, c as LIMITS, d as VARIANTS, f as WALL_VERSION, i as SUBMITIUM_PUBLIC_KEYS, l as REL_TOKENS, n as DEFAULT_REVALIDATE, o as verifySignedWall, p as parseWall, r as fetchWall, s as ATTRIBUTIONS, t as DEFAULT_API_URL, u as THEMES } from "./client-H97YsZf9.js";
|
|
2
|
+
import { a as TAGS, c as styleText, i as ATTRS, n as EMPTY_TEXT, o as attrsOf, r as layoutWall, s as cssOf, t as ATTRIBUTION_URL } from "./layout-cMiQ5rSW.js";
|
|
3
3
|
//#region src/html.ts
|
|
4
4
|
const VOID = /* @__PURE__ */ new Set([
|
|
5
5
|
"img",
|
|
@@ -45,4 +45,4 @@ function write(node, depth) {
|
|
|
45
45
|
return `<${node.tag}${attrs}>${inner}${lines ? end : ""}</${node.tag}>`;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
48
|
-
export { ATTRIBUTIONS, ATTRIBUTION_URL, ATTRS, DEFAULT_API_URL, EMPTY_TEXT, LIMITS, REL_TOKENS, SUBMITIUM_PUBLIC_KEYS, TAGS, THEMES, VARIANTS, WALL_VERSION, attrsOf, cssOf, fetchWall, isSignedWall, layoutWall, parseWall, styleText, verifySignedWall, wallToHtml };
|
|
48
|
+
export { ATTRIBUTIONS, ATTRIBUTION_URL, ATTRS, DEFAULT_API_URL, DEFAULT_REVALIDATE, EMPTY_TEXT, LIMITS, REL_TOKENS, SUBMITIUM_PUBLIC_KEYS, TAGS, THEMES, VARIANTS, WALL_VERSION, attrsOf, cssOf, fetchWall, isSignedWall, layoutWall, parseWall, styleText, verifySignedWall, wallToHtml };
|
|
@@ -186,8 +186,8 @@ function windowFormat({ movers, badge }) {
|
|
|
186
186
|
const EMPTY_TEXT = "Directory badges will appear here as this product gets listed.";
|
|
187
187
|
const CSS_EMPTY = `.sbm-ph{position:relative;flex:1 1 260px;width:260px;min-width:0;max-width:520px;height:${H}px;overflow:hidden;${mask(40)}}.sbm-col .sbm-ph{flex:none;width:320px;max-width:100%}.sbm-pt{display:flex;width:max-content;height:100%;align-items:center;animation:sbm-pe 18s linear infinite}.sbm-pt span{padding-right:48px;white-space:nowrap;opacity:.7}@keyframes sbm-pe{from{transform:translateX(0)}to{transform:translateX(-50%)}}.sbm:hover .sbm-pt,.sbm:focus-within .sbm-pt{animation-play-state:paused}@media (prefers-reduced-motion:reduce){.sbm-ph{width:auto;height:auto;${NO_MASK}}.sbm-pt{width:auto;animation:none}.sbm-pt span{white-space:normal;padding:0}.sbm-pt span+span{display:none}}`;
|
|
188
188
|
const LAYOUT_CSS = {
|
|
189
|
-
ribbon: ".sbm-ribbon{container-type:inline-size;
|
|
190
|
-
window: ".sbm-col{display:flex;flex-direction:column;align-items:flex-start;gap:10px;font-size:12px}"
|
|
189
|
+
ribbon: ".sbm-ribbon{container-type:inline-size;width:100%;min-width:0}.sbm-bar{display:flex;align-items:center;flex-wrap:wrap;gap:10px 16px;font-size:12px}@container (max-width:520px){.sbm-lb{display:none}.sbm-w{flex-basis:100%}}",
|
|
190
|
+
window: ".sbm-window{min-width:0;max-width:100%}.sbm-col{display:flex;flex-direction:column;align-items:flex-start;gap:10px;font-size:12px}"
|
|
191
191
|
};
|
|
192
192
|
const MARK_PATHS = [["#4432F4", "M20 1l-1 1 0 2 6 27 1 1 2 0 1 0 6-12 12-2 1-2-1-1-11-6-14-8-2 0zM39 22l-2 0-7 11-2 1-2 0-2-1-3-11-14 8-1 1 2 2 20 10 3 1 2 0 23-11 2-2-1-1-15-9-3 1z"], ["#CAC9FD", "M11 37l-5 3 0 1 1 0 25 13 2-1 18-9 5-2 1-2-1-1-5-2-19 9-1 1-21-10zM10 47l-4 1 0 2 1 1 25 12 2 0 9-5 13-7 2-1 0-2-2 0-4-2-17 9-3 1-20-10-2 1z"]];
|
|
193
193
|
function attributionNode(rel) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@submitium/badges",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "The Submitium badge wall: a signed, checked wall rendered as plain links and images. React, Vue, Svelte, Astro.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Submitium <contact@submitium.com>",
|
package/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as Wall } from "./contract-KBbC2LBR.js";
|
|
2
|
-
import {
|
|
2
|
+
import { r as FetchWallOptions } from "./client-gGbOYZbN.js";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
//#region src/react.d.ts
|
|
5
5
|
/** A wall you already fetched (with `fetchWall`), rendered. */
|
package/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as TAGS, o as attrsOf, r as layoutWall, s as cssOf } from "./layout-
|
|
1
|
+
import { r as fetchWall } from "./client-H97YsZf9.js";
|
|
2
|
+
import { a as TAGS, o as attrsOf, r as layoutWall, s as cssOf } from "./layout-cMiQ5rSW.js";
|
|
3
3
|
import { createElement } from "react";
|
|
4
4
|
//#region src/react.tsx
|
|
5
5
|
/** React's names for the attributes React spells differently. */
|
package/vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as TAGS, o as attrsOf, r as layoutWall, s as cssOf } from "./layout-
|
|
1
|
+
import { a as TAGS, o as attrsOf, r as layoutWall, s as cssOf } from "./layout-cMiQ5rSW.js";
|
|
2
2
|
import { defineComponent, h } from "vue";
|
|
3
3
|
//#region src/vue.ts
|
|
4
4
|
function toVue(node) {
|