mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-10-27 08:31:00 +00:00
Add --bitgate-* theme tokens and a build-injected VERSION.
Widget styles now prefer --bitgate-* custom properties, falling back to the deprecated --gov-* names and then the existing defaults so current pages and overrides keep working. Browser bundles and ESM entry points expose VERSION (and window.bitgate.version) from package.json. Document allowlist on the widget policy preset list. Sync the lockfile so npm ci sees @bitgate/standalone. Co-authored-by: thePR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
90d4290989
commit
9e6f6c659e
24 files changed
+369
-22
No files matched your search
@@ -87,6 +87,11 @@ allowlisted publishers are shown — the symmetric twin of a denylist, for
|
||||
invite-only or curated surfaces). Every number in them is a starting point —
|
||||
see the [integration guide](docs/integration-guide.md) to write your own.
|
||||
|
||||
The package version is exported as `VERSION` and, in browser bundles, as
|
||||
`window.bitgate.version`. Both are injected from `package.json` at build time.
|
||||
Widget theme tokens use `--bitgate-*` custom properties; the older `--gov-*`
|
||||
names remain as fallbacks (see [`packages/widget/README.md`](packages/widget/README.md)).
|
||||
|
||||
## Quick start — JavaScript
|
||||
|
||||
```js
|
||||
|
||||
+3
-1
@@ -175,7 +175,9 @@ unchanged.
|
||||
authority or leaking private application context.
|
||||
- [ ] Decide whether standalone output is generated from the canonical engine
|
||||
directly or from a separately maintained browser entry point.
|
||||
- [ ] Decide how browser builds expose version and integrity metadata.
|
||||
- [x] Decide how browser builds expose version and integrity metadata.
|
||||
`VERSION` + `window.bitgate.version` equal `package.json`, injected at
|
||||
build time. Integrity manifests remain open (distribution / D7).
|
||||
|
||||
## BitBlocks App Kit compatibility layer
|
||||
|
||||
|
||||
Generated
+16
@@ -54,6 +54,10 @@
|
||||
"resolved": "packages/site",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitgate/standalone": {
|
||||
"resolved": "packages/standalone",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitgate/testing": {
|
||||
"resolved": "packages/testing",
|
||||
"link": true
|
||||
@@ -1837,6 +1841,18 @@
|
||||
"name": "@bitgate/site",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"packages/standalone": {
|
||||
"name": "@bitgate/standalone",
|
||||
"version": "0.1.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@bitgate/core": "*",
|
||||
"@bitgate/nostr": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.28.2"
|
||||
}
|
||||
},
|
||||
"packages/testing": {
|
||||
"name": "@bitgate/testing",
|
||||
"version": "0.1.0",
|
||||
|
||||
@@ -111,6 +111,8 @@ export {
|
||||
aggregateMutes,
|
||||
} from "./evaluator.js";
|
||||
|
||||
export { VERSION } from "./version.js";
|
||||
|
||||
/**
|
||||
* @typedef {import('./identifiers.js').GovernanceTarget} GovernanceTarget
|
||||
* @typedef {import('./identifiers.js').UserTarget} UserTarget
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* BitGate library version.
|
||||
*
|
||||
* Keep this string in lockstep with the root and `@bitgate/core` `package.json`
|
||||
* versions. `packages/core/test/version.test.js` asserts they match. Browser
|
||||
* bundles replace this module at build time with the same value read from
|
||||
* `package.json`, so the shipped file cannot drift.
|
||||
*/
|
||||
export const VERSION = "0.1.0";
|
||||
@@ -0,0 +1,17 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { VERSION } from "../src/index.js";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const rootPkg = JSON.parse(readFileSync(join(HERE, "..", "..", "..", "package.json"), "utf8"));
|
||||
const corePkg = JSON.parse(readFileSync(join(HERE, "..", "package.json"), "utf8"));
|
||||
|
||||
describe("VERSION", () => {
|
||||
it("matches package.json (root and @bitgate/core)", () => {
|
||||
expect(VERSION).toBe(rootPkg.version);
|
||||
expect(VERSION).toBe(corePkg.version);
|
||||
});
|
||||
});
|
||||
@@ -181,6 +181,7 @@ provider.runtime.policies.setLocalPolicy(policy);</code></pre>
|
||||
<tr><td><code>social</code></td><td>feed, profile, playback</td><td>Timelines and posts. Discovery declines to hard-hide.</td></tr>
|
||||
<tr><td><code>commerce</code></td><td>browse, detail, checkout, seller-dashboard</td><td>Marketplaces. Checkout decides transactions only.</td></tr>
|
||||
<tr><td><code>admin-only</code></td><td>default</td><td>Operator deny lists, with no trust-graph signals at all.</td></tr>
|
||||
<tr><td><code>allowlist</code></td><td>default</td><td>Only allowlisted publishers are shown; unlisted ones are hidden. Symmetric twin of a denylist, for invite-only or curated surfaces.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import { build, context } from "esbuild";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { bitgateVersionPlugin } from "../../scripts/esbuild-bitgate-version.mjs";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/** @type {import("esbuild").BuildOptions} */
|
||||
@@ -31,6 +33,7 @@ const options = {
|
||||
banner: {
|
||||
js: "/*! BitGate (headless standalone) — GPL-3.0-or-later — https://github.com/PR0M3TH3AN/Nostr-Governance */",
|
||||
},
|
||||
plugins: [bitgateVersionPlugin()],
|
||||
};
|
||||
|
||||
const watch = process.argv.includes("--watch");
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { VERSION } from "@bitgate/core";
|
||||
|
||||
/**
|
||||
* Publish `window.bitgate.version` for script-tag integrators.
|
||||
*
|
||||
* Additive: creates `window.bitgate` when missing, otherwise writes `version`
|
||||
* onto the existing object so a host-provided namespace is not replaced.
|
||||
* No-op in Node (no `window`).
|
||||
*
|
||||
* @param {Record<string, any>} [target] - Defaults to `globalThis.window`
|
||||
* @returns {Record<string, any>|null} The namespace that received `version`
|
||||
*/
|
||||
export function exposeBitGateVersion(target) {
|
||||
const root =
|
||||
target ??
|
||||
(typeof globalThis.window !== "undefined"
|
||||
? /** @type {Record<string, any>} */ (globalThis.window)
|
||||
: null);
|
||||
if (!root || typeof root !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const existing = root.bitgate;
|
||||
const ns =
|
||||
existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
||||
ns.version = VERSION;
|
||||
root.bitgate = ns;
|
||||
return ns;
|
||||
}
|
||||
@@ -11,5 +11,10 @@
|
||||
// Both re-exported packages are dependency-free (@bitgate/verify, the only
|
||||
// package with third-party deps, is intentionally not pulled in), so the bundle
|
||||
// carries no external code.
|
||||
import { exposeBitGateVersion } from "./expose-version.js";
|
||||
|
||||
export * from "@bitgate/core";
|
||||
export * from "@bitgate/nostr";
|
||||
export { exposeBitGateVersion };
|
||||
|
||||
exposeBitGateVersion();
|
||||
@@ -2,10 +2,16 @@
|
||||
// engine (core) + label codec (nostr) — so a no-build site gets everything from
|
||||
// the single bundled file. Tests the entry, not the built artifact (dist is
|
||||
// gitignored); the build itself is exercised by `npm run build:standalone`.
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import * as bitgate from "../src/index.js";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const rootPkg = JSON.parse(readFileSync(join(HERE, "..", "..", "..", "package.json"), "utf8"));
|
||||
|
||||
describe("standalone entry surface", () => {
|
||||
it("re-exports the core engine and the Nostr label codec together", () => {
|
||||
for (const name of [
|
||||
@@ -17,6 +23,7 @@ describe("standalone entry surface", () => {
|
||||
"createPolicyDefinition",
|
||||
"createEventTarget",
|
||||
"ALLOWLIST_POLICY",
|
||||
"VERSION",
|
||||
// nostr label codec
|
||||
"decodeLabels",
|
||||
"encodeLabel",
|
||||
@@ -67,4 +74,15 @@ describe("standalone entry surface", () => {
|
||||
|
||||
expect(decision.transaction?.effect).toBe("deny");
|
||||
});
|
||||
|
||||
it("exports VERSION matching package.json", () => {
|
||||
expect(bitgate.VERSION).toBe(rootPkg.version);
|
||||
});
|
||||
|
||||
it("publishes version onto a window-like global without replacing other keys", () => {
|
||||
const target = { bitgate: { extra: true } };
|
||||
bitgate.exposeBitGateVersion(target);
|
||||
expect(target.bitgate.version).toBe(bitgate.VERSION);
|
||||
expect(target.bitgate.extra).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -34,10 +34,14 @@ both with every descendant. Nothing else to wire.
|
||||
| --- | --- |
|
||||
| `relays` | Comma-separated relay URLs (required) |
|
||||
| `root` | Root administrator pubkey, from your deployment config |
|
||||
| `policy` | `social`, `commerce`, or `admin-only` (default) |
|
||||
| `policy` | `social`, `commerce`, `admin-only` (default), or `allowlist` |
|
||||
| `viewer` | Viewer pubkey, if known at render time |
|
||||
| `application` / `namespace` | Storage and event namespacing |
|
||||
|
||||
`allowlist` is the symmetric twin of a denylist: only publishers with a
|
||||
`user-allow` contribution are shown, unlisted ones are hidden across all four
|
||||
dimensions, and protected actors (root, moderators) are never gated.
|
||||
|
||||
### Target attributes
|
||||
|
||||
Any target-bearing element reads its target from markup:
|
||||
@@ -161,13 +165,44 @@ button teaches nothing about the authority model. The command layer enforces
|
||||
the same check independently — the UI is an explanation, never the gate.
|
||||
|
||||
**Styling is yours.** Elements expose `part` attributes and inherit `color` and
|
||||
`font` from the host. Override the CSS custom properties (`--gov-fg`,
|
||||
`--gov-surface`, `--gov-warn`, `--gov-danger`, `--gov-radius`) or target parts:
|
||||
`font` from the host. Override the CSS custom properties or target parts:
|
||||
|
||||
| Token | Purpose |
|
||||
| --- | --- |
|
||||
| `--bitgate-fg` | Foreground / text (`currentColor`) |
|
||||
| `--bitgate-muted` | Secondary text |
|
||||
| `--bitgate-border` | Control borders |
|
||||
| `--bitgate-surface` | Button / panel background |
|
||||
| `--bitgate-warn` | Warning copy |
|
||||
| `--bitgate-danger` | Danger / destructive accent |
|
||||
| `--bitgate-radius` | Control corner radius |
|
||||
|
||||
Each token prefers `--bitgate-*`, then the deprecated `--gov-*` name
|
||||
(`--gov-fg`, `--gov-muted`, `--gov-border`, `--gov-surface`, `--gov-warn`,
|
||||
`--gov-danger`, `--gov-radius`), then the built-in default. Existing `--gov-*`
|
||||
overrides keep working; new pages should set `--bitgate-*`. `::part()` names
|
||||
are unchanged.
|
||||
|
||||
```css
|
||||
bitgate-veil { --bitgate-warn: #c2410c; }
|
||||
bitgate-veil::part(veil) { border: 1px solid red; }
|
||||
```
|
||||
|
||||
## Version
|
||||
|
||||
The library version is the `package.json` version, injected into browser
|
||||
bundles at build time:
|
||||
|
||||
```js
|
||||
import { VERSION } from "@bitgate/widget";
|
||||
console.log(VERSION); // e.g. "0.1.0"
|
||||
console.log(window.bitgate.version); // same string, for script-tag hosts
|
||||
```
|
||||
|
||||
`window.bitgate` is created if missing; if a host already put an object there,
|
||||
only `.version` is written. Spec / wire versions (`bitgate/1`, policy
|
||||
`schemaVersion`) are independent of this package version.
|
||||
|
||||
## Environment
|
||||
|
||||
`defineBitGateElements()` is safe to call repeatedly and safe to import in
|
||||
|
||||
@@ -19,6 +19,8 @@ import { build, context } from "esbuild";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { bitgateVersionPlugin } from "../../scripts/esbuild-bitgate-version.mjs";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/** @type {import("esbuild").BuildOptions} */
|
||||
@@ -37,6 +39,7 @@ const options = {
|
||||
banner: {
|
||||
js: "/*! BitGate widget — GPL-3.0-or-later — https://github.com/PR0M3TH3AN/Nostr-Governance */",
|
||||
},
|
||||
plugins: [bitgateVersionPlugin()],
|
||||
};
|
||||
|
||||
const watch = process.argv.includes("--watch");
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// Second, nothing here computes policy. The console displays effective state
|
||||
// and issues commands; every accept/reject decision belongs to the runtime.
|
||||
|
||||
import { GovernanceElement, defineElement, escapeHtml, shortenKey } from "./base.js";
|
||||
import { GovernanceElement, defineElement, escapeHtml, shortenKey, themeVar } from "./base.js";
|
||||
|
||||
/**
|
||||
* @typedef {import('@bitgate/runtime').GovernanceCommands} GovernanceCommands
|
||||
@@ -213,10 +213,10 @@ export class GovernanceAdminPanel extends GovernanceElement {
|
||||
this.renderHtml(`
|
||||
<style>
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
td, th { text-align: left; padding: 0.3em 0.5em; border-bottom: 1px solid var(--gov-border); }
|
||||
td, th { text-align: left; padding: 0.3em 0.5em; border-bottom: 1px solid ${themeVar("border")}; }
|
||||
code { font-size: 0.9em; }
|
||||
h3 { margin: 0; font-size: 1em; }
|
||||
.stale { color: var(--gov-warn); }
|
||||
.stale { color: ${themeVar("warn")}; }
|
||||
</style>
|
||||
<div class="stack" part="panel">
|
||||
${
|
||||
|
||||
+54
-13
@@ -27,34 +27,75 @@ export function canRegisterElements() {
|
||||
return typeof globalThis.HTMLElement === "function" && typeof globalThis.customElements === "object";
|
||||
}
|
||||
|
||||
/**
|
||||
* Default values for widget theme tokens.
|
||||
*
|
||||
* `--gov-*` names are the historical prefix (from Nostr-Governance). `--bitgate-*`
|
||||
* is the agreed prefix. Usage sites prefer the new name, then the old one, then
|
||||
* this default, so existing pages and `--gov-*` overrides keep working.
|
||||
*
|
||||
* @type {Readonly<{
|
||||
* fg: string,
|
||||
* muted: string,
|
||||
* border: string,
|
||||
* surface: string,
|
||||
* warn: string,
|
||||
* danger: string,
|
||||
* radius: string,
|
||||
* }>}
|
||||
*/
|
||||
export const THEME_DEFAULTS = Object.freeze({
|
||||
fg: "currentColor",
|
||||
muted: "color-mix(in srgb, currentColor 60%, transparent)",
|
||||
border: "color-mix(in srgb, currentColor 20%, transparent)",
|
||||
surface: "color-mix(in srgb, canvas 96%, currentColor)",
|
||||
warn: "#b45309",
|
||||
danger: "#b91c1c",
|
||||
radius: "8px",
|
||||
});
|
||||
|
||||
/**
|
||||
* CSS value for a theme token: `--bitgate-*` wins, then deprecated `--gov-*`,
|
||||
* then the existing default.
|
||||
*
|
||||
* `--bitgate-*` is deliberately not assigned on `:host`. Assigning it would
|
||||
* make `var(--bitgate-*, …)` always hit and ignore `--gov-*` overrides.
|
||||
*
|
||||
* @param {keyof typeof THEME_DEFAULTS} name
|
||||
* @returns {string}
|
||||
*/
|
||||
export function themeVar(name) {
|
||||
return `var(--bitgate-${name}, var(--gov-${name}, ${THEME_DEFAULTS[name]}))`;
|
||||
}
|
||||
|
||||
/** Base styles shared by every element, scoped inside each shadow root. */
|
||||
export const BASE_STYLES = `
|
||||
:host {
|
||||
--gov-fg: currentColor;
|
||||
--gov-muted: color-mix(in srgb, currentColor 60%, transparent);
|
||||
--gov-border: color-mix(in srgb, currentColor 20%, transparent);
|
||||
--gov-surface: color-mix(in srgb, canvas 96%, currentColor);
|
||||
--gov-warn: #b45309;
|
||||
--gov-danger: #b91c1c;
|
||||
--gov-radius: 8px;
|
||||
--gov-fg: ${THEME_DEFAULTS.fg};
|
||||
--gov-muted: ${THEME_DEFAULTS.muted};
|
||||
--gov-border: ${THEME_DEFAULTS.border};
|
||||
--gov-surface: ${THEME_DEFAULTS.surface};
|
||||
--gov-warn: ${THEME_DEFAULTS.warn};
|
||||
--gov-danger: ${THEME_DEFAULTS.danger};
|
||||
--gov-radius: ${THEME_DEFAULTS.radius};
|
||||
display: block;
|
||||
color: var(--gov-fg);
|
||||
color: ${themeVar("fg")};
|
||||
font: inherit;
|
||||
}
|
||||
:host([hidden]) { display: none; }
|
||||
button {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
background: var(--gov-surface);
|
||||
border: 1px solid var(--gov-border);
|
||||
border-radius: var(--gov-radius);
|
||||
background: ${themeVar("surface")};
|
||||
border: 1px solid ${themeVar("border")};
|
||||
border-radius: ${themeVar("radius")};
|
||||
padding: 0.4em 0.8em;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover:not(:disabled) { border-color: var(--gov-fg); }
|
||||
button:hover:not(:disabled) { border-color: ${themeVar("fg")}; }
|
||||
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.reason {
|
||||
color: var(--gov-muted);
|
||||
color: ${themeVar("muted")};
|
||||
font-size: 0.875em;
|
||||
}
|
||||
.row { display: flex; gap: 0.5em; align-items: center; flex-wrap: wrap; }
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { VERSION } from "@bitgate/core";
|
||||
|
||||
/**
|
||||
* Publish `window.bitgate.version` for script-tag integrators.
|
||||
*
|
||||
* Additive: creates `window.bitgate` when missing, otherwise writes `version`
|
||||
* onto the existing object so a host-provided namespace is not replaced.
|
||||
* No-op in Node (no `window`).
|
||||
*
|
||||
* @param {Record<string, any>} [target] - Defaults to `globalThis.window`
|
||||
* @returns {Record<string, any>|null} The namespace that received `version`
|
||||
*/
|
||||
export function exposeBitGateVersion(target) {
|
||||
const root =
|
||||
target ??
|
||||
(typeof globalThis.window !== "undefined"
|
||||
? /** @type {Record<string, any>} */ (globalThis.window)
|
||||
: null);
|
||||
if (!root || typeof root !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const existing = root.bitgate;
|
||||
const ns =
|
||||
existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
||||
ns.version = VERSION;
|
||||
root.bitgate = ns;
|
||||
return ns;
|
||||
}
|
||||
@@ -21,9 +21,11 @@ export {
|
||||
} from "@bitgate/runtime";
|
||||
|
||||
export {
|
||||
VERSION,
|
||||
SOCIAL_POLICY,
|
||||
COMMERCE_POLICY,
|
||||
ADMIN_ONLY_POLICY,
|
||||
ALLOWLIST_POLICY,
|
||||
POLICY_PRESETS,
|
||||
getPolicyPreset,
|
||||
createPolicyDefinition,
|
||||
@@ -42,6 +44,7 @@ export {
|
||||
|
||||
export {
|
||||
BASE_STYLES,
|
||||
THEME_DEFAULTS,
|
||||
GovernanceElement,
|
||||
canRegisterElements,
|
||||
defineElement,
|
||||
@@ -49,8 +52,11 @@ export {
|
||||
requestContext,
|
||||
shortenKey,
|
||||
targetFromAttributes,
|
||||
themeVar,
|
||||
} from "./base.js";
|
||||
|
||||
export { exposeBitGateVersion } from "./expose-version.js";
|
||||
|
||||
export {
|
||||
BitGateError,
|
||||
BitGateProvider,
|
||||
@@ -76,9 +82,12 @@ export {
|
||||
} from "./admin.js";
|
||||
|
||||
import { defineAdminElements } from "./admin.js";
|
||||
import { exposeBitGateVersion } from "./expose-version.js";
|
||||
import { defineProviderElements } from "./provider.js";
|
||||
import { defineViewerElements } from "./viewer.js";
|
||||
|
||||
exposeBitGateVersion();
|
||||
|
||||
/**
|
||||
* Register every governance element.
|
||||
*
|
||||
|
||||
@@ -151,7 +151,7 @@ export class BitGateProvider extends ProviderBase {
|
||||
|
||||
if (!policy) {
|
||||
throw new Error(
|
||||
`Unknown policy preset "${policyName}". Use social, commerce, admin-only, or set .policy directly.`,
|
||||
`Unknown policy preset "${policyName}". Use social, commerce, admin-only, allowlist, or set .policy directly.`,
|
||||
);
|
||||
}
|
||||
if (relays.length === 0) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
escapeHtml,
|
||||
shortenKey,
|
||||
targetFromAttributes,
|
||||
themeVar,
|
||||
} from "./base.js";
|
||||
|
||||
/**
|
||||
@@ -161,7 +162,7 @@ export class GovernanceVeil extends GovernanceElement {
|
||||
position: absolute; inset: 0; display: grid; place-content: center;
|
||||
gap: 0.5em; text-align: center; padding: 1em;
|
||||
}
|
||||
.warn { color: var(--gov-warn); }
|
||||
.warn { color: ${themeVar("warn")}; }
|
||||
</style>
|
||||
<div class="overlay" part="container">
|
||||
<div class="${blurred ? "blurred" : ""}" part="content"><slot></slot></div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { build } from "esbuild";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { bitgateVersionPlugin, readLibraryVersion } from "../../../scripts/esbuild-bitgate-version.mjs";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const versionJs = join(HERE, "..", "..", "core", "src", "version.js");
|
||||
|
||||
describe("build-time version injection", () => {
|
||||
it("replaces the version module with package.json's version", async () => {
|
||||
const version = readLibraryVersion();
|
||||
const result = await build({
|
||||
entryPoints: [versionJs],
|
||||
bundle: true,
|
||||
write: false,
|
||||
format: "esm",
|
||||
platform: "neutral",
|
||||
plugins: [bitgateVersionPlugin(version)],
|
||||
});
|
||||
|
||||
const bundled = result.outputFiles[0].text;
|
||||
expect(bundled).toContain(`VERSION = ${JSON.stringify(version)}`);
|
||||
expect(bundled).not.toContain("Keep this string in lockstep");
|
||||
});
|
||||
});
|
||||
@@ -135,6 +135,11 @@ describe("<bitgate-provider>", () => {
|
||||
expect(provider.runtime.policies.policy.id).toBe("bitgate-commerce");
|
||||
});
|
||||
|
||||
it("accepts the allowlist policy preset", async () => {
|
||||
const provider = await mountProvider({ policy: "allowlist" });
|
||||
expect(provider.runtime.policies.policy.id).toBe("bitgate-allowlist");
|
||||
});
|
||||
|
||||
it("defaults to administrative-only governance", async () => {
|
||||
const provider = await mountProvider({ policy: null });
|
||||
expect(provider.runtime.policies.policy.id).toBe("bitgate-admin-only");
|
||||
@@ -154,6 +159,7 @@ describe("<bitgate-provider>", () => {
|
||||
const provider = await mountProvider({ policy: "invented" });
|
||||
expect(provider.runtime).toBeNull();
|
||||
expect(provider.getAttribute("data-error")).toMatch(/Unknown policy preset/);
|
||||
expect(provider.getAttribute("data-error")).toMatch(/allowlist/);
|
||||
});
|
||||
|
||||
it("reports a missing relays attribute", async () => {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { BASE_STYLES, THEME_DEFAULTS, themeVar } from "../src/base.js";
|
||||
|
||||
describe("theme tokens", () => {
|
||||
it("resolves --bitgate-* first, then deprecated --gov-*, then the default", () => {
|
||||
for (const name of Object.keys(THEME_DEFAULTS)) {
|
||||
expect(themeVar(/** @type {keyof typeof THEME_DEFAULTS} */ (name))).toBe(
|
||||
`var(--bitgate-${name}, var(--gov-${name}, ${THEME_DEFAULTS[name]}))`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps --gov-* host defaults so existing overrides still apply", () => {
|
||||
for (const [name, value] of Object.entries(THEME_DEFAULTS)) {
|
||||
expect(BASE_STYLES).toContain(`--gov-${name}: ${value}`);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not assign --bitgate-* on :host (that would shadow --gov-* overrides)", () => {
|
||||
expect(BASE_STYLES).not.toMatch(/--bitgate-[a-z]+:/);
|
||||
});
|
||||
|
||||
it("uses the fallback chain at every painted token", () => {
|
||||
expect(BASE_STYLES).toContain(themeVar("fg"));
|
||||
expect(BASE_STYLES).toContain(themeVar("muted"));
|
||||
expect(BASE_STYLES).toContain(themeVar("border"));
|
||||
expect(BASE_STYLES).toContain(themeVar("surface"));
|
||||
expect(BASE_STYLES).toContain(themeVar("radius"));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { VERSION, exposeBitGateVersion } from "../src/index.js";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const rootPkg = JSON.parse(readFileSync(join(HERE, "..", "..", "..", "package.json"), "utf8"));
|
||||
|
||||
describe("VERSION", () => {
|
||||
it("matches package.json", () => {
|
||||
expect(VERSION).toBe(rootPkg.version);
|
||||
});
|
||||
|
||||
it("is published on window.bitgate when the widget is imported", () => {
|
||||
expect(/** @type {any} */ (window).bitgate.version).toBe(VERSION);
|
||||
});
|
||||
|
||||
it("writes version onto an existing window.bitgate object", () => {
|
||||
const target = { bitgate: { extra: true } };
|
||||
exposeBitGateVersion(target);
|
||||
expect(target.bitgate.version).toBe(VERSION);
|
||||
expect(target.bitgate.extra).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* esbuild plugin: replace `@bitgate/core`'s version module with the value from
|
||||
* package.json so browser bundles cannot drift from the package.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
/** @returns {string} */
|
||||
export function readLibraryVersion() {
|
||||
return JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [injected]
|
||||
* @returns {import("esbuild").Plugin}
|
||||
*/
|
||||
export function bitgateVersionPlugin(injected = readLibraryVersion()) {
|
||||
return {
|
||||
name: "bitgate-version",
|
||||
setup(pluginBuild) {
|
||||
pluginBuild.onLoad({ filter: /[/\\]core[/\\]src[/\\]version\.js$/ }, () => ({
|
||||
contents: `export const VERSION = ${JSON.stringify(injected)};\n`,
|
||||
loader: "js",
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user