@happyvertical/smrt-subscriptions 0.60.15 → 0.60.17
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/AGENTS.md +78 -0
- package/README.md +195 -0
- package/dist/billing/apply.d.ts +52 -0
- package/dist/billing/apply.d.ts.map +1 -0
- package/dist/billing/checkout-sessions.d.ts +99 -0
- package/dist/billing/checkout-sessions.d.ts.map +1 -0
- package/dist/billing/config.d.ts +64 -0
- package/dist/billing/config.d.ts.map +1 -0
- package/dist/billing/events.d.ts +49 -0
- package/dist/billing/events.d.ts.map +1 -0
- package/dist/billing/host.d.ts +3 -0
- package/dist/billing/host.d.ts.map +1 -0
- package/dist/billing/index.d.ts +4 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/plans.d.ts +45 -0
- package/dist/billing/plans.d.ts.map +1 -0
- package/dist/billing/signing.d.ts +24 -0
- package/dist/billing/signing.d.ts.map +1 -0
- package/dist/billing/types.d.ts +265 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1648 -26
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +313 -4
- package/dist/models/BillingCheckoutSession.d.ts +39 -0
- package/dist/models/BillingCheckoutSession.d.ts.map +1 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/recipes.d.ts +1 -1
- package/dist/smrt-knowledge.json +101 -8
- package/dist/smrt-knowledge.okf.md +5 -2
- package/dist/svelte/CommercialOverview.svelte +37 -11
- package/dist/svelte/PlanPicker.svelte +18 -17
- package/dist/svelte/SubscriptionSummary.svelte +13 -11
- package/dist/svelte/UsageThresholds.svelte +21 -17
- package/dist/vault-account/actions.d.ts +24 -0
- package/dist/vault-account/actions.d.ts.map +1 -0
- package/dist/vault-account/errors.d.ts +20 -0
- package/dist/vault-account/errors.d.ts.map +1 -0
- package/dist/vault-account/index.d.ts +5 -0
- package/dist/vault-account/index.d.ts.map +1 -0
- package/dist/vault-account/relay-admin-client.d.ts +10 -0
- package/dist/vault-account/relay-admin-client.d.ts.map +1 -0
- package/dist/vault-account/types.d.ts +115 -0
- package/dist/vault-account/types.d.ts.map +1 -0
- package/package.json +9 -7
|
@@ -5,10 +5,10 @@ description: "Tenant subscriptions, entitlement resolution, usage thresholds, an
|
|
|
5
5
|
status: "stable"
|
|
6
6
|
generated:
|
|
7
7
|
by: "smrt/scripts/generate-knowledge-okf.ts"
|
|
8
|
-
at: "2026-11-
|
|
8
|
+
at: "2026-11-09T23:09:38Z"
|
|
9
9
|
audience: "internal"
|
|
10
10
|
evidence_level: "source-inspected"
|
|
11
|
-
source_revision: "sha256:
|
|
11
|
+
source_revision: "sha256:48d3599db9578e435280b3dd42751090babf37fc1f9fc84da681314f14dc5579"
|
|
12
12
|
sources:
|
|
13
13
|
- id: "manifest"
|
|
14
14
|
resource: "https://github.com/happyvertical/smrt/blob/main/packages/subscriptions/package.json"
|
|
@@ -37,6 +37,7 @@ sources:
|
|
|
37
37
|
- [smrt-features](/components/smrt-features.md)
|
|
38
38
|
- [smrt-tenancy](/components/smrt-tenancy.md)
|
|
39
39
|
- [smrt-ui](/components/smrt-ui.md)
|
|
40
|
+
- [smrt-users](/components/smrt-users.md)
|
|
40
41
|
- [smrt-vitest](/components/smrt-vitest.md)
|
|
41
42
|
|
|
42
43
|
## SDK dependencies
|
|
@@ -54,6 +55,8 @@ sources:
|
|
|
54
55
|
|
|
55
56
|
- `BillingAdjustment` (collection `billingadjustments`, extends `SmrtObject`)
|
|
56
57
|
- `BillingAdjustmentCollection` (collection `billingadjustments`, extends `SmrtCollection`)
|
|
58
|
+
- `BillingCheckoutSession` (collection `billingcheckoutsessions`, extends `SmrtObject`)
|
|
59
|
+
- `BillingCheckoutSessionCollection` (collection `billingcheckoutsessions`, extends `SmrtCollection`)
|
|
57
60
|
- `ClientCharge` (collection `clientcharges`, extends `SmrtObject`)
|
|
58
61
|
- `ClientChargeCollection` (collection `clientcharges`, extends `SmrtCollection`)
|
|
59
62
|
- `CreditGrant` (collection `creditgrants`, extends `SmrtObject`)
|
|
@@ -41,11 +41,11 @@ function formatCharge(metric: CommercialMetricView): string {
|
|
|
41
41
|
}
|
|
42
42
|
</script>
|
|
43
43
|
|
|
44
|
-
<section class="commercial-
|
|
44
|
+
<section class="smrt-commercial-overview__root" aria-label={t(M['subscriptions.commercial.overview_label'])} data-smrt-component="CommercialOverview">
|
|
45
45
|
{#each metrics as metric (metric.metricKey)}
|
|
46
|
-
<article>
|
|
47
|
-
<header><strong>{metric.metricKey}</strong><span class:
|
|
48
|
-
<dl>
|
|
46
|
+
<article data-smrt-part="row">
|
|
47
|
+
<header data-smrt-part="header"><strong>{metric.metricKey}</strong><span class:smrt-commercial-overview__blocked={metric.thresholdState === 'blocked'} data-smrt-part="indicator">{metric.thresholdState ?? 'ok'}</span></header>
|
|
48
|
+
<dl data-smrt-part="body">
|
|
49
49
|
<div><dt>Usage</dt><dd>{metric.usage}</dd></div>
|
|
50
50
|
<div><dt>Allowance</dt><dd>{metric.allowance ?? '—'}</dd></div>
|
|
51
51
|
<div><dt>Forecast</dt><dd>{metric.forecast ?? '—'}</dd></div>
|
|
@@ -57,13 +57,39 @@ function formatCharge(metric: CommercialMetricView): string {
|
|
|
57
57
|
|
|
58
58
|
<style>
|
|
59
59
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
60
|
+
@layer smrt.structure {
|
|
61
|
+
.smrt-commercial-overview__root {
|
|
62
|
+
display:grid;
|
|
63
|
+
gap:.75rem;
|
|
64
|
+
}
|
|
65
|
+
article {
|
|
66
|
+
padding:1rem;
|
|
67
|
+
}
|
|
68
|
+
header, dl, dl div {
|
|
69
|
+
display:flex;
|
|
70
|
+
justify-content:space-between;
|
|
71
|
+
gap:1rem;
|
|
72
|
+
}
|
|
73
|
+
dl {
|
|
74
|
+
flex-wrap:wrap;
|
|
75
|
+
margin:.75rem 0 0;
|
|
76
|
+
}
|
|
77
|
+
dd {
|
|
78
|
+
margin:0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
60
82
|
@layer smrt.theme {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
dt {
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
article {
|
|
84
|
+
border:var(--smrt-hairline);
|
|
85
|
+
border-radius:var(--smrt-radius-md);
|
|
86
|
+
}
|
|
87
|
+
dt {
|
|
88
|
+
color:var(--smrt-color-on-surface-variant);
|
|
89
|
+
}
|
|
90
|
+
.smrt-commercial-overview__blocked {
|
|
91
|
+
color:var(--smrt-color-error);
|
|
92
|
+
}
|
|
68
93
|
}
|
|
94
|
+
|
|
69
95
|
</style>
|
|
@@ -91,13 +91,13 @@ let {
|
|
|
91
91
|
<!-- raw-primitive-allow: pressable selection card (aria-pressed toggle with multi-line plan summary), not a standard action button -->
|
|
92
92
|
<button
|
|
93
93
|
aria-pressed={plan.planKey === selectedPlanKey}
|
|
94
|
-
class:
|
|
94
|
+
class:smrt-plan-picker__selected={plan.planKey === selectedPlanKey}
|
|
95
95
|
class="smrt-plan-picker__plan"
|
|
96
96
|
type="button"
|
|
97
|
-
onclick={() => onSelect?.(plan)}
|
|
97
|
+
onclick={() => onSelect?.(plan)} data-smrt-part="trigger"
|
|
98
98
|
>
|
|
99
|
-
<span class="smrt-plan-picker__name">{plan.name}</span>
|
|
100
|
-
<span class="smrt-plan-picker__price">
|
|
99
|
+
<span class="smrt-plan-picker__name" data-smrt-part="title">{plan.name}</span>
|
|
100
|
+
<span class="smrt-plan-picker__price" data-smrt-part="value">
|
|
101
101
|
{#if plan.pricingMode === 'free'}
|
|
102
102
|
{t(M['subscriptions.picker.free'])}
|
|
103
103
|
{:else if plan.pricingMode === 'pay_what_you_can'}
|
|
@@ -112,9 +112,9 @@ let {
|
|
|
112
112
|
{/if}
|
|
113
113
|
</span>
|
|
114
114
|
{#if plan.description}
|
|
115
|
-
<span class="smrt-plan-picker__description">{plan.description}</span>
|
|
115
|
+
<span class="smrt-plan-picker__description" data-smrt-part="description">{plan.description}</span>
|
|
116
116
|
{/if}
|
|
117
|
-
<span class="smrt-plan-picker__features">
|
|
117
|
+
<span class="smrt-plan-picker__features" data-smrt-part="group">
|
|
118
118
|
{featureCount(plan)} features
|
|
119
119
|
</span>
|
|
120
120
|
</button>
|
|
@@ -123,40 +123,40 @@ let {
|
|
|
123
123
|
|
|
124
124
|
<style>
|
|
125
125
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
126
|
-
@layer smrt.
|
|
126
|
+
@layer smrt.structure {
|
|
127
127
|
.smrt-plan-picker {
|
|
128
128
|
display: grid;
|
|
129
129
|
gap: 0.75rem;
|
|
130
130
|
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
|
131
131
|
}
|
|
132
|
-
|
|
133
132
|
.smrt-plan-picker__plan {
|
|
134
133
|
align-items: flex-start;
|
|
135
|
-
background: var(--smrt-color-surface);
|
|
136
|
-
border: var(--smrt-hairline);
|
|
137
|
-
border-radius: var(--smrt-radius-md);
|
|
138
|
-
color: inherit;
|
|
139
134
|
cursor: pointer;
|
|
140
135
|
display: grid;
|
|
141
136
|
gap: 0.45rem;
|
|
142
137
|
padding: 1rem;
|
|
143
|
-
text-align: left;
|
|
144
138
|
}
|
|
139
|
+
}
|
|
145
140
|
|
|
146
|
-
|
|
141
|
+
@layer smrt.theme {
|
|
142
|
+
.smrt-plan-picker__plan {
|
|
143
|
+
background: var(--smrt-color-surface);
|
|
144
|
+
border: var(--smrt-hairline);
|
|
145
|
+
border-radius: var(--smrt-radius-md);
|
|
146
|
+
color: inherit;
|
|
147
|
+
text-align: left;
|
|
148
|
+
}
|
|
149
|
+
.smrt-plan-picker__plan.smrt-plan-picker__selected {
|
|
147
150
|
border-color: var(--smrt-color-primary);
|
|
148
151
|
box-shadow: 0 0 0 1px var(--smrt-color-primary);
|
|
149
152
|
}
|
|
150
|
-
|
|
151
153
|
.smrt-plan-picker__name {
|
|
152
154
|
font-weight: var(--smrt-typography-weight-bold, 650);
|
|
153
155
|
}
|
|
154
|
-
|
|
155
156
|
.smrt-plan-picker__price {
|
|
156
157
|
font-size: var(--smrt-typography-title-large-size, 1.25rem);
|
|
157
158
|
font-weight: var(--smrt-typography-weight-bold, 700);
|
|
158
159
|
}
|
|
159
|
-
|
|
160
160
|
.smrt-plan-picker__price small,
|
|
161
161
|
.smrt-plan-picker__description,
|
|
162
162
|
.smrt-plan-picker__features {
|
|
@@ -164,4 +164,5 @@ let {
|
|
|
164
164
|
font-size: var(--smrt-typography-body-medium-size, 0.875rem);
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
+
|
|
167
168
|
</style>
|
|
@@ -48,7 +48,7 @@ const periodLabel = $derived(
|
|
|
48
48
|
|
|
49
49
|
<section class="smrt-subscription-summary" data-smrt-component="SubscriptionSummary">
|
|
50
50
|
<div>
|
|
51
|
-
<p class="smrt-subscription-summary__label">{t(M['subscriptions.summary.current_plan'])}</p>
|
|
51
|
+
<p class="smrt-subscription-summary__label" data-smrt-part="label">{t(M['subscriptions.summary.current_plan'])}</p>
|
|
52
52
|
<h2>{resolution?.planKey ?? t(M['subscriptions.summary.no_active_plan'])}</h2>
|
|
53
53
|
</div>
|
|
54
54
|
<dl>
|
|
@@ -75,34 +75,36 @@ const periodLabel = $derived(
|
|
|
75
75
|
|
|
76
76
|
<style>
|
|
77
77
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
78
|
-
@layer smrt.
|
|
78
|
+
@layer smrt.structure {
|
|
79
79
|
.smrt-subscription-summary {
|
|
80
80
|
align-items: center;
|
|
81
81
|
display: flex;
|
|
82
82
|
gap: 1rem;
|
|
83
83
|
justify-content: space-between;
|
|
84
84
|
}
|
|
85
|
-
|
|
86
85
|
.smrt-subscription-summary h2,
|
|
87
86
|
.smrt-subscription-summary__label {
|
|
88
87
|
margin: 0;
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
.smrt-subscription-summary__label,
|
|
92
|
-
.smrt-subscription-summary dt {
|
|
93
|
-
color: var(--smrt-color-on-surface-variant);
|
|
94
|
-
font-size: var(--smrt-typography-label-medium-size, 0.8rem);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
89
|
.smrt-subscription-summary dl {
|
|
98
90
|
display: flex;
|
|
99
91
|
gap: 1rem;
|
|
100
92
|
margin: 0;
|
|
101
93
|
}
|
|
94
|
+
.smrt-subscription-summary dd {
|
|
95
|
+
margin: 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
102
98
|
|
|
99
|
+
@layer smrt.theme {
|
|
100
|
+
.smrt-subscription-summary__label,
|
|
101
|
+
.smrt-subscription-summary dt {
|
|
102
|
+
color: var(--smrt-color-on-surface-variant);
|
|
103
|
+
font-size: var(--smrt-typography-label-medium-size, 0.8rem);
|
|
104
|
+
}
|
|
103
105
|
.smrt-subscription-summary dd {
|
|
104
106
|
font-weight: var(--smrt-typography-weight-bold, 650);
|
|
105
|
-
margin: 0;
|
|
106
107
|
}
|
|
107
108
|
}
|
|
109
|
+
|
|
108
110
|
</style>
|
|
@@ -12,11 +12,12 @@ let {
|
|
|
12
12
|
<div class="smrt-usage-thresholds" data-smrt-component="UsageThresholds">
|
|
13
13
|
{#each evaluations as evaluation (`${evaluation.threshold.metricKey}:${evaluation.usage.windowStart.toISOString()}`)}
|
|
14
14
|
<article
|
|
15
|
-
class:
|
|
16
|
-
class:
|
|
15
|
+
class:smrt-usage-thresholds__warn={evaluation.state === 'warn'}
|
|
16
|
+
class:smrt-usage-thresholds__blocked={evaluation.state === 'blocked'}
|
|
17
17
|
class="smrt-usage-thresholds__row"
|
|
18
|
+
data-smrt-part="row"
|
|
18
19
|
>
|
|
19
|
-
<div class="smrt-usage-thresholds__header">
|
|
20
|
+
<div class="smrt-usage-thresholds__header" data-smrt-part="header">
|
|
20
21
|
<strong>{evaluation.threshold.label ?? evaluation.threshold.metricKey}</strong>
|
|
21
22
|
<span>{evaluation.usage.quantity} / {evaluation.threshold.limit}</span>
|
|
22
23
|
</div>
|
|
@@ -34,38 +35,41 @@ let {
|
|
|
34
35
|
|
|
35
36
|
<style>
|
|
36
37
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
37
|
-
@layer smrt.
|
|
38
|
+
@layer smrt.structure {
|
|
38
39
|
.smrt-usage-thresholds {
|
|
39
40
|
display: grid;
|
|
40
41
|
gap: 0.75rem;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
43
|
.smrt-usage-thresholds__row {
|
|
44
|
-
border: var(--smrt-hairline);
|
|
45
|
-
border-radius: var(--smrt-radius-md);
|
|
46
44
|
display: grid;
|
|
47
45
|
gap: 0.45rem;
|
|
48
46
|
padding: 0.875rem;
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
.smrt-usage-thresholds__row.warn {
|
|
52
|
-
border-color: var(--smrt-color-warning);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.smrt-usage-thresholds__row.blocked {
|
|
56
|
-
border-color: var(--smrt-color-error);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
48
|
.smrt-usage-thresholds__header {
|
|
60
49
|
align-items: center;
|
|
61
50
|
display: flex;
|
|
62
51
|
justify-content: space-between;
|
|
63
52
|
}
|
|
53
|
+
.smrt-usage-thresholds p {
|
|
54
|
+
margin: 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
64
57
|
|
|
58
|
+
@layer smrt.theme {
|
|
59
|
+
.smrt-usage-thresholds__row {
|
|
60
|
+
border: var(--smrt-hairline);
|
|
61
|
+
border-radius: var(--smrt-radius-md);
|
|
62
|
+
}
|
|
63
|
+
.smrt-usage-thresholds__row.smrt-usage-thresholds__warn {
|
|
64
|
+
border-color: var(--smrt-color-warning);
|
|
65
|
+
}
|
|
66
|
+
.smrt-usage-thresholds__row.smrt-usage-thresholds__blocked {
|
|
67
|
+
border-color: var(--smrt-color-error);
|
|
68
|
+
}
|
|
65
69
|
.smrt-usage-thresholds p {
|
|
66
70
|
color: var(--smrt-color-on-surface-variant);
|
|
67
71
|
font-size: var(--smrt-typography-body-medium-size, 0.875rem);
|
|
68
|
-
margin: 0;
|
|
69
72
|
}
|
|
70
73
|
}
|
|
74
|
+
|
|
71
75
|
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ProvisionRestartAccountInput, ProvisionRestartAccountResult, SupersedeVaultAccountInput, UnsupersedeVaultAccountInput } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Replace `oldAccount` by `newAccount` (relay admin `PUT
|
|
4
|
+
* .../superseded-by`). The new account must already hold an active device
|
|
5
|
+
* (see {@link provisionRestartAccount}); the relay enforces that, and no
|
|
6
|
+
* chains or loops. Idempotent for the same target.
|
|
7
|
+
*/
|
|
8
|
+
export declare function supersedeVaultAccount(input: SupersedeVaultAccountInput): Promise<'superseded' | 'unchanged'>;
|
|
9
|
+
/**
|
|
10
|
+
* Undo a supersession (relay admin `DELETE .../superseded-by`), for an
|
|
11
|
+
* operator mistake or a restart that never reached the working device. Returns
|
|
12
|
+
* false when `oldAccount` was not superseded.
|
|
13
|
+
*/
|
|
14
|
+
export declare function unsupersedeVaultAccount(input: UnsupersedeVaultAccountInput): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Step 1 of the restart flow (app-runtime README, "Restarting when the
|
|
17
|
+
* recovery secret is lost"), before the supersede: allocate a fresh vault
|
|
18
|
+
* account id, link it to the caller's tenant (so `backup.vault` resolves for
|
|
19
|
+
* it), and register the working device's relay signing key in it (relay admin
|
|
20
|
+
* `PUT .../devices/:device`). The id is minted per call, so a retry after a
|
|
21
|
+
* crash makes another empty account unless `newAccountId` is pinned.
|
|
22
|
+
*/
|
|
23
|
+
export declare function provisionRestartAccount(input: ProvisionRestartAccountInput): Promise<ProvisionRestartAccountResult>;
|
|
24
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/vault-account/actions.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,4BAA4B,EAI7B,MAAM,YAAY,CAAC;AA2KpB;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,YAAY,GAAG,WAAW,CAAC,CAYrC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,6BAA6B,CAAC,CA6CxC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StepUpRequiredError } from '@happyvertical/smrt-users/step-up';
|
|
2
|
+
export { StepUpRequiredError };
|
|
3
|
+
/** Stable machine code of {@link NotOwnerError}. */
|
|
4
|
+
export declare const NOT_OWNER_CODE = "not_owner";
|
|
5
|
+
/**
|
|
6
|
+
* The caller is not the owner of the tenant that holds the account: a member,
|
|
7
|
+
* a device-only or layered session, no session, or another tenant's account.
|
|
8
|
+
* One message and code for all of them, so nothing about the account or the
|
|
9
|
+
* tenant leaks.
|
|
10
|
+
*/
|
|
11
|
+
export declare class NotOwnerError extends Error {
|
|
12
|
+
readonly code = "not_owner";
|
|
13
|
+
constructor(message?: string);
|
|
14
|
+
}
|
|
15
|
+
/** A refusal after both checks passed (the relay or directory said no). */
|
|
16
|
+
export declare class VaultAccountAdminError extends Error {
|
|
17
|
+
readonly code: string;
|
|
18
|
+
constructor(code: string, message?: string);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/vault-account/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAExE,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAE/B,oDAAoD;AACpD,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,eAAkB;gBAEnB,OAAO,SAAwC;CAI5D;AAED,2EAA2E;AAC3E,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,SAAqC;CAKvE"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { provisionRestartAccount, supersedeVaultAccount, unsupersedeVaultAccount, } from './actions.js';
|
|
2
|
+
export { NOT_OWNER_CODE, NotOwnerError, StepUpRequiredError, VaultAccountAdminError, } from './errors.js';
|
|
3
|
+
export { createVaultRelayAdminClient, type VaultRelayAdminClientOptions, } from './relay-admin-client.js';
|
|
4
|
+
export type { ProvisionRestartAccountInput, ProvisionRestartAccountResult, SupersedeVaultAccountInput, UnsupersedeVaultAccountInput, VaultAccountAction, VaultAccountAdminDeps, VaultAccountAuditEntry, VaultAccountAuditSink, VaultAccountDenial, VaultAccountDirectory, VaultRelayAdminPort, } from './types.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vault-account/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,2BAA2B,EAC3B,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,4BAA4B,EAC5B,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { VaultRelayAdminPort } from './types.js';
|
|
2
|
+
export interface VaultRelayAdminClientOptions {
|
|
3
|
+
/** Admin listener origin, e.g. `http://vault-relay-admin:8081`. */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** `VAULT_RELAY_ADMIN_TOKEN`. */
|
|
6
|
+
adminToken: string;
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
}
|
|
9
|
+
export declare function createVaultRelayAdminClient(options: VaultRelayAdminClientOptions): VaultRelayAdminPort;
|
|
10
|
+
//# sourceMappingURL=relay-admin-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-admin-client.d.ts","sourceRoot":"","sources":["../../src/vault-account/relay-admin-client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,4BAA4B;IAC3C,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAWD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,mBAAmB,CA8ErB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the hosted vault-account admin surface (#4841).
|
|
3
|
+
*
|
|
4
|
+
* Everything the gated actions touch is an injected port, so no HTTP server or
|
|
5
|
+
* real relay is needed to run or test them. `VaultRelay` from
|
|
6
|
+
* `@happyvertical/smrt-vault-relay` satisfies {@link VaultRelayAdminPort}
|
|
7
|
+
* structurally (this package cannot depend on it: the relay depends on this
|
|
8
|
+
* package), and {@link createVaultRelayAdminClient} implements it over the
|
|
9
|
+
* relay's admin HTTP API.
|
|
10
|
+
*/
|
|
11
|
+
import type { StepUpSession } from '@happyvertical/smrt-users/step-up';
|
|
12
|
+
/** The three gated actions. */
|
|
13
|
+
export type VaultAccountAction = 'supersede' | 'unsupersede' | 'provision_restart_account';
|
|
14
|
+
/** Why an attempt was refused. The caller sees only the error code. */
|
|
15
|
+
export type VaultAccountDenial = 'not_owner' | 'step_up_required';
|
|
16
|
+
/**
|
|
17
|
+
* The relay admin operations the actions use: the account-layer hooks of
|
|
18
|
+
* `VaultRelay` (`registerDevice`, `supersedeAccount`, `clearSupersession`).
|
|
19
|
+
*/
|
|
20
|
+
export interface VaultRelayAdminPort {
|
|
21
|
+
/** Register a device's raw Ed25519 public key (base64url) in an account. */
|
|
22
|
+
registerDevice(accountId: string, deviceId: string, publicKey: string): Promise<'registered' | 'unchanged' | 'conflict'>;
|
|
23
|
+
/** Replace `accountId` by `supersededBy`. Idempotent for the same target. */
|
|
24
|
+
supersedeAccount(accountId: string, supersededBy: string): Promise<'superseded' | 'unchanged'>;
|
|
25
|
+
/** Undo a supersession; false when there was none. */
|
|
26
|
+
clearSupersession(accountId: string): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The host's account directory: which tenant a vault account belongs to (the
|
|
30
|
+
* same mapping the relay's `tenantForAccount` entitlement lookup reads, so
|
|
31
|
+
* `backup.vault` resolves for a linked account).
|
|
32
|
+
*/
|
|
33
|
+
export interface VaultAccountDirectory {
|
|
34
|
+
/** The tenant that owns `accountId`, or null when unknown. */
|
|
35
|
+
tenantForAccount(accountId: string): string | null | Promise<string | null>;
|
|
36
|
+
/** Record that `accountId` belongs to `tenantId`. Idempotent. */
|
|
37
|
+
linkAccount(tenantId: string, accountId: string): void | Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One audit record. Carries ids, codes and times only: never a password, a
|
|
41
|
+
* session id, a token, a key or a recovery secret.
|
|
42
|
+
*/
|
|
43
|
+
export interface VaultAccountAuditEntry {
|
|
44
|
+
/** ISO time. */
|
|
45
|
+
at: string;
|
|
46
|
+
action: VaultAccountAction;
|
|
47
|
+
/**
|
|
48
|
+
* `denied`: refused before any relay call. `authorized`: both checks
|
|
49
|
+
* passed, written before the relay call. `succeeded` / `failed`: the relay
|
|
50
|
+
* call's outcome.
|
|
51
|
+
*/
|
|
52
|
+
outcome: 'denied' | 'authorized' | 'succeeded' | 'failed';
|
|
53
|
+
/** The caller's user id; null when there was no usable session. */
|
|
54
|
+
actorUserId: string | null;
|
|
55
|
+
tenantId: string | null;
|
|
56
|
+
oldAccountId?: string;
|
|
57
|
+
newAccountId?: string;
|
|
58
|
+
deviceId?: string;
|
|
59
|
+
/** Set when `outcome` is `denied`. */
|
|
60
|
+
denial?: VaultAccountDenial;
|
|
61
|
+
/** Set when `outcome` is `failed`: an error name or code, never a message. */
|
|
62
|
+
errorCode?: string;
|
|
63
|
+
/** The relay's result word, when the call returned one. */
|
|
64
|
+
result?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface VaultAccountAuditSink {
|
|
67
|
+
record(entry: VaultAccountAuditEntry): void | Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
/** Collaborators and policy shared by every gated action. */
|
|
70
|
+
export interface VaultAccountAdminDeps {
|
|
71
|
+
relay: VaultRelayAdminPort;
|
|
72
|
+
directory: VaultAccountDirectory;
|
|
73
|
+
audit: VaultAccountAuditSink;
|
|
74
|
+
/**
|
|
75
|
+
* Longest age of the owner's last authentication, in seconds. Default 300
|
|
76
|
+
* (5 minutes); at most one hour.
|
|
77
|
+
*/
|
|
78
|
+
stepUpMaxAgeSeconds?: number;
|
|
79
|
+
/**
|
|
80
|
+
* Replaces the default owner test (`isTenantOwnerSession`) for hosts with a
|
|
81
|
+
* custom owner role. It must still refuse device-only and layered sessions.
|
|
82
|
+
*/
|
|
83
|
+
isOwner?: (session: StepUpSession) => boolean;
|
|
84
|
+
/** Clock, injectable for tests. */
|
|
85
|
+
now?: () => Date;
|
|
86
|
+
}
|
|
87
|
+
export interface SupersedeVaultAccountInput extends VaultAccountAdminDeps {
|
|
88
|
+
/** The caller's session context, from `SessionService.loadSessionContext`. */
|
|
89
|
+
session: StepUpSession | null | undefined;
|
|
90
|
+
oldAccount: string;
|
|
91
|
+
newAccount: string;
|
|
92
|
+
}
|
|
93
|
+
export interface UnsupersedeVaultAccountInput extends VaultAccountAdminDeps {
|
|
94
|
+
session: StepUpSession | null | undefined;
|
|
95
|
+
oldAccount: string;
|
|
96
|
+
}
|
|
97
|
+
export interface ProvisionRestartAccountInput extends VaultAccountAdminDeps {
|
|
98
|
+
session: StepUpSession | null | undefined;
|
|
99
|
+
/** The account being replaced; must belong to the caller's tenant. */
|
|
100
|
+
oldAccount: string;
|
|
101
|
+
/** The working device's relay signing key id. */
|
|
102
|
+
deviceId: string;
|
|
103
|
+
/** Its raw Ed25519 public key, base64url. */
|
|
104
|
+
devicePublicKey: string;
|
|
105
|
+
/** Mints the new account id. Default `crypto.randomUUID()`. */
|
|
106
|
+
newAccountId?: () => string | Promise<string>;
|
|
107
|
+
}
|
|
108
|
+
export interface ProvisionRestartAccountResult {
|
|
109
|
+
/** The new vault account, linked to the caller's tenant. */
|
|
110
|
+
accountId: string;
|
|
111
|
+
deviceId: string;
|
|
112
|
+
/** `registered` the first time, `unchanged` on a retry. */
|
|
113
|
+
device: 'registered' | 'unchanged';
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/vault-account/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-subscriptions",
|
|
3
|
-
"version": "0.60.
|
|
3
|
+
"version": "0.60.17",
|
|
4
4
|
"description": "Tenant subscriptions, entitlement resolution, usage thresholds, and subscription UI for SMRT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"smrtRawPrimitives": "strict",
|
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@happyvertical/smrt-core": "0.60.
|
|
30
|
-
"@happyvertical/smrt-tenancy": "0.60.
|
|
31
|
-
"@happyvertical/smrt-ui": "0.60.
|
|
32
|
-
"@happyvertical/
|
|
29
|
+
"@happyvertical/smrt-core": "0.60.17",
|
|
30
|
+
"@happyvertical/smrt-tenancy": "0.60.17",
|
|
31
|
+
"@happyvertical/smrt-ui": "0.60.17",
|
|
32
|
+
"@happyvertical/smrt-users": "0.60.17",
|
|
33
|
+
"@happyvertical/sql": "^0.112.2"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
35
36
|
"svelte": "^5.57.0"
|
|
@@ -40,8 +41,9 @@
|
|
|
40
41
|
}
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@happyvertical/
|
|
44
|
-
"@happyvertical/smrt-
|
|
44
|
+
"@happyvertical/accounting": "^0.112.2",
|
|
45
|
+
"@happyvertical/smrt-features": "0.60.17",
|
|
46
|
+
"@happyvertical/smrt-vitest": "0.60.17",
|
|
45
47
|
"@sveltejs/package": "^2.5.8",
|
|
46
48
|
"@types/node": "24.13.6",
|
|
47
49
|
"svelte": "^5.57.1",
|