@ic-reactor/react 3.6.0 → 3.8.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 (131) hide show
  1. package/README.md +117 -13
  2. package/dist/auth/authentication-manager.d.ts +98 -0
  3. package/dist/auth/authentication-manager.d.ts.map +1 -0
  4. package/dist/auth/authentication-manager.js +532 -0
  5. package/dist/auth/authentication-manager.js.map +1 -0
  6. package/dist/auth/constants.d.ts +18 -0
  7. package/dist/auth/constants.d.ts.map +1 -0
  8. package/dist/auth/constants.js +18 -0
  9. package/dist/auth/constants.js.map +1 -0
  10. package/dist/auth/createIdentityAttributeHooks.d.ts +14 -0
  11. package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -0
  12. package/dist/auth/createIdentityAttributeHooks.js +54 -0
  13. package/dist/auth/createIdentityAttributeHooks.js.map +1 -0
  14. package/dist/auth/identity-attributes-manager.d.ts +26 -0
  15. package/dist/auth/identity-attributes-manager.d.ts.map +1 -0
  16. package/dist/auth/identity-attributes-manager.js +108 -0
  17. package/dist/auth/identity-attributes-manager.js.map +1 -0
  18. package/dist/auth/identity-attributes.d.ts +19 -0
  19. package/dist/auth/identity-attributes.d.ts.map +1 -0
  20. package/dist/auth/identity-attributes.js +170 -0
  21. package/dist/auth/identity-attributes.js.map +1 -0
  22. package/dist/auth/index.d.ts +8 -0
  23. package/dist/auth/index.d.ts.map +1 -0
  24. package/dist/auth/index.js +8 -0
  25. package/dist/auth/index.js.map +1 -0
  26. package/dist/auth/types.d.ts +182 -0
  27. package/dist/auth/types.d.ts.map +1 -0
  28. package/dist/auth/types.js +2 -0
  29. package/dist/auth/types.js.map +1 -0
  30. package/dist/createActorHooks.d.ts +16 -16
  31. package/dist/createActorHooks.d.ts.map +1 -1
  32. package/dist/createActorHooks.js +6 -6
  33. package/dist/createActorHooks.js.map +1 -1
  34. package/dist/createInfiniteQuery.d.ts +33 -33
  35. package/dist/createInfiniteQuery.d.ts.map +1 -1
  36. package/dist/createInfiniteQuery.js +6 -6
  37. package/dist/createInfiniteQuery.js.map +1 -1
  38. package/dist/createMutation.d.ts +13 -2
  39. package/dist/createMutation.d.ts.map +1 -1
  40. package/dist/createMutation.js +11 -0
  41. package/dist/createMutation.js.map +1 -1
  42. package/dist/createQuery.d.ts +13 -3
  43. package/dist/createQuery.d.ts.map +1 -1
  44. package/dist/createQuery.js +16 -6
  45. package/dist/createQuery.js.map +1 -1
  46. package/dist/createSuspenseInfiniteQuery.d.ts +32 -32
  47. package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
  48. package/dist/createSuspenseInfiniteQuery.js +6 -6
  49. package/dist/createSuspenseInfiniteQuery.js.map +1 -1
  50. package/dist/createSuspenseQuery.d.ts +3 -3
  51. package/dist/createSuspenseQuery.d.ts.map +1 -1
  52. package/dist/createSuspenseQuery.js +1 -1
  53. package/dist/createSuspenseQuery.js.map +1 -1
  54. package/dist/defineReactor.d.ts +124 -0
  55. package/dist/defineReactor.d.ts.map +1 -0
  56. package/dist/defineReactor.js +145 -0
  57. package/dist/defineReactor.js.map +1 -0
  58. package/dist/{createAuthHooks.d.ts → hooks/createAuthHooks.d.ts} +6 -13
  59. package/dist/hooks/createAuthHooks.d.ts.map +1 -0
  60. package/dist/{createAuthHooks.js → hooks/createAuthHooks.js} +18 -58
  61. package/dist/hooks/createAuthHooks.js.map +1 -0
  62. package/dist/hooks/index.d.ts +2 -0
  63. package/dist/hooks/index.d.ts.map +1 -1
  64. package/dist/hooks/index.js +2 -0
  65. package/dist/hooks/index.js.map +1 -1
  66. package/dist/hooks/useActorInfiniteQuery.d.ts +9 -9
  67. package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
  68. package/dist/hooks/useActorInfiniteQuery.js +3 -1
  69. package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
  70. package/dist/hooks/useActorMethod.d.ts +17 -17
  71. package/dist/hooks/useActorMethod.d.ts.map +1 -1
  72. package/dist/hooks/useActorMethod.js.map +1 -1
  73. package/dist/hooks/useActorMutation.d.ts +18 -7
  74. package/dist/hooks/useActorMutation.d.ts.map +1 -1
  75. package/dist/hooks/useActorMutation.js +11 -0
  76. package/dist/hooks/useActorMutation.js.map +1 -1
  77. package/dist/hooks/useActorQuery.d.ts +8 -8
  78. package/dist/hooks/useActorQuery.d.ts.map +1 -1
  79. package/dist/hooks/useActorQuery.js.map +1 -1
  80. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +9 -9
  81. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
  82. package/dist/hooks/useActorSuspenseInfiniteQuery.js +3 -1
  83. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
  84. package/dist/hooks/useActorSuspenseQuery.d.ts +8 -8
  85. package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
  86. package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
  87. package/dist/index.d.ts +11 -10
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +11 -10
  90. package/dist/index.js.map +1 -1
  91. package/dist/types.d.ts +24 -24
  92. package/dist/types.d.ts.map +1 -1
  93. package/dist/utils.d.ts +3 -0
  94. package/dist/utils.d.ts.map +1 -1
  95. package/dist/utils.js +10 -2
  96. package/dist/utils.js.map +1 -1
  97. package/llms.txt +48 -0
  98. package/package.json +34 -23
  99. package/src/auth/authentication-manager.ts +591 -0
  100. package/src/auth/constants.ts +25 -0
  101. package/src/auth/createIdentityAttributeHooks.ts +83 -0
  102. package/src/auth/identity-attributes-manager.ts +136 -0
  103. package/src/auth/identity-attributes.ts +270 -0
  104. package/src/auth/index.ts +7 -0
  105. package/src/auth/types.ts +204 -0
  106. package/src/createActorHooks.ts +90 -58
  107. package/src/createInfiniteQuery.ts +154 -98
  108. package/src/createMutation.ts +35 -19
  109. package/src/createQuery.ts +94 -50
  110. package/src/createSuspenseInfiniteQuery.ts +166 -115
  111. package/src/createSuspenseQuery.ts +69 -43
  112. package/src/defineReactor.ts +272 -0
  113. package/src/{createAuthHooks.ts → hooks/createAuthHooks.ts} +23 -99
  114. package/src/hooks/index.ts +4 -0
  115. package/src/hooks/useActorInfiniteQuery.ts +44 -30
  116. package/src/hooks/useActorMethod.ts +62 -53
  117. package/src/hooks/useActorMutation.ts +47 -30
  118. package/src/hooks/useActorQuery.ts +36 -32
  119. package/src/hooks/useActorSuspenseInfiniteQuery.ts +56 -30
  120. package/src/hooks/useActorSuspenseQuery.ts +42 -32
  121. package/src/index.ts +12 -10
  122. package/src/types.ts +76 -71
  123. package/src/utils.ts +15 -2
  124. package/dist/createAuthHooks.d.ts.map +0 -1
  125. package/dist/createAuthHooks.js.map +0 -1
  126. package/src/hooks/useActorInfiniteQuery.test.tsx +0 -457
  127. package/src/hooks/useActorMethod.test.tsx +0 -798
  128. package/src/hooks/useActorMutation.test.tsx +0 -220
  129. package/src/hooks/useActorQuery.test.tsx +0 -287
  130. package/src/hooks/useActorSuspenseInfiniteQuery.test.tsx +0 -472
  131. package/src/hooks/useActorSuspenseQuery.test.tsx +0 -254
@@ -0,0 +1,25 @@
1
+ export const IC_INTERNET_IDENTITY_PROVIDER = "https://id.ai/authorize"
2
+
3
+ /**
4
+ * `ic_env` key used to provide a fully resolved Internet Identity URL.
5
+ */
6
+ export const INTERNET_IDENTITY_PROVIDER_ENV_KEY = "INTERNET_IDENTITY_PROVIDER"
7
+
8
+ /**
9
+ * Well-known Internet Identity canister ID used when deploying II locally.
10
+ */
11
+ export const LOCAL_INTERNET_IDENTITY_CANISTER_ID = "rdmx6-jaaaa-aaaaa-aaadq-cai"
12
+
13
+ /**
14
+ * Builds the local Internet Identity provider URL for the given replica port.
15
+ */
16
+ export const localInternetIdentityProvider = (
17
+ port: number,
18
+ canisterId: string = LOCAL_INTERNET_IDENTITY_CANISTER_ID
19
+ ) => `http://${canisterId}.localhost:${port}/authorize`
20
+
21
+ /**
22
+ * Legacy default URL for local Internet Identity, using port `4943`.
23
+ */
24
+ export const LOCAL_INTERNET_IDENTITY_PROVIDER =
25
+ localInternetIdentityProvider(4943)
@@ -0,0 +1,83 @@
1
+ import { useCallback, useState } from "react"
2
+ import type { IdentityAttributesManager } from "./identity-attributes-manager.js"
3
+ import type {
4
+ IdentityAttributeResult,
5
+ RequestIdentityAttributesParameters,
6
+ RequestOpenIdIdentityAttributesParameters,
7
+ } from "./types.js"
8
+
9
+ export interface UseIdentityAttributesReturn {
10
+ requestAttributes: (
11
+ params: RequestIdentityAttributesParameters
12
+ ) => Promise<IdentityAttributeResult>
13
+ requestOpenIdAttributes: (
14
+ params: RequestOpenIdIdentityAttributesParameters
15
+ ) => Promise<IdentityAttributeResult>
16
+ attributes: IdentityAttributeResult | null
17
+ isRequestingAttributes: boolean
18
+ attributeError: Error | null
19
+ clearAttributes: () => void
20
+ }
21
+
22
+ export function createIdentityAttributeHooks(
23
+ identityAttributes: IdentityAttributesManager
24
+ ) {
25
+ const useIdentityAttributes = (): UseIdentityAttributesReturn => {
26
+ const [attributes, setAttributes] =
27
+ useState<IdentityAttributeResult | null>(null)
28
+ const [isRequestingAttributes, setIsRequestingAttributes] = useState(false)
29
+ const [attributeError, setAttributeError] = useState<Error | null>(null)
30
+
31
+ const requestAttributes = useCallback(
32
+ async (params: RequestIdentityAttributesParameters) => {
33
+ setIsRequestingAttributes(true)
34
+ setAttributeError(null)
35
+ try {
36
+ const result = await identityAttributes.request(params)
37
+ setAttributes(result)
38
+ return result
39
+ } catch (error) {
40
+ setAttributeError(error as Error)
41
+ throw error
42
+ } finally {
43
+ setIsRequestingAttributes(false)
44
+ }
45
+ },
46
+ []
47
+ )
48
+
49
+ const requestOpenIdAttributes = useCallback(
50
+ async (params: RequestOpenIdIdentityAttributesParameters) => {
51
+ setIsRequestingAttributes(true)
52
+ setAttributeError(null)
53
+ try {
54
+ const result = await identityAttributes.requestOpenId(params)
55
+ setAttributes(result)
56
+ return result
57
+ } catch (error) {
58
+ setAttributeError(error as Error)
59
+ throw error
60
+ } finally {
61
+ setIsRequestingAttributes(false)
62
+ }
63
+ },
64
+ []
65
+ )
66
+
67
+ const clearAttributes = useCallback(() => {
68
+ setAttributes(null)
69
+ setAttributeError(null)
70
+ }, [])
71
+
72
+ return {
73
+ requestAttributes,
74
+ requestOpenIdAttributes,
75
+ attributes,
76
+ isRequestingAttributes,
77
+ attributeError,
78
+ clearAttributes,
79
+ }
80
+ }
81
+
82
+ return { useIdentityAttributes }
83
+ }
@@ -0,0 +1,136 @@
1
+ import type {
2
+ AuthClientLike,
3
+ IdentityAttributeNonce,
4
+ IdentityAttributeResult,
5
+ RequestIdentityAttributesParameters,
6
+ RequestOpenIdIdentityAttributesParameters,
7
+ } from "./types.js"
8
+
9
+ import { AuthenticationManager } from "./authentication-manager.js"
10
+ import {
11
+ decodeIdentityAttributeValues,
12
+ identityAttributeKeys,
13
+ normalizeSignedIdentityAttributes,
14
+ } from "./identity-attributes.js"
15
+
16
+ /**
17
+ * Requests and decodes signed identity attributes for an authenticated
18
+ * Internet Identity session.
19
+ *
20
+ * Await `authentication.prepareClient()` once during startup (the `useAuth`
21
+ * hook already does) so that {@link request} can open the identity provider
22
+ * window synchronously from a click handler.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const attributes = new IdentityAttributesManager(authentication)
27
+ * const result = await attributes.request({
28
+ * keys: ["name"],
29
+ * nonce: () => fetchNonceFromCanister(),
30
+ * })
31
+ * ```
32
+ */
33
+ export class IdentityAttributesManager {
34
+ constructor(public readonly authentication: AuthenticationManager) {}
35
+
36
+ public request = async ({
37
+ keys,
38
+ nonce,
39
+ signIn = true,
40
+ maxTimeToLive,
41
+ targets,
42
+ ...clientOptions
43
+ }: RequestIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
44
+ // Nothing may be awaited before `requestAttributes` / `signIn` are invoked:
45
+ // the ICRC-29 transport only opens the identity provider window while the
46
+ // browser still considers us inside the user gesture. `getPreparedClient`
47
+ // is synchronous whenever `prepareClient()` has run.
48
+ const preparedClient = this.authentication.getPreparedClient(clientOptions)
49
+ const authClient =
50
+ preparedClient ?? (await this.authentication.ensureClient(clientOptions))
51
+
52
+ if (!authClient) {
53
+ throw new Error(
54
+ "Authentication module is missing or failed to initialize. To request identity attributes, please install @icp-sdk/auth v7 or later, or provide a compatible authClient."
55
+ )
56
+ }
57
+
58
+ this.authentication.setAuthenticating()
59
+
60
+ try {
61
+ // `signIn` first: it opens the transport channel synchronously, and the
62
+ // attribute request then reuses that same channel and window.
63
+ const identityPromise = signIn
64
+ ? this.authentication.signInOrRecoverIdentity({
65
+ maxTimeToLive,
66
+ targets,
67
+ })
68
+ : Promise.resolve(authClient.getIdentity())
69
+ const requestPromise = authClient.requestAttributes({
70
+ keys,
71
+ nonce: toAuthClientNonce(authClient, nonce),
72
+ })
73
+
74
+ const [signedAttributes, identity] = await Promise.all([
75
+ requestPromise,
76
+ identityPromise,
77
+ ])
78
+
79
+ const finalIdentity = identity ?? (await authClient.getIdentity())
80
+ const isAuthenticated = await authClient.isAuthenticated()
81
+ await this.authentication.commitIdentity(finalIdentity, isAuthenticated)
82
+
83
+ const normalizedSignedAttributes =
84
+ normalizeSignedIdentityAttributes(signedAttributes)
85
+
86
+ return {
87
+ principal: finalIdentity.getPrincipal().toText(),
88
+ requestedKeys: keys,
89
+ signedAttributes: normalizedSignedAttributes,
90
+ decodedAttributes: decodeIdentityAttributeValues(
91
+ normalizedSignedAttributes.data,
92
+ keys
93
+ ),
94
+ completedAt: new Date().toISOString(),
95
+ }
96
+ } catch (error) {
97
+ this.authentication.setAuthenticationError(error as Error)
98
+ throw error
99
+ }
100
+ }
101
+
102
+ public requestOpenId = ({
103
+ keys,
104
+ openIdProvider,
105
+ ...rest
106
+ }: RequestOpenIdIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
107
+ // Called (not awaited) synchronously so the user gesture is preserved.
108
+ return this.request({
109
+ ...rest,
110
+ openIdProvider,
111
+ keys: identityAttributeKeys({ openIdProvider, keys }),
112
+ })
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Bridges the two `@icp-sdk/auth` nonce contracts.
118
+ *
119
+ * v8 takes `() => Promise<Uint8Array>` (it journals the value so a redirect
120
+ * flow replays the same bytes); v7 takes `Uint8Array | Promise<Uint8Array>`.
121
+ * Both open the transport channel synchronously for the deferred forms, so we
122
+ * always hand over the deferred shape the installed client understands.
123
+ * `memoize` only exists from v8 onwards, which makes it a reliable marker.
124
+ */
125
+ function toAuthClientNonce(
126
+ authClient: AuthClientLike,
127
+ nonce: IdentityAttributeNonce
128
+ ): Uint8Array | Promise<Uint8Array> | (() => Promise<Uint8Array>) {
129
+ const supportsNonceThunk = typeof authClient.memoize === "function"
130
+
131
+ if (supportsNonceThunk) {
132
+ return () => Promise.resolve(typeof nonce === "function" ? nonce() : nonce)
133
+ }
134
+
135
+ return Promise.resolve(typeof nonce === "function" ? nonce() : nonce)
136
+ }
@@ -0,0 +1,270 @@
1
+ import { IDL } from "@icp-sdk/core/candid"
2
+
3
+ import type {
4
+ IdentityAttributeOpenIdProvider,
5
+ IdentityAttributeValues,
6
+ SignedIdentityAttributes,
7
+ } from "./types.js"
8
+ import { IC_INTERNET_IDENTITY_PROVIDER } from "./constants.js"
9
+
10
+ export const IDENTITY_ATTRIBUTES_PROVIDER = IC_INTERNET_IDENTITY_PROVIDER
11
+
12
+ /**
13
+ * @deprecated Use the default ClientManager identity provider or
14
+ * `IDENTITY_ATTRIBUTES_PROVIDER` instead.
15
+ */
16
+ export const IDENTITY_ATTRIBUTES_BETA_PROVIDER = IDENTITY_ATTRIBUTES_PROVIDER
17
+
18
+ const OPEN_ID_PROVIDER_URLS = {
19
+ apple: "https://appleid.apple.com",
20
+ google: "https://accounts.google.com",
21
+ microsoft: "https://login.microsoftonline.com/{tid}/v2.0",
22
+ } as const
23
+
24
+ export function identityAttributeKeys({
25
+ openIdProvider,
26
+ keys,
27
+ }: {
28
+ openIdProvider: IdentityAttributeOpenIdProvider
29
+ keys: string[]
30
+ }): string[] {
31
+ const provider = normalizeOpenIdProvider(openIdProvider)
32
+ return keys.map((key) => `openid:${provider}:${key}`)
33
+ }
34
+
35
+ export function normalizeOpenIdProvider(
36
+ openIdProvider: IdentityAttributeOpenIdProvider
37
+ ): string {
38
+ return (
39
+ OPEN_ID_PROVIDER_URLS[
40
+ openIdProvider as keyof typeof OPEN_ID_PROVIDER_URLS
41
+ ] ?? openIdProvider
42
+ )
43
+ }
44
+
45
+ export async function resolveIdentityAttributeKeys({
46
+ openIdProvider,
47
+ keys,
48
+ }: {
49
+ openIdProvider: IdentityAttributeOpenIdProvider
50
+ keys: string[]
51
+ }): Promise<string[]> {
52
+ return identityAttributeKeys({ openIdProvider, keys })
53
+ }
54
+
55
+ export function decodeIdentityAttributeValues(
56
+ data: Uint8Array,
57
+ requestedKeys: string[]
58
+ ): IdentityAttributeValues {
59
+ const requestedKeyMap = requestedKeys.reduce<Record<string, string>>(
60
+ (acc, key) => {
61
+ acc[key] = identityAttributeDisplayKey(key)
62
+ acc[identityAttributeDisplayKey(key)] = identityAttributeDisplayKey(key)
63
+ return acc
64
+ },
65
+ {}
66
+ )
67
+
68
+ const decodedValues = decodeCandidAttributeValues(data, requestedKeyMap)
69
+ if (Object.keys(decodedValues).length > 0) {
70
+ return decodedValues
71
+ }
72
+
73
+ return extractPrintableAttributeValues(data, requestedKeys)
74
+ }
75
+
76
+ function decodeCandidAttributeValues(
77
+ data: Uint8Array,
78
+ requestedKeyMap: Record<string, string>
79
+ ): IdentityAttributeValues {
80
+ const textPairs = IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text))
81
+ const recordPairs = IDL.Vec(
82
+ IDL.Record({
83
+ key: IDL.Text,
84
+ value: IDL.Text,
85
+ })
86
+ )
87
+
88
+ const candidates = [
89
+ [textPairs],
90
+ [recordPairs],
91
+ [IDL.Record({ attributes: textPairs })],
92
+ [IDL.Record({ values: textPairs })],
93
+ [IDL.Record({ attributes: recordPairs })],
94
+ [IDL.Record({ values: recordPairs })],
95
+ ]
96
+
97
+ for (const candidate of candidates) {
98
+ try {
99
+ const decoded = IDL.decode(candidate, data)
100
+ const values = collectDecodedAttributeValues(decoded, requestedKeyMap)
101
+ if (Object.keys(values).length > 0) {
102
+ return values
103
+ }
104
+ } catch {
105
+ // Try the next known beta payload shape, then fall back to text extraction.
106
+ }
107
+ }
108
+
109
+ return {}
110
+ }
111
+
112
+ function collectDecodedAttributeValues(
113
+ value: unknown,
114
+ requestedKeyMap: Record<string, string>,
115
+ activeKey?: string
116
+ ): IdentityAttributeValues {
117
+ const values: IdentityAttributeValues = {}
118
+
119
+ if (typeof value === "string") {
120
+ const displayKey = requestedKeyMap[value]
121
+ if (displayKey) {
122
+ return values
123
+ }
124
+ if (activeKey && isPrintableTextValue(value)) {
125
+ values[activeKey] = value
126
+ }
127
+ return values
128
+ }
129
+
130
+ if (Array.isArray(value)) {
131
+ if (value.length === 2 && typeof value[0] === "string") {
132
+ const displayKey = requestedKeyMap[value[0]]
133
+ if (
134
+ displayKey &&
135
+ typeof value[1] === "string" &&
136
+ isPrintableTextValue(value[1])
137
+ ) {
138
+ values[displayKey] = value[1]
139
+ return values
140
+ }
141
+ }
142
+
143
+ for (const item of value) {
144
+ Object.assign(
145
+ values,
146
+ collectDecodedAttributeValues(item, requestedKeyMap, activeKey)
147
+ )
148
+ }
149
+ return values
150
+ }
151
+
152
+ if (value && typeof value === "object") {
153
+ const record = value as Record<string, unknown>
154
+ if (typeof record.key === "string" && typeof record.value === "string") {
155
+ const displayKey = requestedKeyMap[record.key]
156
+ if (displayKey && isPrintableTextValue(record.value)) {
157
+ values[displayKey] = record.value
158
+ return values
159
+ }
160
+ }
161
+
162
+ for (const [key, nested] of Object.entries(record)) {
163
+ const displayKey = requestedKeyMap[key] ?? activeKey
164
+ if (
165
+ displayKey &&
166
+ typeof nested === "string" &&
167
+ isPrintableTextValue(nested)
168
+ ) {
169
+ values[displayKey] = nested
170
+ continue
171
+ }
172
+ Object.assign(
173
+ values,
174
+ collectDecodedAttributeValues(nested, requestedKeyMap, displayKey)
175
+ )
176
+ }
177
+ }
178
+
179
+ return values
180
+ }
181
+
182
+ function extractPrintableAttributeValues(
183
+ data: Uint8Array,
184
+ requestedKeys: string[]
185
+ ): IdentityAttributeValues {
186
+ const text = new TextDecoder().decode(data)
187
+ const values: IdentityAttributeValues = {}
188
+
189
+ for (const requestedKey of requestedKeys) {
190
+ const start = text.indexOf(requestedKey)
191
+ if (start === -1) {
192
+ continue
193
+ }
194
+
195
+ const tail = text.slice(start + requestedKey.length)
196
+ const printableRuns = tail.match(/[\x20-\x7E]{2,512}/g) ?? []
197
+ const value = printableRuns
198
+ .map((candidate) =>
199
+ cleanPrintableAttributeValue(candidate, requestedKeys)
200
+ )
201
+ .find(
202
+ (candidate) =>
203
+ isPrintableTextValue(candidate) &&
204
+ candidate !== requestedKey &&
205
+ !candidate.startsWith("openid:")
206
+ )
207
+
208
+ if (value) {
209
+ values[identityAttributeDisplayKey(requestedKey)] = value.trim()
210
+ }
211
+ }
212
+
213
+ return values
214
+ }
215
+
216
+ function cleanPrintableAttributeValue(
217
+ value: string,
218
+ requestedKeys: string[]
219
+ ): string {
220
+ let cleaned = value
221
+
222
+ for (const requestedKey of requestedKeys) {
223
+ const nextKeyIndex = cleaned.indexOf(requestedKey)
224
+ if (nextKeyIndex > 0) {
225
+ cleaned = cleaned.slice(
226
+ 0,
227
+ trimCandidTextLengthPrefix(cleaned, nextKeyIndex)
228
+ )
229
+ }
230
+ }
231
+
232
+ const nextOpenIdKeyIndex = cleaned.indexOf("openid:")
233
+ if (nextOpenIdKeyIndex > 0) {
234
+ cleaned = cleaned.slice(
235
+ 0,
236
+ trimCandidTextLengthPrefix(cleaned, nextOpenIdKeyIndex)
237
+ )
238
+ }
239
+
240
+ return cleaned.trim()
241
+ }
242
+
243
+ function trimCandidTextLengthPrefix(value: string, index: number): number {
244
+ let trimmedIndex = index
245
+ while (trimmedIndex > 0 && isAsciiDigit(value.charCodeAt(trimmedIndex - 1))) {
246
+ trimmedIndex -= 1
247
+ }
248
+ return trimmedIndex
249
+ }
250
+
251
+ function isAsciiDigit(charCode: number): boolean {
252
+ return charCode >= 48 && charCode <= 57
253
+ }
254
+
255
+ function identityAttributeDisplayKey(key: string): string {
256
+ return key.split(":").pop() || key
257
+ }
258
+
259
+ function isPrintableTextValue(value: string): boolean {
260
+ return value.length > 0 && value.length <= 512 && /^[\x20-\x7E]+$/.test(value)
261
+ }
262
+
263
+ export function normalizeSignedIdentityAttributes(
264
+ attributes: SignedIdentityAttributes
265
+ ): SignedIdentityAttributes {
266
+ return {
267
+ data: new Uint8Array(attributes.data),
268
+ signature: new Uint8Array(attributes.signature),
269
+ }
270
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./authentication-manager.js"
2
+ export * from "./identity-attributes-manager.js"
3
+ export * from "./identity-attributes.js"
4
+ export * from "./constants.js"
5
+ export * from "./types.js"
6
+ export * from "../hooks/createAuthHooks.js"
7
+ export * from "./createIdentityAttributeHooks.js"