@happyvertical/smrt-users 0.55.16 → 0.55.18

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as DEFAULT_LOGIN_LOCKOUT_FACTOR, $t as UsersLoginAttemptCollection, A as readMobileBearerToken, At as deriveOperationPermissionCollectionName, B as DEFAULT_PIN_MANAGE_PERMISSION, Bt as UsersPasswordCredentialCollection, C as hasOperationPermission, Ct as applyTenantHierarchyUpdates, D as withSessionPermissionContext, Dt as DEFAULT_ROLE_PERMISSION_PATTERNS, E as withPrincipalPermissionContext, Et as recordTenantHierarchyChanges, F as encodeOidcTransaction, Ft as RolePermission, G as PIN_LOGIN_KIND, Gt as GroupRoleCollection, H as DeviceCredentialForbiddenError, Ht as MembershipOverrideCollection, I as getUsersOidcConfig, It as RoleCollection, J as ensurePinPermissionsRegistered, Jt as GroupMember, K as PIN_PERMISSION_DEFINITIONS, Kt as GroupRole, L as resolveOidcProviderConfig, Lt as UsersPinCredentialCollection, M as OidcLoginError, Mt as normalizeOperationPermissionAction, N as OidcLoginService, Nt as registerPermissionDefinitions, O as MobileAuthError, Ot as RolePermissionCollection, P as decodeOidcTransaction, Pt as syncPermissionCatalog, Q as DEFAULT_LOGIN_LOCKOUT_BASE_SECONDS, Qt as UsersLoginAuditEvent, Rt as UsersPinCredential, S as checkOperationPermission, St as TenantHierarchyError, T as getRequestScopedDatabase, Tt as planTenantHierarchy, U as DeviceCredentialPolicyError, Ut as MembershipOverride, V as DeviceCredentialError, Vt as UsersPasswordCredential, W as DeviceCredentialService, Wt as MembershipCollection, X as PermissionResolver, Xt as Group, Y as SessionService, Yt as GroupCollection, Z as DEFAULT_LOGIN_ATTEMPT_WINDOW_SECONDS, Zt as UsersLoginAuditEventCollection, _ as PasswordCredentialService, _t as TenantPermissionOverrideCollection, a as TerminalAuthError, an as isValidEmail, at as LoginRateLimitError, b as OperationPermissionError, bt as Tenant, c as DEFAULT_PASSWORD_DENYLIST, cn as SESSION_DATA_KEYS, ct as DEFAULT_ANCESTOR_READ_MAX_DEPTH, d as DEFAULT_PASSWORD_MIN_LENGTH, dn as Role, dt as normalizeAncestorReadPolicy, en as UsersLoginAttempt, et as DEFAULT_LOGIN_LOCKOUT_MAX_SECONDS, f as PASSWORD_LOGIN_KIND, fn as Permission, ft as OidcProvisioningError, g as PasswordCredentialForbiddenError, gt as backfillUserEmailKeys, h as PasswordCredentialError, ht as UserEmailKeyBackfillError, i as TERMINAL_APPROVE_LOGIN_KIND, in as User, it as LoginAttemptLimiter, j as validateMobileRedirectUri, jt as deriveOperationPermissionSlug, k as MobileAuthService, kt as PermissionCatalogService, l as DEFAULT_PASSWORD_MANAGE_PERMISSION, ln as Session, lt as getConfiguredAncestorReadPolicy, m as PASSWORD_PERMISSION_DEFINITIONS, n as DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, nn as SessionCollection, nt as DurableLoginAuditSink, o as TerminalAuthRateLimitError, on as normalizeEmail, ot as withoutListBounds, p as PASSWORD_MAX_LENGTH_CEILING, pn as Membership, pt as UserCollection, q as PinVerifier, qt as GroupMemberCollection, r as DEFAULT_CLI_SESSION_TTL_SECONDS, rn as UsersCliAuthRequest, rt as InvalidCredentialsError, s as TerminalAuthService, sn as DEFAULT_SESSION_TTL, st as ANCESTOR_READ_ACTION, t as DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, tn as UsersCliAuthRequestCollection, tt as DEFAULT_LOGIN_MAX_ATTEMPTS, u as DEFAULT_PASSWORD_MAX_LENGTH, un as generateSessionId, ut as isAncestorReadableSlug, v as PasswordPolicyError, vt as TenantPermissionOverride, w as getCurrentSessionPermissionContext, wt as assertTenantTableName, x as assertOperationPermission, xt as MAX_TENANT_HIERARCHY_DEPTH, y as ensurePasswordPermissionsRegistered, yt as TenantCollection, z as DEFAULT_DEVICE_PERSON_IDLE_SECONDS, zt as PermissionCollection } from "./chunks/TerminalAuthService-HRcfnPFd.js";
1
+ import { $ as DEFAULT_LOGIN_LOCKOUT_FACTOR, $t as UsersLoginAttemptCollection, A as readMobileBearerToken, At as deriveOperationPermissionCollectionName, B as DEFAULT_PIN_MANAGE_PERMISSION, Bt as UsersPasswordCredentialCollection, C as hasOperationPermission, Ct as applyTenantHierarchyUpdates, D as withSessionPermissionContext, Dt as DEFAULT_ROLE_PERMISSION_PATTERNS, E as withPrincipalPermissionContext, Et as recordTenantHierarchyChanges, F as encodeOidcTransaction, Ft as RolePermission, G as PIN_LOGIN_KIND, Gt as GroupRoleCollection, H as DeviceCredentialForbiddenError, Ht as MembershipOverrideCollection, I as getUsersOidcConfig, It as RoleCollection, J as ensurePinPermissionsRegistered, Jt as GroupMember, K as PIN_PERMISSION_DEFINITIONS, Kt as GroupRole, L as resolveOidcProviderConfig, Lt as UsersPinCredentialCollection, M as OidcLoginError, Mt as normalizeOperationPermissionAction, N as OidcLoginService, Nt as registerPermissionDefinitions, O as MobileAuthError, Ot as RolePermissionCollection, P as decodeOidcTransaction, Pt as syncPermissionCatalog, Q as DEFAULT_LOGIN_LOCKOUT_BASE_SECONDS, Qt as UsersLoginAuditEvent, Rt as UsersPinCredential, S as checkOperationPermission, St as TenantHierarchyError, T as getRequestScopedDatabase, Tt as planTenantHierarchy, U as DeviceCredentialPolicyError, Ut as MembershipOverride, V as DeviceCredentialError, Vt as UsersPasswordCredential, W as DeviceCredentialService, Wt as MembershipCollection, X as PermissionResolver, Xt as Group, Y as SessionService, Yt as GroupCollection, Z as DEFAULT_LOGIN_ATTEMPT_WINDOW_SECONDS, Zt as UsersLoginAuditEventCollection, _ as PasswordCredentialService, _t as TenantPermissionOverrideCollection, a as TerminalAuthError, an as isValidEmail, at as LoginRateLimitError, b as OperationPermissionError, bt as Tenant, c as DEFAULT_PASSWORD_DENYLIST, cn as SESSION_DATA_KEYS, ct as DEFAULT_ANCESTOR_READ_MAX_DEPTH, d as DEFAULT_PASSWORD_MIN_LENGTH, dn as Role, dt as normalizeAncestorReadPolicy, en as UsersLoginAttempt, et as DEFAULT_LOGIN_LOCKOUT_MAX_SECONDS, f as PASSWORD_LOGIN_KIND, fn as Permission, ft as OidcProvisioningError, g as PasswordCredentialForbiddenError, gt as backfillUserEmailKeys, h as PasswordCredentialError, ht as UserEmailKeyBackfillError, i as TERMINAL_APPROVE_LOGIN_KIND, in as User, it as LoginAttemptLimiter, j as validateMobileRedirectUri, jt as deriveOperationPermissionSlug, k as MobileAuthService, kt as PermissionCatalogService, l as DEFAULT_PASSWORD_MANAGE_PERMISSION, ln as Session, lt as getConfiguredAncestorReadPolicy, m as PASSWORD_PERMISSION_DEFINITIONS, n as DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, nn as SessionCollection, nt as DurableLoginAuditSink, o as TerminalAuthRateLimitError, on as normalizeEmail, ot as withoutListBounds, p as PASSWORD_MAX_LENGTH_CEILING, pn as Membership, pt as UserCollection, q as PinVerifier, qt as GroupMemberCollection, r as DEFAULT_CLI_SESSION_TTL_SECONDS, rn as UsersCliAuthRequest, rt as InvalidCredentialsError, s as TerminalAuthService, sn as DEFAULT_SESSION_TTL, st as ANCESTOR_READ_ACTION, t as DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, tn as UsersCliAuthRequestCollection, tt as DEFAULT_LOGIN_MAX_ATTEMPTS, u as DEFAULT_PASSWORD_MAX_LENGTH, un as generateSessionId, ut as isAncestorReadableSlug, v as PasswordPolicyError, vt as TenantPermissionOverride, w as getCurrentSessionPermissionContext, wt as assertTenantTableName, x as assertOperationPermission, xt as MAX_TENANT_HIERARCHY_DEPTH, y as ensurePasswordPermissionsRegistered, yt as TenantCollection, z as DEFAULT_DEVICE_PERSON_IDLE_SECONDS, zt as PermissionCollection } from "./chunks/TerminalAuthService-DjGsRtqb.js";
2
2
  import { a as MembershipStatus, c as TenantPermissionEffect, i as DEFAULT_TENANT_POLICY, l as TenantStatus, n as DEFAULT_ROLES, o as OverrideEffect, r as DEFAULT_ROLE_SLUGS, s as SessionStatus, t as AccessRequestStatus, u as UserStatus } from "./chunks/types-DFqeph0R.js";
3
3
  import { ObjectRegistry, SmrtCollection, SmrtObject, SmrtRecipe, bumpChangeFeed, ensureChangeFeedTable, field, findManifestEntryByQualifiedName, foreignKey, isEmbeddedDatabase, isUniqueViolationError, registerRetentionTask, smrt, unregisterRetentionTask, withEmbeddedWriteTransaction } from "@happyvertical/smrt-core";
4
4
  import { PROFILE_EMAIL_KEY_BACKFILL_NAME, Person, ProfileCollection, ProfileTypeCollection, normalizeIdentityEmail } from "@happyvertical/smrt-profiles";
@@ -1492,7 +1492,7 @@ var MagicLinkService = class MagicLinkService {
1492
1492
  * The caller is responsible for emailing the token to the user.
1493
1493
  */
1494
1494
  async generate(email) {
1495
- const { SignJWT } = await import("./chunks/TerminalAuthService-HRcfnPFd.js").then((n) => n.R);
1495
+ const { SignJWT } = await import("./chunks/TerminalAuthService-DjGsRtqb.js").then((n) => n.R);
1496
1496
  const key = await this.getSigningKey();
1497
1497
  const nonce = crypto.randomUUID();
1498
1498
  const normalizedEmail = normalizeEmail(email);
@@ -1521,7 +1521,7 @@ var MagicLinkService = class MagicLinkService {
1521
1521
  * @throws {MagicLinkError} If the token is invalid, expired, or already used
1522
1522
  */
1523
1523
  async verify(token) {
1524
- const { jwtVerify, errors } = await import("./chunks/TerminalAuthService-HRcfnPFd.js").then((n) => n.R);
1524
+ const { jwtVerify, errors } = await import("./chunks/TerminalAuthService-DjGsRtqb.js").then((n) => n.R);
1525
1525
  const key = await this.getSigningKey();
1526
1526
  let payload;
1527
1527
  try {
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-users",
5
- "packageVersion": "0.55.16",
5
+ "packageVersion": "0.55.18",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-users:AccessRequestCollection": {
8
8
  "name": "accessrequestcollection",
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-users",
6
- "packageVersion": "0.55.16",
6
+ "packageVersion": "0.55.18",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "92f4bae18450e746a142ec7b4592568b82b074c89e14ecd32592840e384079d9",
11
- "packageJson": "5f262b9c52031f3e6202525f1abfbaefbef7fb88d054e1b911db6be6409e889f",
10
+ "manifest": "ab8b58873ab1fd02a7b11f4f10101fdaffee736de6ba9959f31365f70cb7afc9",
11
+ "packageJson": "b45c8f57c3c100ccc7b41757c7e3d085b59390a67b1004b764a4681e10d6f955",
12
12
  "agents": "eb74cdf8186c27e8721515cff653c688567284015ce9845a9b7714ac898620bc",
13
13
  "moduleDoc:agents/permissions.md": "832a684513d831d3cb9bd8d0c73295d756ca7cf9bc32de1ca3254691160371b1",
14
14
  "moduleDoc:agents/oidc-provisioning.md": "b73f3fdaf61136720132a614c7e14237b7bd35f7bf5d43eabb29f565485803b9",
@@ -38,5 +38,10 @@ async function revoke(kind: 'session' | 'api-key', id: string) {
38
38
  }
39
39
  }
40
40
  </script>
41
- <section class="security" aria-labelledby="account-security-title"><h2 id="account-security-title">{t('users.auth.account_security')}</h2>{#if error}<p role="alert">{t(error)}</p>{/if}<h3>{t('users.auth.signed_in_sessions')}</h3>{#if sessions.length}<ul>{#each sessions as session (session.id)}<li><div><strong>{session.label}</strong>{#if session.current}<span>{t('users.auth.current_session')}</span>{/if}{#if session.lastActiveAt}<small>{t('users.auth.last_active',{value:session.lastActiveAt})}</small>{/if}</div><Button variant="secondary" size="sm" onclick={()=>revoke('session',session.id)} disabled={session.current||!adapter.revokeSession} loading={pending===`session:${session.id}`}>{t('users.auth.sign_out')}</Button></li>{/each}</ul>{:else}<p>{t('users.auth.no_active_sessions')}</p>{/if}<h3>{t('users.auth.api_keys')}</h3>{#if apiKeys.length}<ul>{#each apiKeys as key (key.id)}<li><div><strong>{key.label}</strong>{#if key.lastUsedAt}<small>{t('users.auth.last_used',{value:key.lastUsedAt})}</small>{/if}</div><Button variant="secondary" size="sm" onclick={()=>revoke('api-key',key.id)} disabled={!adapter.revokeApiKey} loading={pending===`api-key:${key.id}`}>{t('users.auth.revoke')}</Button></li>{/each}</ul>{:else}<p>{t('users.auth.no_api_keys')}</p>{/if}</section>
42
- <style>.security{display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2,h3,p{margin:0}h2{font:var(--smrt-typography-title-large-font)}h3{margin-top:var(--smrt-spacing-sm,.5rem);font:var(--smrt-typography-title-small-font)}p[role=alert]{color:var(--smrt-color-error)}ul{list-style:none;padding:0;margin:0;display:grid;gap:var(--smrt-spacing-sm,.5rem)}li{display:flex;justify-content:space-between;align-items:center;gap:var(--smrt-spacing-md,1rem);padding:var(--smrt-spacing-sm,.5rem);background:var(--smrt-color-surface-container-low);border-radius:var(--smrt-radius-sm,.25rem)}li div{display:grid;gap:var(--smrt-spacing-2,.25rem)}small,span{color:var(--smrt-color-on-surface-variant)}span{font:var(--smrt-typography-label-small-font)}</style>
41
+ <section class="security" aria-labelledby="account-security-title" data-smrt-component="AccountSecurityPanel"><h2 id="account-security-title">{t('users.auth.account_security')}</h2>{#if error}<p role="alert">{t(error)}</p>{/if}<h3>{t('users.auth.signed_in_sessions')}</h3>{#if sessions.length}<ul>{#each sessions as session (session.id)}<li><div><strong>{session.label}</strong>{#if session.current}<span>{t('users.auth.current_session')}</span>{/if}{#if session.lastActiveAt}<small>{t('users.auth.last_active',{value:session.lastActiveAt})}</small>{/if}</div><Button variant="secondary" size="sm" onclick={()=>revoke('session',session.id)} disabled={session.current||!adapter.revokeSession} loading={pending===`session:${session.id}`}>{t('users.auth.sign_out')}</Button></li>{/each}</ul>{:else}<p>{t('users.auth.no_active_sessions')}</p>{/if}<h3>{t('users.auth.api_keys')}</h3>{#if apiKeys.length}<ul>{#each apiKeys as key (key.id)}<li><div><strong>{key.label}</strong>{#if key.lastUsedAt}<small>{t('users.auth.last_used',{value:key.lastUsedAt})}</small>{/if}</div><Button variant="secondary" size="sm" onclick={()=>revoke('api-key',key.id)} disabled={!adapter.revokeApiKey} loading={pending===`api-key:${key.id}`}>{t('users.auth.revoke')}</Button></li>{/each}</ul>{:else}<p>{t('users.auth.no_api_keys')}</p>{/if}</section>
42
+ <style>
43
+ @layer smrt.reset, smrt.structure, smrt.theme;
44
+ @layer smrt.theme {
45
+ .security{display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2,h3,p{margin:0}h2{font:var(--smrt-typography-title-large-font)}h3{margin-top:var(--smrt-spacing-sm,.5rem);font:var(--smrt-typography-title-small-font)}p[role=alert]{color:var(--smrt-color-error)}ul{list-style:none;padding:0;margin:0;display:grid;gap:var(--smrt-spacing-sm,.5rem)}li{display:flex;justify-content:space-between;align-items:center;gap:var(--smrt-spacing-md,1rem);padding:var(--smrt-spacing-sm,.5rem);background:var(--smrt-color-surface-container-low);border-radius:var(--smrt-radius-sm,.25rem)}li div{display:grid;gap:var(--smrt-spacing-2,.25rem)}small,span{color:var(--smrt-color-on-surface-variant)}span{font:var(--smrt-typography-label-small-font)}
46
+ }
47
+ </style>
@@ -149,6 +149,8 @@ function handleClose() {
149
149
  </Modal>
150
150
 
151
151
  <style>
152
+ @layer smrt.reset, smrt.structure, smrt.theme;
153
+ @layer smrt.theme {
152
154
  /* The dialog chrome (backdrop, surface, header, footer bar, close button) is
153
155
  supplied by the smrt-ui Modal. Only the form-content + footer-button styles
154
156
  live here. */
@@ -160,10 +162,10 @@ function handleClose() {
160
162
 
161
163
  .error {
162
164
  padding: var(--smrt-spacing-sm, 0.75rem);
163
- background: var(--smrt-color-error-container, #ffdad6);
164
- border: 1px solid var(--smrt-color-error, #ba1a1a);
165
+ background: var(--smrt-color-error-container);
166
+ border: 1px solid var(--smrt-color-error);
165
167
  border-radius: var(--smrt-radius-medium, 0.5rem);
166
- color: var(--smrt-color-on-error-container, #410002);
168
+ color: var(--smrt-color-on-error-container);
167
169
  font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
168
170
  }
169
171
 
@@ -175,7 +177,7 @@ function handleClose() {
175
177
 
176
178
  label {
177
179
  font: var(--smrt-typography-body-medium-font, 500 0.875rem / 1.25 sans-serif);
178
- color: var(--smrt-color-on-surface-variant, #43474e);
180
+ color: var(--smrt-color-on-surface-variant);
179
181
  }
180
182
 
181
183
  .checkbox-field {
@@ -187,7 +189,7 @@ function handleClose() {
187
189
  .checkbox-field input {
188
190
  width: 1rem;
189
191
  height: 1rem;
190
- accent-color: var(--smrt-color-primary, #005ac1);
192
+ accent-color: var(--smrt-color-primary);
191
193
  }
192
194
 
193
195
  .checkbox-field label {
@@ -196,9 +198,10 @@ function handleClose() {
196
198
 
197
199
  .hint {
198
200
  font: var(--smrt-typography-body-small-font, 0.75rem / 1.25 sans-serif);
199
- color: var(--smrt-color-on-surface-variant, #43474e);
201
+ color: var(--smrt-color-on-surface-variant);
200
202
  padding: var(--smrt-spacing-sm, 0.5rem);
201
- background: var(--smrt-color-surface-container-low, #f9fafb);
203
+ background: var(--smrt-color-surface-container-low);
202
204
  border-radius: var(--smrt-radius-small, 0.25rem);
203
205
  }
206
+ }
204
207
  </style>
@@ -40,4 +40,9 @@ async function submit() {
40
40
  }
41
41
  </script>
42
42
  <Form class="auth-form" onsubmit={submit} aria-busy={loading||pending}><h2>{token ? t('users.auth.confirm_sign_in_link') : t('users.auth.email_sign_in_link')}</h2>{#if error}<p class="error" role="alert">{t(error)}</p>{/if}{#if notice}<p class="notice" role="status">{t(notice)}</p>{/if}{#if !token}<label for="magic-link-email">{t('users.auth.email_address')}</label><Input id="magic-link-email" type="email" bind:value={email} autocomplete="email" required disabled={loading||pending}/>{/if}<Button type="submit" fullWidth disabled={loading || (token ? !adapter.confirmMagicLink : !adapter.requestMagicLink)} loading={pending}>{token ? t('users.auth.confirm_sign_in') : t('users.auth.send_sign_in_link')}</Button></Form>
43
- <style>:global(.auth-form){display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2{margin:0;font:var(--smrt-typography-title-large-font)}label{font:var(--smrt-typography-label-large-font)}.error,.notice{margin:0;padding:var(--smrt-spacing-sm,.5rem);border-radius:var(--smrt-radius-sm,.25rem)}.error{background:var(--smrt-color-error-container);color:var(--smrt-color-on-error-container)}.notice{background:var(--smrt-color-primary-container);color:var(--smrt-color-on-primary-container)}</style>
43
+ <style>
44
+ @layer smrt.reset, smrt.structure, smrt.theme;
45
+ @layer smrt.theme {
46
+ :global(.auth-form){display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2{margin:0;font:var(--smrt-typography-title-large-font)}label{font:var(--smrt-typography-label-large-font)}.error,.notice{margin:0;padding:var(--smrt-spacing-sm,.5rem);border-radius:var(--smrt-radius-sm,.25rem)}.error{background:var(--smrt-color-error-container);color:var(--smrt-color-on-error-container)}.notice{background:var(--smrt-color-primary-container);color:var(--smrt-color-on-primary-container)}
47
+ }
48
+ </style>
@@ -25,4 +25,9 @@ async function signIn(providerId: string) {
25
25
  }
26
26
  }
27
27
  </script>
28
- <section aria-label={t('users.auth.single_sign_on')}><h2>{t('users.auth.or_continue_with')}</h2>{#if error}<p class="error" role="alert">{t(error)}</p>{/if}<div class="providers">{#each providers as provider (provider.id)}<Button variant="secondary" fullWidth onclick={()=>signIn(provider.id)} disabled={provider.disabled||!adapter.signInWithOidc} loading={pending===provider.id}>{provider.label}</Button>{/each}</div></section><style>section{display:grid;gap:var(--smrt-spacing-sm,.5rem)}h2{margin:0;font:var(--smrt-typography-title-small-font)}.providers{display:grid;gap:var(--smrt-spacing-sm,.5rem)}.error{margin:0;color:var(--smrt-color-error)}</style>
28
+ <section aria-label={t('users.auth.single_sign_on')} data-smrt-component="OidcProviderButtons"><h2>{t('users.auth.or_continue_with')}</h2>{#if error}<p class="error" role="alert">{t(error)}</p>{/if}<div class="providers">{#each providers as provider (provider.id)}<Button variant="secondary" fullWidth onclick={()=>signIn(provider.id)} disabled={provider.disabled||!adapter.signInWithOidc} loading={pending===provider.id}>{provider.label}</Button>{/each}</div></section><style>
29
+ @layer smrt.reset, smrt.structure, smrt.theme;
30
+ @layer smrt.theme {
31
+ section{display:grid;gap:var(--smrt-spacing-sm,.5rem)}h2{margin:0;font:var(--smrt-typography-title-small-font)}.providers{display:grid;gap:var(--smrt-spacing-sm,.5rem)}.error{margin:0;color:var(--smrt-color-error)}
32
+ }
33
+ </style>
@@ -25,4 +25,9 @@ async function signIn() {
25
25
  }
26
26
  }
27
27
  </script>
28
- {#if adapter.signInWithPasskey}<div class="passkey"><Button variant="secondary" fullWidth onclick={signIn} loading={pending}>{label ?? t('users.auth.passkey_sign_in')}</Button>{#if error}<p role="alert">{t(error)}</p>{/if}</div>{/if}<style>.passkey{display:grid;gap:var(--smrt-spacing-sm,.5rem)}p{margin:0;color:var(--smrt-color-error)}</style>
28
+ {#if adapter.signInWithPasskey}<div class="passkey" data-smrt-component="PasskeySignInButton"><Button variant="secondary" fullWidth onclick={signIn} loading={pending}>{label ?? t('users.auth.passkey_sign_in')}</Button>{#if error}<p role="alert">{t(error)}</p>{/if}</div>{/if}<style>
29
+ @layer smrt.reset, smrt.structure, smrt.theme;
30
+ @layer smrt.theme {
31
+ .passkey{display:grid;gap:var(--smrt-spacing-sm,.5rem)}p{margin:0;color:var(--smrt-color-error)}
32
+ }
33
+ </style>
@@ -40,4 +40,9 @@ async function submit() {
40
40
  {#if forgotPasswordHref}<Button href={forgotPasswordHref} variant="ghost">{t('users.auth.forgot_password')}</Button>{/if}
41
41
  <Button type="submit" fullWidth disabled={!adapter.signInWithPassword || loading} loading={pending}>{t('users.auth.sign_in')}</Button>
42
42
  </Form>
43
- <style>:global(.auth-form){display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2{margin:0;font:var(--smrt-typography-title-large-font)}label{font:var(--smrt-typography-label-large-font)}.error{margin:0;padding:var(--smrt-spacing-sm,.5rem);border-radius:var(--smrt-radius-sm,.25rem);background:var(--smrt-color-error-container);color:var(--smrt-color-on-error-container)}</style>
43
+ <style>
44
+ @layer smrt.reset, smrt.structure, smrt.theme;
45
+ @layer smrt.theme {
46
+ :global(.auth-form){display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2{margin:0;font:var(--smrt-typography-title-large-font)}label{font:var(--smrt-typography-label-large-font)}.error{margin:0;padding:var(--smrt-spacing-sm,.5rem);border-radius:var(--smrt-radius-sm,.25rem);background:var(--smrt-color-error-container);color:var(--smrt-color-on-error-container)}
47
+ }
48
+ </style>
@@ -35,4 +35,9 @@ async function submit() {
35
35
  }
36
36
  </script>
37
37
  <Form class="auth-form" onsubmit={submit} aria-busy={loading||pending}><h2>{t('users.auth.create_account')}</h2>{#if error}<p class="error" role="alert">{t(error)}</p>{/if}<label for="sign-up-email">{t('users.auth.email_address')}</label><Input id="sign-up-email" type="email" bind:value={email} autocomplete="email" required disabled={loading||pending}/><label for="sign-up-password">{t('users.auth.password')}</label><Input id="sign-up-password" type="password" bind:value={password} autocomplete="new-password" required disabled={loading||pending}/><label for="sign-up-confirm">{t('users.auth.confirm_password')}</label><Input id="sign-up-confirm" type="password" bind:value={confirm} autocomplete="new-password" required disabled={loading||pending}/><Button type="submit" fullWidth disabled={!adapter.signUp||loading} loading={pending}>{t('users.auth.create_account')}</Button></Form>
38
- <style>:global(.auth-form){display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2{margin:0;font:var(--smrt-typography-title-large-font)}label{font:var(--smrt-typography-label-large-font)}.error{margin:0;padding:var(--smrt-spacing-sm,.5rem);border-radius:var(--smrt-radius-sm,.25rem);background:var(--smrt-color-error-container);color:var(--smrt-color-on-error-container)}</style>
38
+ <style>
39
+ @layer smrt.reset, smrt.structure, smrt.theme;
40
+ @layer smrt.theme {
41
+ :global(.auth-form){display:grid;gap:var(--smrt-spacing-sm,.5rem);color:var(--smrt-color-on-surface)}h2{margin:0;font:var(--smrt-typography-title-large-font)}label{font:var(--smrt-typography-label-large-font)}.error{margin:0;padding:var(--smrt-spacing-sm,.5rem);border-radius:var(--smrt-radius-sm,.25rem);background:var(--smrt-color-error-container);color:var(--smrt-color-on-error-container)}
42
+ }
43
+ </style>
@@ -52,7 +52,7 @@ function getColor(name: string): string {
52
52
  }
53
53
  </script>
54
54
 
55
- <div class="user-avatar" class:sm={size === 'sm'} class:lg={size === 'lg'} class:xl={size === 'xl'}>
55
+ <div class="user-avatar" class:sm={size === 'sm'} class:lg={size === 'lg'} class:xl={size === 'xl'} data-smrt-component="UserAvatar">
56
56
  <span class="avatar" style:background-color={getColor(profile.name ?? '')}>
57
57
  {getInitials(profile.name ?? 'U')}
58
58
  </span>
@@ -62,6 +62,8 @@ function getColor(name: string): string {
62
62
  </div>
63
63
 
64
64
  <style>
65
+ @layer smrt.reset, smrt.structure, smrt.theme;
66
+ @layer smrt.theme {
65
67
  .user-avatar {
66
68
  display: inline-flex;
67
69
  align-items: center;
@@ -74,7 +76,7 @@ function getColor(name: string): string {
74
76
  justify-content: center;
75
77
  width: 2.5rem;
76
78
  height: 2.5rem;
77
- border-radius: var(--smrt-radius-full, 9999px);
79
+ border-radius: var(--smrt-radius-full);
78
80
  color: var(--smrt-color-on-primary, white);
79
81
  font: var(--smrt-typography-label-large-font, 600 0.875rem / 1.25 sans-serif);
80
82
  }
@@ -105,4 +107,5 @@ function getColor(name: string): string {
105
107
  .xl .name {
106
108
  font: var(--smrt-typography-body-large-font, 1rem / 1.5 sans-serif);
107
109
  }
110
+ }
108
111
  </style>
@@ -57,7 +57,7 @@ const statusClass = $derived.by(() => {
57
57
  class:clickable={!!onclick}
58
58
  onclick={onclick}
59
59
  disabled={!onclick}
60
- use:ripple
60
+ use:ripple data-smrt-component="UserCard"
61
61
  >
62
62
  <UserAvatar {profile} size="md" />
63
63
 
@@ -77,13 +77,15 @@ const statusClass = $derived.by(() => {
77
77
  </button>
78
78
 
79
79
  <style>
80
+ @layer smrt.reset, smrt.structure, smrt.theme;
81
+ @layer smrt.theme {
80
82
  .user-card {
81
83
  display: flex;
82
84
  align-items: center;
83
- gap: var(--smrt-spacing-4, 16px);
84
- padding: var(--smrt-spacing-3, 12px) var(--smrt-spacing-4, 16px);
85
+ gap: var(--smrt-spacing-4);
86
+ padding: var(--smrt-spacing-3) var(--smrt-spacing-4);
85
87
  background-color: var(--smrt-color-surface-container-low);
86
- border-radius: var(--smrt-radius-lg, 12px);
88
+ border-radius: var(--smrt-radius-lg);
87
89
  width: 100%;
88
90
  text-align: left;
89
91
  cursor: default;
@@ -138,29 +140,29 @@ const statusClass = $derived.by(() => {
138
140
  .meta {
139
141
  display: flex;
140
142
  align-items: center;
141
- gap: var(--smrt-spacing-2, 8px);
143
+ gap: var(--smrt-spacing-2);
142
144
  flex-shrink: 0;
143
145
  }
144
146
 
145
147
  .role {
146
148
  font: var(--smrt-typography-label-small-font);
147
- padding: 0 var(--smrt-spacing-2, 8px);
149
+ padding: 0 var(--smrt-spacing-2);
148
150
  height: 20px;
149
151
  display: inline-flex;
150
152
  align-items: center;
151
153
  background-color: var(--smrt-color-surface-container-highest);
152
154
  color: var(--smrt-color-on-surface-variant);
153
- border-radius: var(--smrt-radius-lg, 12px);
155
+ border-radius: var(--smrt-radius-lg);
154
156
  text-transform: capitalize;
155
157
  }
156
158
 
157
159
  .status {
158
160
  font: var(--smrt-typography-label-small-font);
159
- padding: 0 var(--smrt-spacing-2, 8px);
161
+ padding: 0 var(--smrt-spacing-2);
160
162
  height: 20px;
161
163
  display: inline-flex;
162
164
  align-items: center;
163
- border-radius: var(--smrt-radius-lg, 12px);
165
+ border-radius: var(--smrt-radius-lg);
164
166
  text-transform: capitalize;
165
167
  font-weight: var(--smrt-typography-weight-semibold, 600);
166
168
  }
@@ -184,4 +186,5 @@ const statusClass = $derived.by(() => {
184
186
  background-color: var(--smrt-color-surface-variant);
185
187
  color: var(--smrt-color-on-surface-variant);
186
188
  }
189
+ }
187
190
  </style>
@@ -72,7 +72,7 @@ function handleSubmit() {
72
72
  }
73
73
  </script>
74
74
 
75
- <div class="user-form-shell">
75
+ <div class="user-form-shell" data-smrt-component="UserForm">
76
76
  <Form class="user-form" onsubmit={handleSubmit}>
77
77
  <div class="field">
78
78
  <label for="name">Name</label>
@@ -114,6 +114,8 @@ function handleSubmit() {
114
114
  </div>
115
115
 
116
116
  <style>
117
+ @layer smrt.reset, smrt.structure, smrt.theme;
118
+ @layer smrt.theme {
117
119
  .user-form-shell :global(.user-form) {
118
120
  display: flex;
119
121
  flex-direction: column;
@@ -128,12 +130,12 @@ function handleSubmit() {
128
130
 
129
131
  label {
130
132
  font: var(--smrt-typography-body-medium-font, 500 0.875rem / 1.25 sans-serif);
131
- color: var(--smrt-color-on-surface-variant, #43474e);
133
+ color: var(--smrt-color-on-surface-variant);
132
134
  }
133
135
 
134
136
  .hint {
135
137
  font: var(--smrt-typography-body-small-font, 0.75rem / 1.25 sans-serif);
136
- color: var(--smrt-color-on-surface-variant, #43474e);
138
+ color: var(--smrt-color-on-surface-variant);
137
139
  }
138
140
 
139
141
  .actions {
@@ -142,4 +144,5 @@ function handleSubmit() {
142
144
  gap: var(--smrt-spacing-sm, 0.5rem);
143
145
  margin-top: var(--smrt-spacing-sm, 0.5rem);
144
146
  }
147
+ }
145
148
  </style>
@@ -39,7 +39,7 @@ const {
39
39
  }: Props = $props();
40
40
  </script>
41
41
 
42
- <div class="user-list">
42
+ <div class="user-list" data-smrt-component="UserList">
43
43
  {#if loading}
44
44
  <div class="loading">
45
45
  <div class="spinner"></div>
@@ -66,6 +66,8 @@ const {
66
66
  </div>
67
67
 
68
68
  <style>
69
+ @layer smrt.reset, smrt.structure, smrt.theme;
70
+ @layer smrt.theme {
69
71
  .user-list {
70
72
  display: flex;
71
73
  flex-direction: column;
@@ -78,15 +80,15 @@ const {
78
80
  justify-content: center;
79
81
  gap: var(--smrt-spacing-sm, 0.75rem);
80
82
  padding: var(--smrt-spacing-xl, 2rem);
81
- color: var(--smrt-color-on-surface-variant, #43474e);
83
+ color: var(--smrt-color-on-surface-variant);
82
84
  }
83
85
 
84
86
  .spinner {
85
87
  width: 1.25rem;
86
88
  height: 1.25rem;
87
- border: 2px solid var(--smrt-color-outline-variant, #c4c6cf);
88
- border-top-color: var(--smrt-color-primary, #005ac1);
89
- border-radius: var(--smrt-radius-full, 9999px);
89
+ border: 2px solid var(--smrt-color-outline-variant);
90
+ border-top-color: var(--smrt-color-primary);
91
+ border-radius: var(--smrt-radius-full);
90
92
  animation: spin 0.6s linear infinite;
91
93
  }
92
94
 
@@ -99,9 +101,9 @@ const {
99
101
  .empty {
100
102
  padding: var(--smrt-spacing-xl, 2rem);
101
103
  text-align: center;
102
- color: var(--smrt-color-on-surface-variant, #43474e);
103
- background: var(--smrt-color-surface-container-low, #f9fafb);
104
- border: 1px dashed var(--smrt-color-outline-variant, #c4c6cf);
104
+ color: var(--smrt-color-on-surface-variant);
105
+ background: var(--smrt-color-surface-container-low);
106
+ border: 1px dashed var(--smrt-color-outline-variant);
105
107
  border-radius: var(--smrt-radius-medium, 0.5rem);
106
108
  }
107
109
 
@@ -110,4 +112,5 @@ const {
110
112
  animation: none;
111
113
  }
112
114
  }
115
+ }
113
116
  </style>
@@ -170,7 +170,7 @@ function getInitials(name: string): string {
170
170
 
171
171
  <svelte:window onclick={handleClickOutside} onkeydown={handleKeydown} />
172
172
 
173
- <div class="user-menu" id={menuId}>
173
+ <div class="user-menu" id={menuId} data-smrt-component="UserMenu">
174
174
  <!-- raw-primitive-allow: WAI-ARIA menu-button trigger with bind:this focus management, aria-haspopup/aria-expanded/aria-controls and roving-tabindex keyboard nav; structural menu button no primitive owns -->
175
175
  <button
176
176
  bind:this={triggerButton}
@@ -260,6 +260,8 @@ function getInitials(name: string): string {
260
260
  </div>
261
261
 
262
262
  <style>
263
+ @layer smrt.reset, smrt.structure, smrt.theme;
264
+ @layer smrt.theme {
263
265
  .user-menu {
264
266
  position: relative;
265
267
  display: inline-block;
@@ -268,11 +270,11 @@ function getInitials(name: string): string {
268
270
  .user-menu-trigger {
269
271
  display: flex;
270
272
  align-items: center;
271
- gap: var(--smrt-spacing-3, 12px);
272
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
273
+ gap: var(--smrt-spacing-3);
274
+ padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
273
275
  background: transparent;
274
276
  border: none;
275
- border-radius: var(--smrt-radius-2xl, 24px);
277
+ border-radius: var(--smrt-radius-2xl);
276
278
  cursor: pointer;
277
279
  transition: background-color var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, ease);
278
280
  color: var(--smrt-color-on-surface);
@@ -317,21 +319,21 @@ function getInitials(name: string): string {
317
319
  position: absolute;
318
320
  top: 100%;
319
321
  right: 0;
320
- margin-top: var(--smrt-spacing-1, 4px);
322
+ margin-top: var(--smrt-spacing-1);
321
323
  min-width: 200px;
322
324
  background-color: var(--smrt-color-surface-container);
323
- border-radius: var(--smrt-radius-medium, 4px);
325
+ border-radius: var(--smrt-radius-medium);
324
326
  box-shadow: var(--smrt-elevation-2);
325
327
  z-index: var(--smrt-z-index-dropdown, 1000);
326
- padding: var(--smrt-spacing-1, 4px) 0;
328
+ padding: var(--smrt-spacing-1) 0;
327
329
  overflow: hidden;
328
330
  }
329
331
 
330
332
  .dropdown-item {
331
333
  display: flex;
332
334
  align-items: center;
333
- gap: var(--smrt-spacing-3, 12px);
334
- padding: var(--smrt-spacing-3, 12px) var(--smrt-spacing-4, 16px);
335
+ gap: var(--smrt-spacing-3);
336
+ padding: var(--smrt-spacing-3) var(--smrt-spacing-4);
335
337
  font: var(--smrt-typography-body-medium-font);
336
338
  color: var(--smrt-color-on-surface);
337
339
  text-decoration: none;
@@ -355,10 +357,10 @@ function getInitials(name: string): string {
355
357
  }
356
358
 
357
359
  .user-info {
358
- padding: var(--smrt-spacing-3, 12px) var(--smrt-spacing-4, 16px);
360
+ padding: var(--smrt-spacing-3) var(--smrt-spacing-4);
359
361
  display: flex;
360
362
  flex-direction: column;
361
- gap: var(--smrt-spacing-1, 4px);
363
+ gap: var(--smrt-spacing-1);
362
364
  }
363
365
 
364
366
  .user-info-name {
@@ -383,7 +385,7 @@ function getInitials(name: string): string {
383
385
  }
384
386
 
385
387
  .divider {
386
- margin: var(--smrt-spacing-1, 4px) 0;
388
+ margin: var(--smrt-spacing-1) 0;
387
389
  border: none;
388
390
  border-top: 1px solid var(--smrt-color-outline-variant);
389
391
  }
@@ -399,4 +401,5 @@ function getInitials(name: string): string {
399
401
  transform: none;
400
402
  }
401
403
  }
404
+ }
402
405
  </style>
package/dist/sveltekit.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as readMobileBearerToken, D as withSessionPermissionContext, F as encodeOidcTransaction, H as DeviceCredentialForbiddenError, I as getUsersOidcConfig, L as resolveOidcProviderConfig, M as OidcLoginError, N as OidcLoginService, O as MobileAuthError, P as decodeOidcTransaction, U as DeviceCredentialPolicyError, V as DeviceCredentialError, W as DeviceCredentialService, Y as SessionService, _ as PasswordCredentialService, a as TerminalAuthError, at as LoginRateLimitError, b as OperationPermissionError, g as PasswordCredentialForbiddenError, h as PasswordCredentialError, j as validateMobileRedirectUri, k as MobileAuthService, o as TerminalAuthRateLimitError, s as TerminalAuthService, v as PasswordPolicyError } from "./chunks/TerminalAuthService-HRcfnPFd.js";
1
+ import { A as readMobileBearerToken, D as withSessionPermissionContext, F as encodeOidcTransaction, H as DeviceCredentialForbiddenError, I as getUsersOidcConfig, L as resolveOidcProviderConfig, M as OidcLoginError, N as OidcLoginService, O as MobileAuthError, P as decodeOidcTransaction, U as DeviceCredentialPolicyError, V as DeviceCredentialError, W as DeviceCredentialService, Y as SessionService, _ as PasswordCredentialService, a as TerminalAuthError, at as LoginRateLimitError, b as OperationPermissionError, g as PasswordCredentialForbiddenError, h as PasswordCredentialError, j as validateMobileRedirectUri, k as MobileAuthService, o as TerminalAuthRateLimitError, s as TerminalAuthService, v as PasswordPolicyError } from "./chunks/TerminalAuthService-DjGsRtqb.js";
2
2
  import { DiscoveryArtifactValidationError, SMRT_APP_RESULT_CONTRACT, SMRT_APP_RESULT_SCHEMA, SMRT_APP_RESULT_VERSION, SMRT_DISCOVERY_CONFORMANCE_ARTIFACT_SCHEMA, SMRT_DISCOVERY_CONFORMANCE_SCHEMA, SMRT_DISCOVERY_CONFORMANCE_VERSION, SMRT_MCP_RESULT_METADATA_KEY, canonicalizeDiscoveryArtifact, createDiscoveryConformanceArtifact, deriveCommandRequirements, validateDiscoveryConformanceArtifact } from "./app-contract.js";
3
3
  import { ObjectRegistry, createClassNamePredicate, resolveCustomActionMetadata, resolveEffectiveActionMetadata } from "@happyvertical/smrt-core";
4
4
  import { createLogger } from "@happyvertical/logger";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-users",
3
- "version": "0.55.16",
3
+ "version": "0.55.18",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Multi-tenant user management for the SMRT framework - users, tenants, roles, permissions, groups",
6
6
  "type": "module",
@@ -48,18 +48,18 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@happyvertical/logger": "^0.103.4",
51
- "@happyvertical/smrt-config": "0.55.16",
52
- "@happyvertical/smrt-core": "0.55.16",
53
- "@happyvertical/smrt-mobile-contract": "0.55.16",
54
- "@happyvertical/smrt-profiles": "0.55.16",
55
- "@happyvertical/smrt-tenancy": "0.55.16",
56
- "@happyvertical/smrt-types": "0.55.16",
57
- "@happyvertical/smrt-ui": "0.55.16",
51
+ "@happyvertical/smrt-config": "0.55.18",
52
+ "@happyvertical/smrt-core": "0.55.18",
53
+ "@happyvertical/smrt-mobile-contract": "0.55.18",
54
+ "@happyvertical/smrt-profiles": "0.55.18",
55
+ "@happyvertical/smrt-tenancy": "0.55.18",
56
+ "@happyvertical/smrt-types": "0.55.18",
57
+ "@happyvertical/smrt-ui": "0.55.18",
58
58
  "@happyvertical/sql": "^0.103.4",
59
59
  "jose": "^6.2.12"
60
60
  },
61
61
  "devDependencies": {
62
- "@happyvertical/smrt-vitest": "0.55.16",
62
+ "@happyvertical/smrt-vitest": "0.55.18",
63
63
  "@sveltejs/package": "^2.5.8",
64
64
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
65
65
  "@types/node": "24.13.6",