@luxfi/ui 7.3.2 → 7.4.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/dist/alert.cjs +2 -2
- package/dist/alert.js +1 -1
- package/dist/avatar.cjs +7 -6
- package/dist/avatar.js +7 -5
- package/dist/badge.cjs +36 -38
- package/dist/badge.js +31 -33
- package/dist/bank.cjs +13 -13
- package/dist/bank.d.cts +31 -24
- package/dist/bank.d.ts +31 -24
- package/dist/bank.js +1 -1
- package/dist/button.cjs +3 -3
- package/dist/button.js +1 -1
- package/dist/checkbox.cjs +4 -4
- package/dist/checkbox.d.cts +3 -2
- package/dist/checkbox.d.ts +3 -2
- package/dist/checkbox.js +5 -4
- package/dist/chrome.cjs +950 -0
- package/dist/chrome.d.cts +93 -0
- package/dist/chrome.d.ts +93 -0
- package/dist/chrome.js +921 -0
- package/dist/close-button.cjs +2 -2
- package/dist/close-button.js +1 -1
- package/dist/dialog.cjs +13 -14
- package/dist/dialog.d.cts +58 -5
- package/dist/dialog.d.ts +58 -5
- package/dist/dialog.js +1 -2
- package/dist/drawer.cjs +87 -21
- package/dist/drawer.js +85 -19
- package/dist/expiration-selector.cjs +3 -2
- package/dist/expiration-selector.js +3 -2
- package/dist/greeks-display.cjs +6 -6
- package/dist/greeks-display.js +6 -6
- package/dist/gui.cjs +13 -0
- package/dist/gui.d.cts +2 -0
- package/dist/gui.d.ts +2 -0
- package/dist/gui.js +2 -0
- package/dist/heading.cjs +4 -2
- package/dist/heading.js +4 -2
- package/dist/iam.cjs +206 -0
- package/dist/iam.d.cts +65 -0
- package/dist/iam.d.ts +65 -0
- package/dist/iam.js +201 -0
- package/dist/icon-button.cjs +2 -2
- package/dist/icon-button.js +1 -1
- package/dist/icons.cjs +13 -0
- package/dist/icons.d.cts +1 -0
- package/dist/icons.d.ts +1 -0
- package/dist/icons.js +2 -0
- package/dist/identity.cjs +420 -0
- package/dist/identity.d.cts +64 -0
- package/dist/identity.d.ts +64 -0
- package/dist/identity.js +398 -0
- package/dist/index.cjs +1699 -860
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +1618 -802
- package/dist/input.cjs +13 -3
- package/dist/input.d.cts +6 -0
- package/dist/input.d.ts +6 -0
- package/dist/input.js +13 -3
- package/dist/lux.config.cjs +77 -0
- package/dist/lux.config.d.cts +433 -0
- package/dist/lux.config.d.ts +433 -0
- package/dist/lux.config.js +69 -0
- package/dist/menu.cjs +4 -4
- package/dist/menu.js +4 -4
- package/dist/next.cjs +99 -0
- package/dist/next.d.cts +29 -0
- package/dist/next.d.ts +29 -0
- package/dist/next.js +96 -0
- package/dist/option-chain.cjs +1 -1
- package/dist/option-chain.js +1 -1
- package/dist/option-position.cjs +2 -1
- package/dist/option-position.js +2 -1
- package/dist/pnl-diagram.cjs +8 -6
- package/dist/pnl-diagram.js +8 -6
- package/dist/popover.cjs +8 -9
- package/dist/popover.js +1 -2
- package/dist/progress.cjs +7 -6
- package/dist/progress.js +7 -5
- package/dist/provider.cjs +199 -5
- package/dist/provider.d.cts +16 -3
- package/dist/provider.d.ts +16 -3
- package/dist/provider.js +195 -6
- package/dist/radio.cjs +9 -7
- package/dist/radio.js +10 -7
- package/dist/separator.cjs +3 -1
- package/dist/separator.js +3 -1
- package/dist/slider.cjs +28 -16
- package/dist/slider.js +28 -15
- package/dist/strategy-builder.cjs +2 -1
- package/dist/strategy-builder.js +2 -1
- package/dist/switch.cjs +11 -12
- package/dist/switch.js +12 -12
- package/dist/tag.cjs +38 -41
- package/dist/tag.js +31 -34
- package/dist/textarea.cjs +13 -3
- package/dist/textarea.d.cts +2 -0
- package/dist/textarea.d.ts +2 -0
- package/dist/textarea.js +13 -3
- package/dist/tooltip.cjs +31 -33
- package/dist/tooltip.js +25 -27
- package/dist/use-narrow.cjs +46 -0
- package/dist/use-narrow.d.cts +12 -0
- package/dist/use-narrow.d.ts +12 -0
- package/dist/use-narrow.js +20 -0
- package/dist/vite.cjs +40 -0
- package/dist/vite.d.cts +29 -0
- package/dist/vite.d.ts +29 -0
- package/dist/vite.js +38 -0
- package/dist/white-label.cjs +132 -0
- package/dist/white-label.d.cts +59 -0
- package/dist/white-label.d.ts +59 -0
- package/dist/white-label.js +124 -0
- package/package.json +95 -17
- package/src/avatar.tsx +22 -16
- package/src/bank.tsx +2 -2
- package/src/button.tsx +1 -1
- package/src/checkbox.tsx +15 -10
- package/src/chrome.tsx +459 -0
- package/src/close-button.tsx +1 -1
- package/src/dialog.tsx +1 -1
- package/src/drawer.tsx +83 -10
- package/src/expiration-selector.tsx +3 -2
- package/src/greeks-display.tsx +9 -6
- package/src/gui.ts +17 -0
- package/src/heading.tsx +12 -8
- package/src/iam.ts +170 -0
- package/src/icon-button.tsx +1 -1
- package/src/icons.ts +18 -0
- package/src/identity.tsx +318 -0
- package/src/index.ts +50 -1
- package/src/input.tsx +24 -4
- package/src/lux.config.ts +82 -0
- package/src/menu.tsx +4 -4
- package/src/next.ts +142 -0
- package/src/option-chain.tsx +1 -1
- package/src/option-position.tsx +2 -1
- package/src/pnl-diagram.tsx +11 -6
- package/src/popover.tsx +1 -1
- package/src/progress.tsx +15 -8
- package/src/provider.tsx +92 -12
- package/src/radio.tsx +15 -11
- package/src/separator.tsx +8 -3
- package/src/slider.tsx +35 -19
- package/src/strategy-builder.tsx +3 -2
- package/src/switch.tsx +17 -16
- package/src/textarea.tsx +18 -4
- package/src/tooltip.tsx +4 -23
- package/src/use-narrow.ts +40 -0
- package/src/vite.ts +78 -0
- package/src/white-label.ts +240 -0
- package/tokens.css +544 -319
- package/src/tokens.css +0 -438
|
@@ -32,8 +32,9 @@ function ExpirationPill({ exp, isSelected, onClick }) {
|
|
|
32
32
|
/* @__PURE__ */ jsx("span", { className: "font-medium whitespace-nowrap", children: formatExpDate(exp.date) }),
|
|
33
33
|
/* @__PURE__ */ jsxs("span", { className: cn(
|
|
34
34
|
"font-mono tabular-nums text-[10px]",
|
|
35
|
-
isSelected ? "text-
|
|
36
|
-
|
|
35
|
+
isSelected ? "text-[var(--foreground)]" : "text-[var(--muted-foreground)]",
|
|
36
|
+
// Near expiry is a notice, and a notice in Lux is the loudest neutral.
|
|
37
|
+
exp.dte <= 7 && !isSelected && "text-[var(--color-status-warn)]"
|
|
37
38
|
), children: [
|
|
38
39
|
exp.dte,
|
|
39
40
|
"d"
|
package/dist/greeks-display.cjs
CHANGED
|
@@ -30,12 +30,12 @@ function cn(...inputs) {
|
|
|
30
30
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
31
31
|
}
|
|
32
32
|
var GREEKS = [
|
|
33
|
-
{ key: "delta", label: "Delta", symbol: "\u0394", color: "text-
|
|
34
|
-
{ key: "gamma", label: "Gamma", symbol: "\u0393", color: "text-
|
|
35
|
-
{ key: "theta", label: "Theta", symbol: "\u0398", color: "text-
|
|
36
|
-
{ key: "vega", label: "Vega", symbol: "\u03BD", color: "text-
|
|
37
|
-
{ key: "rho", label: "Rho", symbol: "\u03C1", color: "text-
|
|
38
|
-
{ key: "iv", label: "IV", symbol: "\u03C3", color: "text-
|
|
33
|
+
{ key: "delta", label: "Delta", symbol: "\u0394", color: "text-[var(--foreground)]", barColor: "bg-[var(--foreground)]", decimals: 4, maxMagnitude: 1 },
|
|
34
|
+
{ key: "gamma", label: "Gamma", symbol: "\u0393", color: "text-[var(--chart-1)]", barColor: "bg-[var(--chart-1)]", decimals: 4, maxMagnitude: 0.1 },
|
|
35
|
+
{ key: "theta", label: "Theta", symbol: "\u0398", color: "text-[var(--chart-4)]", barColor: "bg-[var(--chart-4)]", decimals: 4, maxMagnitude: 1 },
|
|
36
|
+
{ key: "vega", label: "Vega", symbol: "\u03BD", color: "text-[var(--chart-2)]", barColor: "bg-[var(--chart-2)]", decimals: 4, maxMagnitude: 1 },
|
|
37
|
+
{ key: "rho", label: "Rho", symbol: "\u03C1", color: "text-[var(--chart-3)]", barColor: "bg-[var(--chart-3)]", decimals: 4, maxMagnitude: 1 },
|
|
38
|
+
{ key: "iv", label: "IV", symbol: "\u03C3", color: "text-[var(--chart-5)]", barColor: "bg-[var(--chart-5)]", decimals: 1, maxMagnitude: 100 }
|
|
39
39
|
];
|
|
40
40
|
function GreekItem({ def, value, compact }) {
|
|
41
41
|
const magnitude = Math.min(Math.abs(value) / def.maxMagnitude, 1);
|
package/dist/greeks-display.js
CHANGED
|
@@ -8,12 +8,12 @@ function cn(...inputs) {
|
|
|
8
8
|
return twMerge(clsx(inputs));
|
|
9
9
|
}
|
|
10
10
|
var GREEKS = [
|
|
11
|
-
{ key: "delta", label: "Delta", symbol: "\u0394", color: "text-
|
|
12
|
-
{ key: "gamma", label: "Gamma", symbol: "\u0393", color: "text-
|
|
13
|
-
{ key: "theta", label: "Theta", symbol: "\u0398", color: "text-
|
|
14
|
-
{ key: "vega", label: "Vega", symbol: "\u03BD", color: "text-
|
|
15
|
-
{ key: "rho", label: "Rho", symbol: "\u03C1", color: "text-
|
|
16
|
-
{ key: "iv", label: "IV", symbol: "\u03C3", color: "text-
|
|
11
|
+
{ key: "delta", label: "Delta", symbol: "\u0394", color: "text-[var(--foreground)]", barColor: "bg-[var(--foreground)]", decimals: 4, maxMagnitude: 1 },
|
|
12
|
+
{ key: "gamma", label: "Gamma", symbol: "\u0393", color: "text-[var(--chart-1)]", barColor: "bg-[var(--chart-1)]", decimals: 4, maxMagnitude: 0.1 },
|
|
13
|
+
{ key: "theta", label: "Theta", symbol: "\u0398", color: "text-[var(--chart-4)]", barColor: "bg-[var(--chart-4)]", decimals: 4, maxMagnitude: 1 },
|
|
14
|
+
{ key: "vega", label: "Vega", symbol: "\u03BD", color: "text-[var(--chart-2)]", barColor: "bg-[var(--chart-2)]", decimals: 4, maxMagnitude: 1 },
|
|
15
|
+
{ key: "rho", label: "Rho", symbol: "\u03C1", color: "text-[var(--chart-3)]", barColor: "bg-[var(--chart-3)]", decimals: 4, maxMagnitude: 1 },
|
|
16
|
+
{ key: "iv", label: "IV", symbol: "\u03C3", color: "text-[var(--chart-5)]", barColor: "bg-[var(--chart-5)]", decimals: 1, maxMagnitude: 100 }
|
|
17
17
|
];
|
|
18
18
|
function GreekItem({ def, value, compact }) {
|
|
19
19
|
const magnitude = Math.min(Math.abs(value) / def.maxMagnitude, 1);
|
package/dist/gui.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var gui = require('@hanzo/gui');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.keys(gui).forEach(function (k) {
|
|
9
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function () { return gui[k]; }
|
|
12
|
+
});
|
|
13
|
+
});
|
package/dist/gui.d.cts
ADDED
package/dist/gui.d.ts
ADDED
package/dist/gui.js
ADDED
package/dist/heading.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var gui = require('@hanzo/gui');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
var clsx = require('clsx');
|
|
6
7
|
var tailwindMerge = require('tailwind-merge');
|
|
@@ -14,7 +15,7 @@ var React__default = /*#__PURE__*/_interopDefault(React);
|
|
|
14
15
|
function cn(...inputs) {
|
|
15
16
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
16
17
|
}
|
|
17
|
-
var LEVEL_TAG = { "1":
|
|
18
|
+
var LEVEL_TAG = { "1": gui.H1, "2": gui.H2, "3": gui.H3 };
|
|
18
19
|
var LEVEL_CLASSES = {
|
|
19
20
|
"1": "text-[18px] leading-[24px] font-medium lg:text-[32px] lg:leading-[40px] lg:tracking-[-0.5px]",
|
|
20
21
|
"2": "text-[16px] leading-[24px] font-medium lg:text-[24px] lg:leading-[32px]",
|
|
@@ -23,13 +24,14 @@ var LEVEL_CLASSES = {
|
|
|
23
24
|
var BASE_CLASSES = "font-heading text-heading";
|
|
24
25
|
var Heading = React__default.default.forwardRef(
|
|
25
26
|
function Heading2({ level, className, mb, size: _size, style: styleProp, ...rest }, ref) {
|
|
26
|
-
const Tag = level ? LEVEL_TAG[level] :
|
|
27
|
+
const Tag = level ? LEVEL_TAG[level] : gui.H2;
|
|
27
28
|
const levelClasses = level ? LEVEL_CLASSES[level] : void 0;
|
|
28
29
|
const mergedStyle = mb !== void 0 ? { ...styleProp, marginBottom: typeof mb === "number" ? `${mb * 4}px` : mb } : styleProp;
|
|
29
30
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
30
31
|
Tag,
|
|
31
32
|
{
|
|
32
33
|
ref,
|
|
34
|
+
unstyled: true,
|
|
33
35
|
className: cn(BASE_CLASSES, levelClasses, className),
|
|
34
36
|
style: mergedStyle,
|
|
35
37
|
...rest
|
package/dist/heading.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { H3, H2, H1 } from '@hanzo/gui';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import { clsx } from 'clsx';
|
|
4
5
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -8,7 +9,7 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
8
9
|
function cn(...inputs) {
|
|
9
10
|
return twMerge(clsx(inputs));
|
|
10
11
|
}
|
|
11
|
-
var LEVEL_TAG = { "1":
|
|
12
|
+
var LEVEL_TAG = { "1": H1, "2": H2, "3": H3 };
|
|
12
13
|
var LEVEL_CLASSES = {
|
|
13
14
|
"1": "text-[18px] leading-[24px] font-medium lg:text-[32px] lg:leading-[40px] lg:tracking-[-0.5px]",
|
|
14
15
|
"2": "text-[16px] leading-[24px] font-medium lg:text-[24px] lg:leading-[32px]",
|
|
@@ -17,13 +18,14 @@ var LEVEL_CLASSES = {
|
|
|
17
18
|
var BASE_CLASSES = "font-heading text-heading";
|
|
18
19
|
var Heading = React.forwardRef(
|
|
19
20
|
function Heading2({ level, className, mb, size: _size, style: styleProp, ...rest }, ref) {
|
|
20
|
-
const Tag = level ? LEVEL_TAG[level] :
|
|
21
|
+
const Tag = level ? LEVEL_TAG[level] : H2;
|
|
21
22
|
const levelClasses = level ? LEVEL_CLASSES[level] : void 0;
|
|
22
23
|
const mergedStyle = mb !== void 0 ? { ...styleProp, marginBottom: typeof mb === "number" ? `${mb * 4}px` : mb } : styleProp;
|
|
23
24
|
return /* @__PURE__ */ jsx(
|
|
24
25
|
Tag,
|
|
25
26
|
{
|
|
26
27
|
ref,
|
|
28
|
+
unstyled: true,
|
|
27
29
|
className: cn(BASE_CLASSES, levelClasses, className),
|
|
28
30
|
style: mergedStyle,
|
|
29
31
|
...rest
|
package/dist/iam.cjs
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/white-label.ts
|
|
4
|
+
var LUX_BRAND = {
|
|
5
|
+
lux: "#7000FF",
|
|
6
|
+
zoo: "#6C5EFB",
|
|
7
|
+
hanzo: "#EA580C",
|
|
8
|
+
pars: "#1C3879"};
|
|
9
|
+
var PARS_GOLD = "#C8A45C";
|
|
10
|
+
var ORGS = {
|
|
11
|
+
lux: {
|
|
12
|
+
org: "lux",
|
|
13
|
+
name: "Lux",
|
|
14
|
+
domain: "lux.network",
|
|
15
|
+
iamDomain: "lux.id",
|
|
16
|
+
accent: LUX_BRAND.lux,
|
|
17
|
+
accentForeground: "#FFFFFF"
|
|
18
|
+
},
|
|
19
|
+
zoo: {
|
|
20
|
+
org: "zoo",
|
|
21
|
+
name: "Zoo",
|
|
22
|
+
domain: "zoo.ngo",
|
|
23
|
+
// Zoo's IdP is `id.zoo.network`, not a `zoo.id` apex — the one row in this
|
|
24
|
+
// table where the issuer is not `<org>.id`, and the one every hand-rolled
|
|
25
|
+
// per-app branding map got wrong.
|
|
26
|
+
iamDomain: "id.zoo.network",
|
|
27
|
+
accent: LUX_BRAND.zoo,
|
|
28
|
+
accentForeground: "#FFFFFF"
|
|
29
|
+
},
|
|
30
|
+
bootnode: {
|
|
31
|
+
org: "bootnode",
|
|
32
|
+
name: "Bootnode",
|
|
33
|
+
domain: "bootno.de",
|
|
34
|
+
iamDomain: "id.bootno.de",
|
|
35
|
+
accent: LUX_BRAND.lux,
|
|
36
|
+
accentForeground: "#FFFFFF"
|
|
37
|
+
},
|
|
38
|
+
hanzo: {
|
|
39
|
+
org: "hanzo",
|
|
40
|
+
name: "Hanzo",
|
|
41
|
+
domain: "hanzo.ai",
|
|
42
|
+
iamDomain: "hanzo.id",
|
|
43
|
+
accent: LUX_BRAND.hanzo,
|
|
44
|
+
accentForeground: "#FFFFFF"
|
|
45
|
+
},
|
|
46
|
+
pars: {
|
|
47
|
+
org: "pars",
|
|
48
|
+
name: "Pars",
|
|
49
|
+
domain: "pars.id",
|
|
50
|
+
iamDomain: "pars.id",
|
|
51
|
+
accent: LUX_BRAND.pars,
|
|
52
|
+
accentForeground: PARS_GOLD
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var DOMAIN_ORG = [
|
|
56
|
+
["lux.network", "lux"],
|
|
57
|
+
["lux.cloud", "lux"],
|
|
58
|
+
["lux.id", "lux"],
|
|
59
|
+
["lux.finance", "lux"],
|
|
60
|
+
["zoo.ngo", "zoo"],
|
|
61
|
+
["zoo.network", "zoo"],
|
|
62
|
+
["zoo.cloud", "zoo"],
|
|
63
|
+
["zoo.id", "zoo"],
|
|
64
|
+
["hanzo.ai", "hanzo"],
|
|
65
|
+
["hanzo.cloud", "hanzo"],
|
|
66
|
+
["hanzo.id", "hanzo"],
|
|
67
|
+
["pars.id", "pars"],
|
|
68
|
+
["parsdao.org", "pars"],
|
|
69
|
+
["bootno.de", "bootnode", "platform"]
|
|
70
|
+
];
|
|
71
|
+
var DEFAULT_ORG = "lux";
|
|
72
|
+
function normalizeHost(host) {
|
|
73
|
+
const noPort = host.trim().toLowerCase().split(":")[0] ?? "";
|
|
74
|
+
return noPort.replace(/\.$/, "");
|
|
75
|
+
}
|
|
76
|
+
function isUnder(host, domain) {
|
|
77
|
+
return host === domain || host.endsWith(`.${domain}`);
|
|
78
|
+
}
|
|
79
|
+
function matchDomain(host) {
|
|
80
|
+
let best;
|
|
81
|
+
for (const entry of DOMAIN_ORG) {
|
|
82
|
+
if (isUnder(host, entry[0]) && (!best || entry[0].length > best[0].length)) {
|
|
83
|
+
best = entry;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return best;
|
|
87
|
+
}
|
|
88
|
+
function appSlug(host, domain, apexApp) {
|
|
89
|
+
const sub = host === domain ? "" : host.slice(0, -(domain.length + 1));
|
|
90
|
+
const leftmost = sub.split(".")[0] ?? "";
|
|
91
|
+
if (leftmost && leftmost !== "www") return leftmost;
|
|
92
|
+
if (apexApp) return apexApp;
|
|
93
|
+
const label = domain.split(".")[0] ?? "";
|
|
94
|
+
const tld = domain.split(".").slice(1).join(".");
|
|
95
|
+
return tld.split(".")[0] || label;
|
|
96
|
+
}
|
|
97
|
+
function isLocal(host) {
|
|
98
|
+
return host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "0.0.0.0" || host === "[::1]" || host.endsWith(".local");
|
|
99
|
+
}
|
|
100
|
+
function resolveWhiteLabel(host) {
|
|
101
|
+
const h = normalizeHost(host ?? "");
|
|
102
|
+
const match = h ? matchDomain(h) : void 0;
|
|
103
|
+
const org = match ? match[1] : DEFAULT_ORG;
|
|
104
|
+
const identity = ORGS[org];
|
|
105
|
+
const app = match ? appSlug(h, match[0], match[2]) : h && isLocal(h) ? "local" : "app";
|
|
106
|
+
return {
|
|
107
|
+
...identity,
|
|
108
|
+
host: h,
|
|
109
|
+
app,
|
|
110
|
+
theme: `dark_${org}`,
|
|
111
|
+
issuer: `https://${identity.iamDomain}`,
|
|
112
|
+
clientId: `${org}-${app}`
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/iam.ts
|
|
117
|
+
var IamError = class extends Error {
|
|
118
|
+
constructor(message, status, unauthenticated) {
|
|
119
|
+
super(message);
|
|
120
|
+
this.name = "IamError";
|
|
121
|
+
this.status = status;
|
|
122
|
+
this.unauthenticated = unauthenticated;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var SIGNED_OUT = /please sign in|authentication required|unauthorized/i;
|
|
126
|
+
function isSignedOut(status, msg) {
|
|
127
|
+
return status === 401 || status === 403 || SIGNED_OUT.test(msg);
|
|
128
|
+
}
|
|
129
|
+
function iamBase(host) {
|
|
130
|
+
const wl = typeof host === "object" && host !== null ? host : resolveWhiteLabel(host);
|
|
131
|
+
return `${wl.issuer}/v1/iam`;
|
|
132
|
+
}
|
|
133
|
+
async function read(call, verb, params) {
|
|
134
|
+
const url = new URL(`${call.base}/${verb}`);
|
|
135
|
+
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, String(v));
|
|
136
|
+
let res;
|
|
137
|
+
try {
|
|
138
|
+
res = await fetch(url.toString(), {
|
|
139
|
+
method: "GET",
|
|
140
|
+
// The bearer is the whole credential. IAM does not allow credentialed
|
|
141
|
+
// CORS, so sending cookies would only get the response blocked.
|
|
142
|
+
headers: call.token ? { Authorization: `Bearer ${call.token}` } : {},
|
|
143
|
+
signal: call.signal
|
|
144
|
+
});
|
|
145
|
+
} catch (e) {
|
|
146
|
+
throw new IamError(e instanceof Error ? e.message : "IAM unreachable", 0, false);
|
|
147
|
+
}
|
|
148
|
+
const body = await res.json().catch(() => ({}));
|
|
149
|
+
const msg = typeof body.msg === "string" ? body.msg : "";
|
|
150
|
+
if (!res.ok || body.status === "error") {
|
|
151
|
+
throw new IamError(msg || `IAM ${verb} failed (${res.status})`, res.status, isSignedOut(res.status, msg));
|
|
152
|
+
}
|
|
153
|
+
return body.data;
|
|
154
|
+
}
|
|
155
|
+
var IAM_PAGE_SIZE = 20;
|
|
156
|
+
var iam = {
|
|
157
|
+
/** The signed-in principal, or null when there is no session. */
|
|
158
|
+
async account(call) {
|
|
159
|
+
try {
|
|
160
|
+
const data = await read(call, "get-account", {});
|
|
161
|
+
return data && data.name ? data : null;
|
|
162
|
+
} catch (e) {
|
|
163
|
+
if (e instanceof IamError && e.unauthenticated) return null;
|
|
164
|
+
throw e;
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
/**
|
|
168
|
+
* One page of the orgs the principal can see. IAM scopes the read to the
|
|
169
|
+
* caller's authority, so a normal member gets their own org and a super
|
|
170
|
+
* admin gets the tenant list — the page never has to decide.
|
|
171
|
+
*/
|
|
172
|
+
async organizations(call, page = 0, query = "", pageSize = IAM_PAGE_SIZE) {
|
|
173
|
+
const params = {
|
|
174
|
+
owner: "admin",
|
|
175
|
+
// IAM orgs live under the reserved `admin` owner
|
|
176
|
+
p: page + 1,
|
|
177
|
+
// the backend page is 1-based
|
|
178
|
+
pageSize,
|
|
179
|
+
sortField: "name",
|
|
180
|
+
sortOrder: "ascending"
|
|
181
|
+
};
|
|
182
|
+
const q = query.trim();
|
|
183
|
+
if (q) {
|
|
184
|
+
params.field = "name";
|
|
185
|
+
params.value = q;
|
|
186
|
+
}
|
|
187
|
+
return await read(call, "get-organizations", params) ?? [];
|
|
188
|
+
},
|
|
189
|
+
/** One organization by name. */
|
|
190
|
+
async organization(call, name) {
|
|
191
|
+
return await read(call, "get-organization", { id: `admin/${name}` }) ?? null;
|
|
192
|
+
},
|
|
193
|
+
/** Members of an organization. */
|
|
194
|
+
async users(call, org) {
|
|
195
|
+
return await read(call, "get-users", { owner: org }) ?? [];
|
|
196
|
+
},
|
|
197
|
+
/** The organization's projects — IAM keys this read by `organization`. */
|
|
198
|
+
async projects(call, org) {
|
|
199
|
+
return await read(call, "get-organization-projects", { organization: org }) ?? [];
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
exports.IAM_PAGE_SIZE = IAM_PAGE_SIZE;
|
|
204
|
+
exports.IamError = IamError;
|
|
205
|
+
exports.iam = iam;
|
|
206
|
+
exports.iamBase = iamBase;
|
package/dist/iam.d.cts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { WhiteLabel } from './white-label.cjs';
|
|
2
|
+
|
|
3
|
+
/** A signed-in principal, as IAM describes it. */
|
|
4
|
+
interface IamAccount {
|
|
5
|
+
readonly owner: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly displayName?: string;
|
|
8
|
+
readonly email?: string;
|
|
9
|
+
readonly avatar?: string;
|
|
10
|
+
readonly isAdmin?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** An organization the principal can see. */
|
|
13
|
+
interface IamOrg {
|
|
14
|
+
readonly owner: string;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly displayName?: string;
|
|
17
|
+
readonly logo?: string;
|
|
18
|
+
}
|
|
19
|
+
/** A project inside an organization (IAM's Org → Workspace → Project tier). */
|
|
20
|
+
interface IamProject {
|
|
21
|
+
readonly owner: string;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly displayName?: string;
|
|
24
|
+
readonly organization?: string;
|
|
25
|
+
readonly workspace?: string;
|
|
26
|
+
readonly isDefault?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A refusal from IAM, carrying enough to tell "not signed in" apart from
|
|
30
|
+
* "signed in, and the answer is genuinely empty" — the distinction a switcher
|
|
31
|
+
* has to render honestly.
|
|
32
|
+
*/
|
|
33
|
+
declare class IamError extends Error {
|
|
34
|
+
readonly status: number;
|
|
35
|
+
readonly unauthenticated: boolean;
|
|
36
|
+
constructor(message: string, status: number, unauthenticated: boolean);
|
|
37
|
+
}
|
|
38
|
+
/** The IAM API root for a host — `lux.cloud` → `https://lux.id/v1/iam`. */
|
|
39
|
+
declare function iamBase(host?: string | null | WhiteLabel): string;
|
|
40
|
+
/** How a read reaches IAM: where, and with whose bearer. */
|
|
41
|
+
interface IamCall {
|
|
42
|
+
readonly base: string;
|
|
43
|
+
readonly token?: string | null;
|
|
44
|
+
readonly signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
/** Rows per page for the org list — small first paint, more on demand. */
|
|
47
|
+
declare const IAM_PAGE_SIZE = 20;
|
|
48
|
+
declare const iam: {
|
|
49
|
+
/** The signed-in principal, or null when there is no session. */
|
|
50
|
+
account(call: IamCall): Promise<IamAccount | null>;
|
|
51
|
+
/**
|
|
52
|
+
* One page of the orgs the principal can see. IAM scopes the read to the
|
|
53
|
+
* caller's authority, so a normal member gets their own org and a super
|
|
54
|
+
* admin gets the tenant list — the page never has to decide.
|
|
55
|
+
*/
|
|
56
|
+
organizations(call: IamCall, page?: number, query?: string, pageSize?: number): Promise<Array<IamOrg>>;
|
|
57
|
+
/** One organization by name. */
|
|
58
|
+
organization(call: IamCall, name: string): Promise<IamOrg | null>;
|
|
59
|
+
/** Members of an organization. */
|
|
60
|
+
users(call: IamCall, org: string): Promise<Array<IamAccount>>;
|
|
61
|
+
/** The organization's projects — IAM keys this read by `organization`. */
|
|
62
|
+
projects(call: IamCall, org: string): Promise<Array<IamProject>>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { IAM_PAGE_SIZE, type IamAccount, type IamCall, IamError, type IamOrg, type IamProject, iam, iamBase };
|
package/dist/iam.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { WhiteLabel } from './white-label.js';
|
|
2
|
+
|
|
3
|
+
/** A signed-in principal, as IAM describes it. */
|
|
4
|
+
interface IamAccount {
|
|
5
|
+
readonly owner: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly displayName?: string;
|
|
8
|
+
readonly email?: string;
|
|
9
|
+
readonly avatar?: string;
|
|
10
|
+
readonly isAdmin?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** An organization the principal can see. */
|
|
13
|
+
interface IamOrg {
|
|
14
|
+
readonly owner: string;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly displayName?: string;
|
|
17
|
+
readonly logo?: string;
|
|
18
|
+
}
|
|
19
|
+
/** A project inside an organization (IAM's Org → Workspace → Project tier). */
|
|
20
|
+
interface IamProject {
|
|
21
|
+
readonly owner: string;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly displayName?: string;
|
|
24
|
+
readonly organization?: string;
|
|
25
|
+
readonly workspace?: string;
|
|
26
|
+
readonly isDefault?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A refusal from IAM, carrying enough to tell "not signed in" apart from
|
|
30
|
+
* "signed in, and the answer is genuinely empty" — the distinction a switcher
|
|
31
|
+
* has to render honestly.
|
|
32
|
+
*/
|
|
33
|
+
declare class IamError extends Error {
|
|
34
|
+
readonly status: number;
|
|
35
|
+
readonly unauthenticated: boolean;
|
|
36
|
+
constructor(message: string, status: number, unauthenticated: boolean);
|
|
37
|
+
}
|
|
38
|
+
/** The IAM API root for a host — `lux.cloud` → `https://lux.id/v1/iam`. */
|
|
39
|
+
declare function iamBase(host?: string | null | WhiteLabel): string;
|
|
40
|
+
/** How a read reaches IAM: where, and with whose bearer. */
|
|
41
|
+
interface IamCall {
|
|
42
|
+
readonly base: string;
|
|
43
|
+
readonly token?: string | null;
|
|
44
|
+
readonly signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
/** Rows per page for the org list — small first paint, more on demand. */
|
|
47
|
+
declare const IAM_PAGE_SIZE = 20;
|
|
48
|
+
declare const iam: {
|
|
49
|
+
/** The signed-in principal, or null when there is no session. */
|
|
50
|
+
account(call: IamCall): Promise<IamAccount | null>;
|
|
51
|
+
/**
|
|
52
|
+
* One page of the orgs the principal can see. IAM scopes the read to the
|
|
53
|
+
* caller's authority, so a normal member gets their own org and a super
|
|
54
|
+
* admin gets the tenant list — the page never has to decide.
|
|
55
|
+
*/
|
|
56
|
+
organizations(call: IamCall, page?: number, query?: string, pageSize?: number): Promise<Array<IamOrg>>;
|
|
57
|
+
/** One organization by name. */
|
|
58
|
+
organization(call: IamCall, name: string): Promise<IamOrg | null>;
|
|
59
|
+
/** Members of an organization. */
|
|
60
|
+
users(call: IamCall, org: string): Promise<Array<IamAccount>>;
|
|
61
|
+
/** The organization's projects — IAM keys this read by `organization`. */
|
|
62
|
+
projects(call: IamCall, org: string): Promise<Array<IamProject>>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { IAM_PAGE_SIZE, type IamAccount, type IamCall, IamError, type IamOrg, type IamProject, iam, iamBase };
|