@structured-id/ui-core 1.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +22 -10
  2. package/package.json +25 -43
  3. package/src/components/SidStatusBadge.vue +1 -5
  4. package/src/composables/useAccountTransport.test.ts +2 -1
  5. package/src/composables/useGrpc.test.ts +17 -14
  6. package/src/composables/useGrpc.ts +4 -2
  7. package/src/index.ts +2 -120
  8. package/src/profile/components/ConsentManager.vue +5 -1
  9. package/src/profile/components/ProfileEditor.vue +1 -1
  10. package/src/profile/components/SecuritySettings.vue +5 -1
  11. package/src/profile/components/SessionList.vue +2 -1
  12. package/src/profile/components/SidRegistrationForm.test.ts +173 -1
  13. package/src/profile/components/SidRegistrationForm.vue +88 -32
  14. package/src/profile/components/index.ts +1 -4
  15. package/src/profile/composables/index.ts +1 -0
  16. package/src/profile/composables/operationPlan.test.ts +155 -0
  17. package/src/profile/composables/operationPlan.ts +218 -0
  18. package/src/profile/composables/useAccountApi.ts +4 -4
  19. package/src/profile/composables/useAuth.test.ts +312 -31
  20. package/src/profile/composables/useAuth.ts +176 -30
  21. package/src/profile/composables/useAuthApi.opaque.test.ts +13 -6
  22. package/src/profile/composables/useAuthApi.ts +40 -18
  23. package/src/profile/composables/useCurrentPasswordRequirement.test.ts +196 -0
  24. package/src/profile/composables/useCurrentPasswordRequirement.ts +95 -0
  25. package/src/profile/composables/useEnrollmentApi.test.ts +7 -3
  26. package/src/profile/composables/useEnrollmentApi.ts +3 -5
  27. package/src/profile/composables/useIdentityApi.ts +4 -4
  28. package/src/profile/composables/useSecurityLevel.test.ts +1 -1
  29. package/src/profile/composables/useSecurityLevel.ts +1 -1
  30. package/src/quasar/SidPasswordCeremony.test.ts +253 -0
  31. package/src/quasar/SidPasswordCeremony.vue +456 -0
  32. package/src/quasar/index.ts +2 -0
  33. package/src/utils/rpcRefusal.test.ts +4 -4
  34. package/src/utils/rpcRefusal.ts +8 -4
  35. package/dist/browser-vp/index.cjs +0 -349
  36. package/dist/browser-vp/index.d.cts +0 -276
  37. package/dist/browser-vp/index.d.ts +0 -276
  38. package/dist/browser-vp/index.js +0 -349
  39. package/dist/chunk-C3Q766CY.cjs +0 -125
  40. package/dist/chunk-J5YXRXB5.js +0 -125
  41. package/dist/index.cjs +0 -23581
  42. package/dist/index.d.cts +0 -11539
  43. package/dist/index.d.ts +0 -11539
  44. package/dist/index.js +0 -23581
  45. package/dist/storage-4CB2JTLW.cjs +0 -22
  46. package/dist/storage-THRWREZP.js +0 -22
  47. package/src/browser-vp/crypto.ts +0 -253
  48. package/src/browser-vp/index.test.ts +0 -10
  49. package/src/browser-vp/index.ts +0 -116
  50. package/src/browser-vp/prf.ts +0 -179
  51. package/src/browser-vp/storage.test.ts +0 -9
  52. package/src/browser-vp/storage.ts +0 -163
  53. package/src/browser-vp/types.ts +0 -95
  54. package/src/browser-vp/vp.test.ts +0 -43
  55. package/src/browser-vp/vp.ts +0 -111
  56. package/src/generated/google/api/http.ts +0 -687
  57. package/src/generated/google/api/httpbody.ts +0 -159
  58. package/src/generated/google/protobuf/any.ts +0 -326
  59. package/src/generated/google/protobuf/descriptor.ts +0 -4865
  60. package/src/generated/google/protobuf/duration.ts +0 -230
  61. package/src/generated/google/protobuf/empty.ts +0 -94
  62. package/src/generated/google/protobuf/struct.ts +0 -484
  63. package/src/generated/google/protobuf/timestamp.ts +0 -287
  64. package/src/generated/google/rpc/error_details.ts +0 -1386
  65. package/src/generated/google/rpc/status.ts +0 -127
  66. package/src/generated/sid/v1/account/account.client.ts +0 -527
  67. package/src/generated/sid/v1/account/account.ts +0 -3261
  68. package/src/generated/sid/v1/admin/admin.client.ts +0 -139
  69. package/src/generated/sid/v1/admin/admin.ts +0 -781
  70. package/src/generated/sid/v1/admin/admin_roles.client.ts +0 -181
  71. package/src/generated/sid/v1/admin/admin_roles.ts +0 -1606
  72. package/src/generated/sid/v1/admin/analytics.client.ts +0 -106
  73. package/src/generated/sid/v1/admin/analytics.ts +0 -1017
  74. package/src/generated/sid/v1/admin/branding.client.ts +0 -166
  75. package/src/generated/sid/v1/admin/branding.ts +0 -1906
  76. package/src/generated/sid/v1/admin/enrollment.client.ts +0 -177
  77. package/src/generated/sid/v1/admin/enrollment.ts +0 -1766
  78. package/src/generated/sid/v1/admin/events.client.ts +0 -195
  79. package/src/generated/sid/v1/admin/events.ts +0 -1900
  80. package/src/generated/sid/v1/admin/flow.client.ts +0 -183
  81. package/src/generated/sid/v1/admin/flow.ts +0 -1552
  82. package/src/generated/sid/v1/admin/keys.client.ts +0 -107
  83. package/src/generated/sid/v1/admin/keys.ts +0 -931
  84. package/src/generated/sid/v1/admin/login_experience.client.ts +0 -160
  85. package/src/generated/sid/v1/admin/login_experience.ts +0 -1061
  86. package/src/generated/sid/v1/admin/migration.client.ts +0 -127
  87. package/src/generated/sid/v1/admin/migration.ts +0 -1948
  88. package/src/generated/sid/v1/admin/notifications.client.ts +0 -134
  89. package/src/generated/sid/v1/admin/notifications.ts +0 -1273
  90. package/src/generated/sid/v1/admin/operations.client.ts +0 -241
  91. package/src/generated/sid/v1/admin/operations.ts +0 -2633
  92. package/src/generated/sid/v1/admin/organizations.client.ts +0 -57
  93. package/src/generated/sid/v1/admin/organizations.ts +0 -332
  94. package/src/generated/sid/v1/admin/provisioning.client.ts +0 -232
  95. package/src/generated/sid/v1/admin/provisioning.ts +0 -1383
  96. package/src/generated/sid/v1/admin/scopes.client.ts +0 -171
  97. package/src/generated/sid/v1/admin/scopes.ts +0 -1324
  98. package/src/generated/sid/v1/admin/security.client.ts +0 -410
  99. package/src/generated/sid/v1/admin/security.ts +0 -4077
  100. package/src/generated/sid/v1/admin/sessions.client.ts +0 -114
  101. package/src/generated/sid/v1/admin/sessions.ts +0 -923
  102. package/src/generated/sid/v1/admin/settings.client.ts +0 -351
  103. package/src/generated/sid/v1/admin/settings.ts +0 -2458
  104. package/src/generated/sid/v1/admin/user_profile.client.ts +0 -57
  105. package/src/generated/sid/v1/admin/user_profile.ts +0 -730
  106. package/src/generated/sid/v1/admin/webhooks.client.ts +0 -269
  107. package/src/generated/sid/v1/admin/webhooks.ts +0 -2804
  108. package/src/generated/sid/v1/attestation/attestation.client.ts +0 -115
  109. package/src/generated/sid/v1/attestation/attestation.ts +0 -813
  110. package/src/generated/sid/v1/authn/auth.client.ts +0 -943
  111. package/src/generated/sid/v1/authn/auth.ts +0 -9719
  112. package/src/generated/sid/v1/authn/flows.client.ts +0 -413
  113. package/src/generated/sid/v1/authn/flows.ts +0 -3205
  114. package/src/generated/sid/v1/authn/issuer.client.ts +0 -489
  115. package/src/generated/sid/v1/authn/issuer.ts +0 -1600
  116. package/src/generated/sid/v1/authn/password_history.client.ts +0 -57
  117. package/src/generated/sid/v1/authn/password_history.ts +0 -578
  118. package/src/generated/sid/v1/authn/system_integration.client.ts +0 -73
  119. package/src/generated/sid/v1/authn/system_integration.ts +0 -218
  120. package/src/generated/sid/v1/authz/authz.client.ts +0 -459
  121. package/src/generated/sid/v1/authz/authz.ts +0 -6524
  122. package/src/generated/sid/v1/authz/forward_auth.client.ts +0 -97
  123. package/src/generated/sid/v1/authz/forward_auth.ts +0 -79
  124. package/src/generated/sid/v1/authz/governance.client.ts +0 -102
  125. package/src/generated/sid/v1/authz/governance.ts +0 -875
  126. package/src/generated/sid/v1/common/errors.ts +0 -710
  127. package/src/generated/sid/v1/common/pagination.ts +0 -152
  128. package/src/generated/sid/v1/common/transport.ts +0 -227
  129. package/src/generated/sid/v1/common/types.ts +0 -151
  130. package/src/generated/sid/v1/events/event_stream.client.ts +0 -67
  131. package/src/generated/sid/v1/events/event_stream.ts +0 -375
  132. package/src/generated/sid/v1/federation/upstream.client.ts +0 -142
  133. package/src/generated/sid/v1/federation/upstream.ts +0 -1736
  134. package/src/generated/sid/v1/identity/identity.client.ts +0 -467
  135. package/src/generated/sid/v1/identity/identity.ts +0 -5676
  136. package/src/generated/sid/v1/ids/ids.ts +0 -395
  137. package/src/generated/sid/v1/machine/machine_user.client.ts +0 -219
  138. package/src/generated/sid/v1/machine/machine_user.ts +0 -2364
  139. package/src/generated/sid/v1/machine/pat.client.ts +0 -130
  140. package/src/generated/sid/v1/machine/pat.ts +0 -1083
  141. package/src/generated/sid/v1/projects/projects.client.ts +0 -404
  142. package/src/generated/sid/v1/projects/projects.ts +0 -4446
  143. package/src/generated/sid/v1/scim/scim.client.ts +0 -520
  144. package/src/generated/sid/v1/scim/scim.ts +0 -3182
  145. package/src/generated/sid/v1/test/test.client.ts +0 -232
  146. package/src/generated/sid/v1/test/test.ts +0 -1136
  147. package/src/transport/adaptive.test.ts +0 -728
  148. package/src/transport/adaptive.ts +0 -308
  149. package/src/transport/framing.test.ts +0 -227
  150. package/src/transport/framing.ts +0 -98
  151. package/src/transport/webtransport.test.ts +0 -728
  152. package/src/transport/webtransport.ts +0 -323
package/README.md CHANGED
@@ -1,23 +1,34 @@
1
1
  # @structured-id/ui-core
2
2
 
3
- Shared UI package for StructuredID applications. Provides Vue 3 composables, Pinia stores, protobuf-ts generated clients, and reusable components.
3
+ Shared UI package for StructuredID applications: Vue 3 composables, Pinia stores and reusable components over the generated clients of [`@structured-id/proto`](https://www.npmjs.com/package/@structured-id/proto).
4
4
 
5
5
  ## What's Inside
6
6
 
7
- - **Proto clients** -- gRPC-Web clients generated from [proto](https://github.com/structured-id/proto) via protobuf-ts
8
- - **Composables** -- `createAuth` (password ceremonies over an injected ZKPP client), `useAuthApi`, `useAccountApi`, `useIdentityApi`, `useSecurityLevel`
7
+ - **Composables** -- `createAuth` (password ceremonies over an injected ZKPP client), `useAuthApi`, `useAccountApi`, `useIdentityApi`, `useSecurityLevel`, transport setup (`initGrpc`, `initAccountApi`)
9
8
  - **Components** -- `SidLoginForm`, `SidRegistrationForm`, `SidMfaChallenge`, `SidPrincipalInput`, profile management pages
10
- - **Utilities** -- `normalizePrincipal`, `detectLoginInputType`, validators, formatters
9
+ - **Utilities** -- `normalizePrincipal`, `detectLoginInputType`, `rpcRefusal`, validators, formatters
10
+
11
+ The generated messages and clients, the `google.rpc` error model and the gRPC-web / WebTransport transport live in `@structured-id/proto`; import them from there (`@structured-id/proto/sid/v1/identity/identity.client`, `@structured-id/proto/transport`).
12
+
13
+ ### Migration from the previous exports
14
+
15
+ The root no longer re-exports generated clients, messages or enums, and the
16
+ `./proto` subpath is removed. Import those symbols from their schema modules in
17
+ `@structured-id/proto`. The `./browser-vp` prototype subpath is also removed;
18
+ this package does not provide a replacement browser-presentation implementation.
19
+ The Vue composables and components keep their UI package owners.
11
20
 
12
21
  ## Usage
13
22
 
14
23
  ```ts
15
24
  import { getTransport, initGrpc } from "@structured-id/ui-core";
16
25
  import { SidLoginForm, createAuth } from "@structured-id/ui-core/profile";
17
- import { loadZkppClient } from "@structured-id/opaque-zkpp";
26
+ import { IdentityServiceClient } from "@structured-id/proto/sid/v1/identity/identity.client";
27
+ import { loadZkppClient } from "@structured-id/opaque";
18
28
 
19
29
  // Initialize gRPC transport
20
30
  initGrpc({ baseUrl: "http://localhost:9080" });
31
+ const identity = new IdentityServiceClient(getTransport());
21
32
 
22
33
  // Password ceremonies (register, login, change, reset) on the app's client
23
34
  const { login } = createAuth(() => loadZkppClient());
@@ -25,25 +36,26 @@ const { login } = createAuth(() => loadZkppClient());
25
36
 
26
37
  ## Development
27
38
 
39
+ The package ships its TypeScript and Vue sources; the consuming application's
40
+ bundler (Vite under Quasar) compiles them, so there is no build step.
41
+
28
42
  ```bash
29
43
  yarn install
30
- yarn build # Build library
31
44
  yarn test # Run tests (vitest)
32
45
  yarn typecheck # vue-tsc --noEmit
46
+ node ci/check-package.mjs # Pack and build an external Vue/Vite consumer
33
47
  ```
34
48
 
35
49
  ## Architecture
36
50
 
37
51
  ```
38
52
  src/
39
- composables/ # Vue 3 composition API
53
+ composables/ # Vue 3 composition API, transport setup
40
54
  stores/ # Pinia stores (auth)
41
55
  profile/ # Profile components and composables
42
56
  components/ # SidLoginForm, SidRegistrationForm, SidMfaChallenge
43
57
  composables/ # createAuth, useAuthApi, useAccountApi, useIdentityApi
44
- utils/ # normalizePrincipal, validators, formatters
45
- generated/ # protobuf-ts generated clients (do not edit)
46
- proto/ # Proto definitions submodule
58
+ utils/ # normalizePrincipal, rpcRefusal, validators, formatters
47
59
  ```
48
60
 
49
61
  ## License
package/package.json CHANGED
@@ -1,30 +1,22 @@
1
1
  {
2
2
  "name": "@structured-id/ui-core",
3
- "version": "1.3.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "registry": "https://registry.npmjs.org"
8
8
  },
9
- "description": "Shared UI package for StructuredID — proto clients, Vue 3 composables, Pinia stores, components.",
9
+ "description": "Shared UI package for StructuredID: Vue 3 composables, Pinia stores and components over the @structured-id/proto clients.",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/structured-id/ui-core.git"
13
13
  },
14
14
  "homepage": "https://github.com/structured-id/ui-core",
15
15
  "bugs": "https://github.com/structured-id/ui-core/issues",
16
- "main": "dist/index.cjs",
17
- "module": "dist/index.js",
18
- "types": "dist/index.d.ts",
16
+ "types": "./src/index.ts",
19
17
  "exports": {
20
18
  ".": {
21
- "types": "./dist/index.d.ts",
22
- "import": "./src/index.ts",
23
- "require": "./dist/index.cjs"
24
- },
25
- "./proto": {
26
- "types": "./src/generated/index.d.ts",
27
- "import": "./src/generated/index.js"
19
+ "import": "./src/index.ts"
28
20
  },
29
21
  "./components": {
30
22
  "import": "./src/components/index.ts"
@@ -32,59 +24,49 @@
32
24
  "./quasar": {
33
25
  "import": "./src/quasar/index.ts"
34
26
  },
35
- "./browser-vp": {
36
- "types": "./dist/browser-vp/index.d.ts",
37
- "import": "./dist/browser-vp/index.js",
38
- "require": "./dist/browser-vp/index.cjs"
39
- },
40
27
  "./profile": {
41
28
  "import": "./src/profile/index.ts"
42
29
  }
43
30
  },
44
31
  "files": [
45
- "dist",
46
32
  "src"
47
33
  ],
48
34
  "scripts": {
49
- "generate": "buf generate --template buf.gen.yaml",
50
- "build": "tsup",
51
- "dev": "tsup --watch",
52
35
  "typecheck": "vue-tsc --noEmit",
53
36
  "lint": "eslint src/",
54
37
  "format": "prettier --write \"src/**/*.{ts,vue}\"",
55
38
  "test": "vitest run",
56
- "test:watch": "vitest",
57
- "prepublishOnly": "yarn generate && yarn build"
39
+ "test:watch": "vitest"
58
40
  },
59
41
  "dependencies": {
60
42
  "@protobuf-ts/grpcweb-transport": "^2.11.1",
61
43
  "@protobuf-ts/runtime": "^2.11.1",
62
44
  "@protobuf-ts/runtime-rpc": "^2.11.1",
63
- "libphonenumber-js": "^1.12.39"
45
+ "@structured-id/proto": "^0.2.0",
46
+ "libphonenumber-js": "^1.13.15"
64
47
  },
65
48
  "devDependencies": {
66
- "@bufbuild/buf": "^1.47.0",
67
- "@protobuf-ts/plugin": "^2.11.1",
68
- "@vitejs/plugin-vue": "^6.0.5",
69
- "@vue/compiler-dom": "^3.5.31",
70
- "@vue/test-utils": "^2.4.6",
71
- "eslint": "^9",
72
- "jsdom": "^29.0.1",
73
- "pinia": "^2.1.0",
74
- "prettier": "^3.0.0",
75
- "tsup": "^8.5.0",
76
- "typescript": "^5.3.0",
77
- "typescript-eslint": "^8.57.0",
78
- "vitest": "^1.0.0",
79
- "vue": "^3.4.0",
80
- "vue-router": "^4.2.0",
81
- "vue-tsc": "^2.0.0"
49
+ "@vitejs/plugin-vue": "^6.0.9",
50
+ "@vue/compiler-dom": "^3.5.43",
51
+ "@vue/devtools-api": "^8.2.1",
52
+ "@vue/test-utils": "^2.5.1",
53
+ "eslint": "^10.12.0",
54
+ "jsdom": "^30.1.2",
55
+ "pinia": "^4.0.3",
56
+ "prettier": "^3.9.9",
57
+ "typescript": "^6.0.3",
58
+ "typescript-eslint": "^8.71.1",
59
+ "vite": "^8.3.4",
60
+ "vitest": "^5.0.3",
61
+ "vue": "^3.5.43",
62
+ "vue-router": "^5.4.0",
63
+ "vue-tsc": "^3.3.12"
82
64
  },
83
65
  "peerDependencies": {
84
- "pinia": "^2.1.0",
66
+ "pinia": "^4.0.3",
85
67
  "quasar": "^2.17.0",
86
- "vue": "^3.4.0",
87
- "vue-router": "^4.2.0"
68
+ "vue": "^3.5.43",
69
+ "vue-router": "^5.4.0"
88
70
  },
89
71
  "peerDependenciesMeta": {
90
72
  "quasar": {
@@ -7,11 +7,7 @@
7
7
  */
8
8
 
9
9
  export type BadgeVariant =
10
- | "active"
11
- | "inactive"
12
- | "pending"
13
- | "error"
14
- | "warning";
10
+ "active" | "inactive" | "pending" | "error" | "warning";
15
11
 
16
12
  const props = withDefaults(
17
13
  defineProps<{
@@ -12,7 +12,8 @@ const created: Array<{
12
12
  }> = [];
13
13
 
14
14
  vi.mock("@protobuf-ts/grpcweb-transport", () => ({
15
- GrpcWebFetchTransport: vi.fn().mockImplementation((options) => {
15
+ // Called with `new`: a constructor mock must be a `function`, not an arrow.
16
+ GrpcWebFetchTransport: vi.fn().mockImplementation(function (options) {
16
17
  created.push(options);
17
18
  return { type: "grpc-web-transport" };
18
19
  }),
@@ -5,32 +5,35 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
5
5
  // ---------------------------------------------------------------------------
6
6
 
7
7
  const mockAdaptiveInit = vi
8
- .fn<[], Promise<void>>()
8
+ .fn<() => Promise<void>>()
9
9
  .mockResolvedValue(undefined);
10
10
  const mockAdaptiveClose = vi.fn();
11
11
  const mockAdaptiveActiveTransport = vi
12
- .fn<[], string>()
12
+ .fn<() => string>()
13
13
  .mockReturnValue("grpc-web");
14
14
 
15
- vi.mock("../transport/adaptive", () => ({
16
- AdaptiveRpcTransport: vi.fn().mockImplementation(() => ({
17
- init: mockAdaptiveInit,
18
- close: mockAdaptiveClose,
19
- get activeTransport() {
20
- return mockAdaptiveActiveTransport();
21
- },
22
- })),
15
+ vi.mock("@structured-id/proto/transport", () => ({
16
+ // Constructor mocks are `function`s: they are called with `new`.
17
+ AdaptiveRpcTransport: vi.fn().mockImplementation(function () {
18
+ return {
19
+ init: mockAdaptiveInit,
20
+ close: mockAdaptiveClose,
21
+ get activeTransport() {
22
+ return mockAdaptiveActiveTransport();
23
+ },
24
+ };
25
+ }),
23
26
  }));
24
27
 
25
28
  vi.mock("@protobuf-ts/grpcweb-transport", () => ({
26
- GrpcWebFetchTransport: vi
27
- .fn()
28
- .mockImplementation(() => ({ type: "grpc-web-transport" })),
29
+ GrpcWebFetchTransport: vi.fn().mockImplementation(function () {
30
+ return { type: "grpc-web-transport" };
31
+ }),
29
32
  }));
30
33
 
31
34
  // Import AFTER mocks
32
35
  import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
33
- import { AdaptiveRpcTransport } from "../transport/adaptive";
36
+ import { AdaptiveRpcTransport } from "@structured-id/proto/transport";
34
37
  import {
35
38
  initGrpc,
36
39
  getTransport,
@@ -2,8 +2,10 @@ import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
2
2
  import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
3
3
  import { ref, type Ref } from "vue";
4
4
  import type { GrpcConfig } from "../types/auth";
5
- import { AdaptiveRpcTransport } from "../transport/adaptive";
6
- import type { TransportType } from "../transport/adaptive";
5
+ import {
6
+ AdaptiveRpcTransport,
7
+ type TransportType,
8
+ } from "@structured-id/proto/transport";
7
9
 
8
10
  /** Shared RPC transport instance (gRPC-web or AdaptiveRpcTransport). */
9
11
  let _transport: RpcTransport | null = null;
package/src/index.ts CHANGED
@@ -69,123 +69,5 @@ export type { AuthState, SessionInfo, GrpcConfig } from "./types";
69
69
  // i18n key constants
70
70
  export { AUTH_KEYS, COMMON_KEYS } from "./i18n";
71
71
 
72
- // Generated proto clients and types
73
- export { AuthServiceClient } from "./generated/sid/v1/authn/auth.client";
74
- export { IdentityServiceClient } from "./generated/sid/v1/identity/identity.client";
75
- export { AccountServiceClient } from "./generated/sid/v1/account/account.client";
76
- export { UpstreamServiceClient } from "./generated/sid/v1/federation/upstream.client";
77
- export { EnrollmentServiceClient } from "./generated/sid/v1/admin/enrollment.client";
78
- export type { InstanceStatus } from "./generated/sid/v1/admin/enrollment";
79
-
80
- // Re-export commonly used proto types
81
- export type {
82
- OpaqueLoginStartRequest,
83
- OpaqueLoginStartResponse,
84
- OpaqueLoginFinishRequest,
85
- OpaqueLoginFinishResponse,
86
- OpaqueRegistrationStartRequest,
87
- OpaqueRegistrationStartResponse,
88
- OpaqueRegistrationFinishRequest,
89
- OpaqueRegistrationFinishResponse,
90
- MfaVerifyResponse,
91
- TotpEnrollmentChallenge,
92
- FinishTotpEnrollmentResponse,
93
- ResendSmsMfaResponse,
94
- GenerateRecoveryCodesResponse,
95
- VerifyOtpResponse,
96
- ResendOtpResponse,
97
- VerifyPhoneResponse,
98
- ConfirmIdpLinkResponse,
99
- UserOrganization,
100
- ListUserOrganizationsResponse,
101
- SelectOrganizationResponse,
102
- GetTermsResponse,
103
- AcceptTermsResponse,
104
- CibaConsentResponse,
105
- VerifyCaptchaResponse,
106
- DynamicPrompt,
107
- DynamicPromptField,
108
- SubmitDynamicPromptResponse,
109
- StepUpChallenge,
110
- CompleteStepUpResponse,
111
- DeleteMfaCredentialResponse,
112
- ConfirmAccountDeletionResponse,
113
- SubmitDeviceUserCodeResponse,
114
- OAuth2AuthorizeResponse,
115
- WebAuthnAuthenticationStartResponse,
116
- WebAuthnAuthenticationFinishResponse,
117
- WebAuthnRegistrationStartResponse,
118
- WebAuthnRegistrationFinishResponse,
119
- RequestMagicLinkResponse,
120
- VerifyMagicLinkResponse,
121
- RequestPasswordResetResponse,
122
- VerifyPasswordResetResponse,
123
- ExecutePasswordResetResponse,
124
- CompletePasswordResetResponse,
125
- OpaqueZkppRegistrationStartResponse,
126
- OpaqueZkppRegistrationFinishResponse,
127
- PasswordChangeChallengeResponse,
128
- PasswordChangeExecuteResponse,
129
- PasswordChangeFinishResponse,
130
- } from "./generated/sid/v1/authn/auth";
131
-
132
- // Password operations: the history evaluator and the proof the finish carries
133
- export { PasswordHistoryEvaluatorServiceClient } from "./generated/sid/v1/authn/password_history.client";
134
- export type {
135
- PasswordHistoryContext,
136
- PasswordHistoryDomain,
137
- PasswordHistoryEvaluation,
138
- PasswordHistoryEvaluationProof,
139
- PasswordRegistrationProof,
140
- } from "./generated/sid/v1/authn/password_history";
141
- export type { PasswordOperationId } from "./generated/sid/v1/ids/ids";
142
-
143
- export type {
144
- Profile,
145
- Session,
146
- Credential,
147
- Principal,
148
- } from "./generated/sid/v1/identity/identity";
149
-
150
- export { StepUpMethod } from "./generated/sid/v1/authn/auth";
151
-
152
- export {
153
- ProfileStatus,
154
- PrincipalType,
155
- CredentialType,
156
- AuthenticatorAttachment,
157
- } from "./generated/sid/v1/identity/identity";
158
-
159
- export type {
160
- ConsentInfo,
161
- ClaimConsent,
162
- ActivityEvent,
163
- ListConsentsResponse,
164
- GetConsentDetailResponse,
165
- ListActivityEventsResponse,
166
- LinkedAccount,
167
- ListLinkedAccountsResponse,
168
- LinkAccountResponse,
169
- ReauthenticateLinkResponse,
170
- AvailableApp,
171
- ListAvailableAppsResponse,
172
- LaunchAppResponse,
173
- PasswordlessStatus,
174
- DisablePasswordlessResponse,
175
- } from "./generated/sid/v1/account/account";
176
-
177
- export {
178
- ConsentStatus,
179
- ClaimType,
180
- ClaimConsentStatus,
181
- ActivityEventStatus,
182
- LinkedAccountTrustCategory,
183
- LinkedAccountStatus,
184
- } from "./generated/sid/v1/account/account";
185
-
186
- export type { EnabledProviderInfo } from "./generated/sid/v1/federation/upstream";
187
-
188
- export { Timestamp } from "./generated/google/protobuf/timestamp";
189
-
190
- // Browser VP — import from '@structured-id/ui-core/browser-vp'
191
- // Separate entry point: WebAuthn PRF, IndexedDB, VP creation (browser-only APIs)
72
+ // Generated clients and messages come from '@structured-id/proto'
73
+ // (e.g. '@structured-id/proto/sid/v1/identity/identity.client').
@@ -66,7 +66,11 @@
66
66
  </template>
67
67
 
68
68
  <script setup lang="ts">
69
- import { type ConsentInfo, ConsentStatus, Timestamp } from "../../index";
69
+ import {
70
+ type ConsentInfo,
71
+ ConsentStatus,
72
+ } from "@structured-id/proto/sid/v1/account/account";
73
+ import { Timestamp } from "@structured-id/proto/google/protobuf/timestamp";
70
74
 
71
75
  defineProps<{
72
76
  consents: ConsentInfo[];
@@ -86,7 +86,7 @@
86
86
 
87
87
  <script setup lang="ts">
88
88
  import { reactive, computed, watch } from "vue";
89
- import type { Profile } from "../../index";
89
+ import type { Profile } from "@structured-id/proto/sid/v1/identity/identity";
90
90
 
91
91
  const props = withDefaults(
92
92
  defineProps<{
@@ -96,7 +96,11 @@
96
96
  </template>
97
97
 
98
98
  <script setup lang="ts">
99
- import { type Credential, CredentialType, Timestamp } from "../../index";
99
+ import {
100
+ type Credential,
101
+ CredentialType,
102
+ } from "@structured-id/proto/sid/v1/identity/identity";
103
+ import { Timestamp } from "@structured-id/proto/google/protobuf/timestamp";
100
104
  import type { FactorCategory } from "../composables/useSecurityLevel";
101
105
 
102
106
  defineProps<{
@@ -49,7 +49,8 @@
49
49
  </template>
50
50
 
51
51
  <script setup lang="ts">
52
- import { type Session, Timestamp } from "../../index";
52
+ import type { Session } from "@structured-id/proto/sid/v1/identity/identity";
53
+ import { Timestamp } from "@structured-id/proto/google/protobuf/timestamp";
53
54
 
54
55
  defineProps<{
55
56
  sessions: Session[];
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi } from "vitest";
2
- import { mount } from "@vue/test-utils";
2
+ import { flushPromises, mount } from "@vue/test-utils";
3
3
  import SidRegistrationForm from "./SidRegistrationForm.vue";
4
4
 
5
5
  const quasarStubs = {
@@ -22,6 +22,13 @@ const quasarStubs = {
22
22
  '<input :value="modelValue" @input="$emit(\'update:modelValue\', $event.target.value)" />',
23
23
  },
24
24
  "q-btn": { template: '<button type="submit"><slot /></button>' },
25
+ "sid-password-ceremony": {
26
+ name: "SidPasswordCeremony",
27
+ props: ["outcome", "refusedText", "length", "fields"],
28
+ emits: ["settled"],
29
+ template:
30
+ '<div class="ceremony" :data-outcome="outcome" :data-refused="refusedText" :data-length="length" :data-fields="fields" />',
31
+ },
25
32
  "sid-principal-input": {
26
33
  props: ["modelValue", "disable", "label"],
27
34
  emits: ["update:modelValue", "update:principal-type"],
@@ -89,6 +96,23 @@ describe("SidRegistrationForm", () => {
89
96
  expect(w.findAll("input")).toHaveLength(3);
90
97
  });
91
98
 
99
+ // The ceremony folds as many rows as the form had password fields.
100
+ it("gives the ceremony the form's password fields", async () => {
101
+ for (const [showConfirmPassword, fields] of [
102
+ [true, "2"],
103
+ [false, "1"],
104
+ ] as const) {
105
+ const w = mountForm({ claimRequired: false, showConfirmPassword });
106
+ const inputs = w.findAll("input");
107
+ await inputs[0].setValue("owner@example.com");
108
+ for (const input of inputs.slice(1))
109
+ await input.setValue("Secret-Pass-123");
110
+ await w.find("form").trigger("submit");
111
+ await flushPromises();
112
+ expect(w.find(".ceremony").attributes("data-fields")).toBe(fields);
113
+ }
114
+ });
115
+
92
116
  // The pasted token reaches registerFn trimmed, and no invite code with it.
93
117
  it("submits the trimmed claim token", async () => {
94
118
  registerFn.mockClear();
@@ -125,6 +149,154 @@ describe("SidRegistrationForm", () => {
125
149
  );
126
150
  });
127
151
 
152
+ // While the registration runs the ceremony stands in for the fields; the
153
+ // account is announced only once its closing animation has played, and the
154
+ // form then settles on its success text, without the fields.
155
+ it("shows the ceremony and announces success after it settles", async () => {
156
+ let finish: () => void = () => {};
157
+ registerFn.mockImplementationOnce(
158
+ () => new Promise<void>((resolve) => (finish = resolve)),
159
+ );
160
+ const w = mountForm();
161
+ const [principal, password, confirm] = w.findAll("input");
162
+ await principal.setValue("user@example.com");
163
+ await password.setValue("Secret-Pass-123");
164
+ await confirm.setValue("Secret-Pass-123");
165
+ await w.find("form").trigger("submit");
166
+
167
+ const ceremony = w.find(".ceremony");
168
+ expect(ceremony.attributes("data-outcome")).toBe("running");
169
+ expect(ceremony.attributes("data-length")).toBe("15");
170
+ expect(w.find("form").isVisible()).toBe(false);
171
+
172
+ finish();
173
+ await flushPromises();
174
+ expect(w.find(".ceremony").attributes("data-outcome")).toBe("accepted");
175
+ expect(w.emitted("success")).toBeUndefined();
176
+ w.findComponent({ name: "SidPasswordCeremony" }).vm.$emit(
177
+ "settled",
178
+ "accepted",
179
+ );
180
+ expect(w.emitted("success")).toHaveLength(1);
181
+ await flushPromises();
182
+ expect(w.find(".ceremony").exists()).toBe(false);
183
+ expect(w.text()).toContain("Account created. Signing you in");
184
+ expect(w.find("form").isVisible()).toBe(false);
185
+ });
186
+
187
+ // A refusal names its reason in the ceremony, then gives the fields back
188
+ // with the reason above them.
189
+ it("gives the fields back with the reason after a refusal", async () => {
190
+ registerFn.mockRejectedValueOnce(new Error("This identifier is taken"));
191
+ const w = mountForm();
192
+ const [principal, password, confirm] = w.findAll("input");
193
+ await principal.setValue("user@example.com");
194
+ await password.setValue("Secret-Pass-123");
195
+ await confirm.setValue("Secret-Pass-123");
196
+ await w.find("form").trigger("submit");
197
+ await flushPromises();
198
+
199
+ const ceremony = w.find(".ceremony");
200
+ expect(ceremony.attributes("data-outcome")).toBe("refused");
201
+ expect(ceremony.attributes("data-refused")).toBe(
202
+ "This identifier is taken",
203
+ );
204
+ w.findComponent({ name: "SidPasswordCeremony" }).vm.$emit(
205
+ "settled",
206
+ "refused",
207
+ );
208
+ await flushPromises();
209
+ expect(w.find(".ceremony").exists()).toBe(false);
210
+ expect(w.find("form").isVisible()).toBe(true);
211
+ expect(w.text()).toContain("This identifier is taken");
212
+ expect(w.emitted("error")).toHaveLength(1);
213
+ });
214
+
215
+ // An application that draws its own progress keeps the `progress` slot:
216
+ // it replaces the ceremony and receives the progress while the operation
217
+ // runs. The final report comes right before the operation resolves, so
218
+ // the slot keeps it for a painted frame; with no closing animation to wait
219
+ // for, the form then announces the result.
220
+ it("lets a progress slot stand in for the ceremony", async () => {
221
+ const frames: FrameRequestCallback[] = [];
222
+ vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) =>
223
+ frames.push(cb),
224
+ );
225
+ const paint = async () => {
226
+ for (const cb of frames.splice(0)) cb(0);
227
+ await flushPromises();
228
+ };
229
+ let report: (p: { fraction: number; label: string }) => void = () => {};
230
+ let finish: () => void = () => {};
231
+ registerFn.mockImplementationOnce(
232
+ (_id, _pw, _extras, onProgress) =>
233
+ new Promise<void>((resolve) => {
234
+ report = onProgress;
235
+ finish = () => {
236
+ onProgress({ fraction: 1, label: "Done" });
237
+ resolve();
238
+ };
239
+ }),
240
+ );
241
+ const w = mount(SidRegistrationForm, {
242
+ props: { registerFn },
243
+ global: { stubs: quasarStubs },
244
+ slots: {
245
+ progress: `<template #progress="{ progress }">
246
+ <div class="own-gauge">{{ progress?.label }}</div>
247
+ </template>`,
248
+ },
249
+ });
250
+ const [principal, password, confirm] = w.findAll("input");
251
+ await principal.setValue("user@example.com");
252
+ await password.setValue("Secret-Pass-123");
253
+ await confirm.setValue("Secret-Pass-123");
254
+ await w.find("form").trigger("submit");
255
+
256
+ report({ fraction: 0.4, label: "Building the proof" });
257
+ await flushPromises();
258
+ expect(w.find(".ceremony").exists()).toBe(false);
259
+ expect(w.find(".own-gauge").text()).toBe("Building the proof");
260
+
261
+ finish();
262
+ await flushPromises();
263
+ expect(w.find(".own-gauge").text()).toBe("Done");
264
+ expect(w.emitted("success")).toBeUndefined();
265
+
266
+ await paint();
267
+ await paint();
268
+ expect(w.emitted("success")).toHaveLength(1);
269
+ expect(w.find(".own-gauge").exists()).toBe(false);
270
+ expect(w.text()).toContain("Account created. Signing you in");
271
+ vi.unstubAllGlobals();
272
+ });
273
+
274
+ // A background tab may never run animation frames: the result is still
275
+ // announced, once, after a short timer, so a parent's sign-in or
276
+ // navigation is never held back by a paint that does not come.
277
+ it("announces the result without animation frames", async () => {
278
+ vi.useFakeTimers();
279
+ vi.stubGlobal("requestAnimationFrame", () => 0);
280
+ registerFn.mockResolvedValueOnce(undefined);
281
+ const w = mount(SidRegistrationForm, {
282
+ props: { registerFn },
283
+ global: { stubs: quasarStubs },
284
+ slots: { progress: `<div class="own-gauge" />` },
285
+ });
286
+ const [principal, password, confirm] = w.findAll("input");
287
+ await principal.setValue("user@example.com");
288
+ await password.setValue("Secret-Pass-123");
289
+ await confirm.setValue("Secret-Pass-123");
290
+ await w.find("form").trigger("submit");
291
+ await flushPromises();
292
+ expect(w.emitted("success")).toBeUndefined();
293
+
294
+ await vi.advanceTimersByTimeAsync(1000);
295
+ expect(w.emitted("success")).toHaveLength(1);
296
+ vi.unstubAllGlobals();
297
+ vi.useRealTimers();
298
+ });
299
+
128
300
  it("renders footer slot", () => {
129
301
  const w = mount(SidRegistrationForm, {
130
302
  props: { registerFn },