@ic-reactor/react 3.13.0 → 4.0.0-beta.1

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 (174) hide show
  1. package/README.md +234 -787
  2. package/dist/index.d.ts +259 -16
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +470 -23
  5. package/dist/index.js.map +1 -1
  6. package/llms.txt +80 -278
  7. package/package.json +11 -39
  8. package/src/index.tsx +611 -0
  9. package/dist/auth/auth-client-compat.d.ts +0 -122
  10. package/dist/auth/auth-client-compat.d.ts.map +0 -1
  11. package/dist/auth/auth-client-compat.js +0 -162
  12. package/dist/auth/auth-client-compat.js.map +0 -1
  13. package/dist/auth/authentication-manager.d.ts +0 -405
  14. package/dist/auth/authentication-manager.d.ts.map +0 -1
  15. package/dist/auth/authentication-manager.js +0 -1537
  16. package/dist/auth/authentication-manager.js.map +0 -1
  17. package/dist/auth/constants.d.ts +0 -24
  18. package/dist/auth/constants.d.ts.map +0 -1
  19. package/dist/auth/constants.js +0 -24
  20. package/dist/auth/constants.js.map +0 -1
  21. package/dist/auth/createIdentityAttributeHooks.d.ts +0 -14
  22. package/dist/auth/createIdentityAttributeHooks.d.ts.map +0 -1
  23. package/dist/auth/createIdentityAttributeHooks.js +0 -122
  24. package/dist/auth/createIdentityAttributeHooks.js.map +0 -1
  25. package/dist/auth/identity-attributes-manager.d.ts +0 -27
  26. package/dist/auth/identity-attributes-manager.d.ts.map +0 -1
  27. package/dist/auth/identity-attributes-manager.js +0 -191
  28. package/dist/auth/identity-attributes-manager.js.map +0 -1
  29. package/dist/auth/identity-attributes.d.ts +0 -19
  30. package/dist/auth/identity-attributes.d.ts.map +0 -1
  31. package/dist/auth/identity-attributes.js +0 -227
  32. package/dist/auth/identity-attributes.js.map +0 -1
  33. package/dist/auth/index.d.ts +0 -8
  34. package/dist/auth/index.d.ts.map +0 -1
  35. package/dist/auth/index.js +0 -8
  36. package/dist/auth/index.js.map +0 -1
  37. package/dist/auth/local-ii-probe.d.ts +0 -57
  38. package/dist/auth/local-ii-probe.d.ts.map +0 -1
  39. package/dist/auth/local-ii-probe.js +0 -121
  40. package/dist/auth/local-ii-probe.js.map +0 -1
  41. package/dist/auth/types.d.ts +0 -222
  42. package/dist/auth/types.d.ts.map +0 -1
  43. package/dist/auth/types.js +0 -2
  44. package/dist/auth/types.js.map +0 -1
  45. package/dist/createActorHooks.d.ts +0 -41
  46. package/dist/createActorHooks.d.ts.map +0 -1
  47. package/dist/createActorHooks.js +0 -17
  48. package/dist/createActorHooks.js.map +0 -1
  49. package/dist/createInfiniteQuery.d.ts +0 -185
  50. package/dist/createInfiniteQuery.d.ts.map +0 -1
  51. package/dist/createInfiniteQuery.js +0 -198
  52. package/dist/createInfiniteQuery.js.map +0 -1
  53. package/dist/createMutation.d.ts +0 -33
  54. package/dist/createMutation.d.ts.map +0 -1
  55. package/dist/createMutation.js +0 -199
  56. package/dist/createMutation.js.map +0 -1
  57. package/dist/createQuery.d.ts +0 -63
  58. package/dist/createQuery.d.ts.map +0 -1
  59. package/dist/createQuery.js +0 -204
  60. package/dist/createQuery.js.map +0 -1
  61. package/dist/createReactorProvider.d.ts +0 -158
  62. package/dist/createReactorProvider.d.ts.map +0 -1
  63. package/dist/createReactorProvider.js +0 -256
  64. package/dist/createReactorProvider.js.map +0 -1
  65. package/dist/createSuspenseInfiniteQuery.d.ts +0 -154
  66. package/dist/createSuspenseInfiniteQuery.d.ts.map +0 -1
  67. package/dist/createSuspenseInfiniteQuery.js +0 -209
  68. package/dist/createSuspenseInfiniteQuery.js.map +0 -1
  69. package/dist/createSuspenseQuery.d.ts +0 -46
  70. package/dist/createSuspenseQuery.d.ts.map +0 -1
  71. package/dist/createSuspenseQuery.js +0 -158
  72. package/dist/createSuspenseQuery.js.map +0 -1
  73. package/dist/defineDisplayReactor.d.ts +0 -43
  74. package/dist/defineDisplayReactor.d.ts.map +0 -1
  75. package/dist/defineDisplayReactor.js +0 -42
  76. package/dist/defineDisplayReactor.js.map +0 -1
  77. package/dist/defineReactor.d.ts +0 -99
  78. package/dist/defineReactor.d.ts.map +0 -1
  79. package/dist/defineReactor.js +0 -15
  80. package/dist/defineReactor.js.map +0 -1
  81. package/dist/defineReactorShared.d.ts +0 -84
  82. package/dist/defineReactorShared.d.ts.map +0 -1
  83. package/dist/defineReactorShared.js +0 -139
  84. package/dist/defineReactorShared.js.map +0 -1
  85. package/dist/hooks/createAuthHooks.d.ts +0 -50
  86. package/dist/hooks/createAuthHooks.d.ts.map +0 -1
  87. package/dist/hooks/createAuthHooks.js +0 -291
  88. package/dist/hooks/createAuthHooks.js.map +0 -1
  89. package/dist/hooks/index.d.ts +0 -21
  90. package/dist/hooks/index.d.ts.map +0 -1
  91. package/dist/hooks/index.js +0 -24
  92. package/dist/hooks/index.js.map +0 -1
  93. package/dist/hooks/useActorInfiniteQuery.d.ts +0 -67
  94. package/dist/hooks/useActorInfiniteQuery.d.ts.map +0 -1
  95. package/dist/hooks/useActorInfiniteQuery.js +0 -89
  96. package/dist/hooks/useActorInfiniteQuery.js.map +0 -1
  97. package/dist/hooks/useActorMethod.d.ts +0 -148
  98. package/dist/hooks/useActorMethod.d.ts.map +0 -1
  99. package/dist/hooks/useActorMethod.js +0 -394
  100. package/dist/hooks/useActorMethod.js.map +0 -1
  101. package/dist/hooks/useActorMutation.d.ts +0 -51
  102. package/dist/hooks/useActorMutation.d.ts.map +0 -1
  103. package/dist/hooks/useActorMutation.js +0 -70
  104. package/dist/hooks/useActorMutation.js.map +0 -1
  105. package/dist/hooks/useActorQuery.d.ts +0 -45
  106. package/dist/hooks/useActorQuery.d.ts.map +0 -1
  107. package/dist/hooks/useActorQuery.js +0 -67
  108. package/dist/hooks/useActorQuery.js.map +0 -1
  109. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +0 -51
  110. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +0 -1
  111. package/dist/hooks/useActorSuspenseInfiniteQuery.js +0 -76
  112. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +0 -1
  113. package/dist/hooks/useActorSuspenseQuery.d.ts +0 -32
  114. package/dist/hooks/useActorSuspenseQuery.d.ts.map +0 -1
  115. package/dist/hooks/useActorSuspenseQuery.js +0 -58
  116. package/dist/hooks/useActorSuspenseQuery.js.map +0 -1
  117. package/dist/ownedAuthentication.d.ts +0 -52
  118. package/dist/ownedAuthentication.d.ts.map +0 -1
  119. package/dist/ownedAuthentication.js +0 -49
  120. package/dist/ownedAuthentication.js.map +0 -1
  121. package/dist/server.d.ts +0 -21
  122. package/dist/server.d.ts.map +0 -1
  123. package/dist/server.js +0 -23
  124. package/dist/server.js.map +0 -1
  125. package/dist/testing.d.ts +0 -19
  126. package/dist/testing.d.ts.map +0 -1
  127. package/dist/testing.js +0 -19
  128. package/dist/testing.js.map +0 -1
  129. package/dist/types.d.ts +0 -671
  130. package/dist/types.d.ts.map +0 -1
  131. package/dist/types.js +0 -5
  132. package/dist/types.js.map +0 -1
  133. package/dist/utils.d.ts +0 -207
  134. package/dist/utils.d.ts.map +0 -1
  135. package/dist/utils.js +0 -405
  136. package/dist/utils.js.map +0 -1
  137. package/dist/validation.d.ts +0 -136
  138. package/dist/validation.d.ts.map +0 -1
  139. package/dist/validation.js +0 -144
  140. package/dist/validation.js.map +0 -1
  141. package/src/auth/auth-client-compat.ts +0 -273
  142. package/src/auth/authentication-manager.ts +0 -1682
  143. package/src/auth/constants.ts +0 -32
  144. package/src/auth/createIdentityAttributeHooks.ts +0 -169
  145. package/src/auth/identity-attributes-manager.ts +0 -226
  146. package/src/auth/identity-attributes.ts +0 -345
  147. package/src/auth/index.ts +0 -7
  148. package/src/auth/local-ii-probe.ts +0 -173
  149. package/src/auth/types.ts +0 -243
  150. package/src/createActorHooks.ts +0 -208
  151. package/src/createInfiniteQuery.ts +0 -670
  152. package/src/createMutation.ts +0 -324
  153. package/src/createQuery.ts +0 -369
  154. package/src/createReactorProvider.ts +0 -365
  155. package/src/createSuspenseInfiniteQuery.ts +0 -651
  156. package/src/createSuspenseQuery.ts +0 -304
  157. package/src/defineDisplayReactor.ts +0 -62
  158. package/src/defineReactor.ts +0 -142
  159. package/src/defineReactorShared.ts +0 -268
  160. package/src/hooks/createAuthHooks.ts +0 -371
  161. package/src/hooks/index.ts +0 -103
  162. package/src/hooks/useActorInfiniteQuery.ts +0 -278
  163. package/src/hooks/useActorMethod.ts +0 -710
  164. package/src/hooks/useActorMutation.ts +0 -205
  165. package/src/hooks/useActorQuery.ts +0 -157
  166. package/src/hooks/useActorSuspenseInfiniteQuery.ts +0 -248
  167. package/src/hooks/useActorSuspenseQuery.ts +0 -147
  168. package/src/index.ts +0 -31
  169. package/src/ownedAuthentication.ts +0 -81
  170. package/src/server.ts +0 -23
  171. package/src/testing.ts +0 -18
  172. package/src/types.ts +0 -948
  173. package/src/utils.ts +0 -505
  174. package/src/validation.ts +0 -226
@@ -1,345 +0,0 @@
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
- // Internet Identity's own format, read by its type. Once a message is known
69
- // to be one, a key it does not hold is absent: the text fallback below
70
- // cannot add anything true, only a neighbouring value under the wrong name.
71
- const icrc3Entries = decodeIcrc3AttributeMap(data)
72
- if (icrc3Entries) {
73
- return collectIcrc3TextValues(icrc3Entries, requestedKeyMap)
74
- }
75
-
76
- const decodedValues = decodeCandidAttributeValues(data, requestedKeyMap)
77
- if (Object.keys(decodedValues).length > 0) {
78
- return decodedValues
79
- }
80
-
81
- return extractPrintableAttributeValues(data, requestedKeys)
82
- }
83
-
84
- /** The ICRC-3 `Value` an Internet Identity attribute message is encoded as. */
85
- type Icrc3Value =
86
- | { Nat: bigint }
87
- | { Int: bigint }
88
- | { Blob: Uint8Array | number[] }
89
- | { Text: string }
90
- | { Array: Icrc3Value[] }
91
- | { Map: Array<[string, Icrc3Value]> }
92
-
93
- /**
94
- * The entries of an Internet Identity attribute message, or `undefined` when
95
- * `data` is not one.
96
- *
97
- * The canister certifies `Encode!(&Icrc3Value::Map(..))`: the requested
98
- * attributes under the keys they were requested by (bare, such as `email`, for
99
- * an unscoped request), plus `implicit:nonce`, `implicit:origin` and
100
- * `implicit:issued_at_timestamp_ns`. None of the shapes
101
- * {@link decodeCandidAttributeValues} tries match it, so without this every
102
- * real response was read by scraping printable text, which returned the
103
- * neighbouring length byte as part of a value, cut names at the first
104
- * non-ASCII character, and matched keys inside other values.
105
- *
106
- * @see https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md
107
- */
108
- function decodeIcrc3AttributeMap(
109
- data: Uint8Array
110
- ): Array<[string, Icrc3Value]> | undefined {
111
- const value = IDL.Rec()
112
- value.fill(
113
- IDL.Variant({
114
- Nat: IDL.Nat,
115
- Int: IDL.Int,
116
- Blob: IDL.Vec(IDL.Nat8),
117
- Text: IDL.Text,
118
- Array: IDL.Vec(value),
119
- Map: IDL.Vec(IDL.Tuple(IDL.Text, value)),
120
- })
121
- )
122
-
123
- try {
124
- const [decoded] = IDL.decode([value], data) as [Icrc3Value]
125
- return "Map" in decoded ? decoded.Map : undefined
126
- } catch {
127
- return undefined
128
- }
129
- }
130
-
131
- /**
132
- * The requested attributes in an ICRC-3 map, as certified.
133
- *
134
- * Only `Text` entries are attribute values; the `implicit:` entries are there
135
- * for the verifier and are returned only if asked for by name.
136
- */
137
- function collectIcrc3TextValues(
138
- entries: Array<[string, Icrc3Value]>,
139
- requestedKeyMap: Record<string, string>
140
- ): IdentityAttributeValues {
141
- const values: IdentityAttributeValues = {}
142
- for (const [key, value] of entries) {
143
- if (!Object.prototype.hasOwnProperty.call(requestedKeyMap, key)) continue
144
- if ("Text" in value) {
145
- values[requestedKeyMap[key]] = value.Text
146
- }
147
- }
148
- return values
149
- }
150
-
151
- function decodeCandidAttributeValues(
152
- data: Uint8Array,
153
- requestedKeyMap: Record<string, string>
154
- ): IdentityAttributeValues {
155
- const textPairs = IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text))
156
- const recordPairs = IDL.Vec(
157
- IDL.Record({
158
- key: IDL.Text,
159
- value: IDL.Text,
160
- })
161
- )
162
-
163
- const candidates = [
164
- [textPairs],
165
- [recordPairs],
166
- [IDL.Record({ attributes: textPairs })],
167
- [IDL.Record({ values: textPairs })],
168
- [IDL.Record({ attributes: recordPairs })],
169
- [IDL.Record({ values: recordPairs })],
170
- ]
171
-
172
- for (const candidate of candidates) {
173
- try {
174
- const decoded = IDL.decode(candidate, data)
175
- const values = collectDecodedAttributeValues(decoded, requestedKeyMap)
176
- if (Object.keys(values).length > 0) {
177
- return values
178
- }
179
- } catch {
180
- // Try the next known beta payload shape, then fall back to text extraction.
181
- }
182
- }
183
-
184
- return {}
185
- }
186
-
187
- function collectDecodedAttributeValues(
188
- value: unknown,
189
- requestedKeyMap: Record<string, string>,
190
- activeKey?: string
191
- ): IdentityAttributeValues {
192
- const values: IdentityAttributeValues = {}
193
-
194
- if (typeof value === "string") {
195
- const displayKey = requestedKeyMap[value]
196
- if (displayKey) {
197
- return values
198
- }
199
- if (activeKey && isPrintableTextValue(value)) {
200
- values[activeKey] = value
201
- }
202
- return values
203
- }
204
-
205
- if (Array.isArray(value)) {
206
- if (value.length === 2 && typeof value[0] === "string") {
207
- const displayKey = requestedKeyMap[value[0]]
208
- if (
209
- displayKey &&
210
- typeof value[1] === "string" &&
211
- isPrintableTextValue(value[1])
212
- ) {
213
- values[displayKey] = value[1]
214
- return values
215
- }
216
- }
217
-
218
- for (const item of value) {
219
- Object.assign(
220
- values,
221
- collectDecodedAttributeValues(item, requestedKeyMap, activeKey)
222
- )
223
- }
224
- return values
225
- }
226
-
227
- if (value && typeof value === "object") {
228
- const record = value as Record<string, unknown>
229
- if (typeof record.key === "string" && typeof record.value === "string") {
230
- const displayKey = requestedKeyMap[record.key]
231
- if (displayKey && isPrintableTextValue(record.value)) {
232
- values[displayKey] = record.value
233
- return values
234
- }
235
- }
236
-
237
- for (const [key, nested] of Object.entries(record)) {
238
- const displayKey = requestedKeyMap[key] ?? activeKey
239
- if (
240
- displayKey &&
241
- typeof nested === "string" &&
242
- isPrintableTextValue(nested)
243
- ) {
244
- values[displayKey] = nested
245
- continue
246
- }
247
- Object.assign(
248
- values,
249
- collectDecodedAttributeValues(nested, requestedKeyMap, displayKey)
250
- )
251
- }
252
- }
253
-
254
- return values
255
- }
256
-
257
- function extractPrintableAttributeValues(
258
- data: Uint8Array,
259
- requestedKeys: string[]
260
- ): IdentityAttributeValues {
261
- const text = new TextDecoder().decode(data)
262
- const values: IdentityAttributeValues = {}
263
-
264
- for (const requestedKey of requestedKeys) {
265
- const start = text.indexOf(requestedKey)
266
- if (start === -1) {
267
- continue
268
- }
269
-
270
- const tail = text.slice(start + requestedKey.length)
271
- const printableRuns = tail.match(/[\x20-\x7E]{2,512}/g) ?? []
272
- const value = printableRuns
273
- .map((candidate) =>
274
- cleanPrintableAttributeValue(candidate, requestedKeys)
275
- )
276
- .find(
277
- (candidate) =>
278
- isPrintableTextValue(candidate) &&
279
- candidate !== requestedKey &&
280
- !candidate.startsWith("openid:")
281
- )
282
-
283
- if (value) {
284
- values[identityAttributeDisplayKey(requestedKey)] = value.trim()
285
- }
286
- }
287
-
288
- return values
289
- }
290
-
291
- function cleanPrintableAttributeValue(
292
- value: string,
293
- requestedKeys: string[]
294
- ): string {
295
- let cleaned = value
296
-
297
- for (const requestedKey of requestedKeys) {
298
- const nextKeyIndex = cleaned.indexOf(requestedKey)
299
- if (nextKeyIndex > 0) {
300
- cleaned = cleaned.slice(
301
- 0,
302
- trimCandidTextLengthPrefix(cleaned, nextKeyIndex)
303
- )
304
- }
305
- }
306
-
307
- const nextOpenIdKeyIndex = cleaned.indexOf("openid:")
308
- if (nextOpenIdKeyIndex > 0) {
309
- cleaned = cleaned.slice(
310
- 0,
311
- trimCandidTextLengthPrefix(cleaned, nextOpenIdKeyIndex)
312
- )
313
- }
314
-
315
- return cleaned.trim()
316
- }
317
-
318
- function trimCandidTextLengthPrefix(value: string, index: number): number {
319
- let trimmedIndex = index
320
- while (trimmedIndex > 0 && isAsciiDigit(value.charCodeAt(trimmedIndex - 1))) {
321
- trimmedIndex -= 1
322
- }
323
- return trimmedIndex
324
- }
325
-
326
- function isAsciiDigit(charCode: number): boolean {
327
- return charCode >= 48 && charCode <= 57
328
- }
329
-
330
- function identityAttributeDisplayKey(key: string): string {
331
- return key.split(":").pop() || key
332
- }
333
-
334
- function isPrintableTextValue(value: string): boolean {
335
- return value.length > 0 && value.length <= 512 && /^[\x20-\x7E]+$/.test(value)
336
- }
337
-
338
- export function normalizeSignedIdentityAttributes(
339
- attributes: SignedIdentityAttributes
340
- ): SignedIdentityAttributes {
341
- return {
342
- data: new Uint8Array(attributes.data),
343
- signature: new Uint8Array(attributes.signature),
344
- }
345
- }
package/src/auth/index.ts DELETED
@@ -1,7 +0,0 @@
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"
@@ -1,173 +0,0 @@
1
- /**
2
- * Ask a locally deployed Internet Identity canister what it actually serves.
3
- *
4
- * `localInternetIdentityProvider` builds `http://<id>.localhost:<port>/authorize`,
5
- * the id.ai-era path. Whether that works depends on which Internet Identity
6
- * build is installed, and the window is narrow:
7
- *
8
- * - up to `release-2025-03-07`, the SPA lived at `/` and used a `#authorize`
9
- * fragment; `/authorize` is not an asset
10
- * - `release-2026-01-05` through `release-2026-03-16` serve `/authorize`
11
- * - from `release-2026-03-23` the frontend left the canister entirely (the id.ai
12
- * split), so `/authorize` 404s even on `.raw`
13
- *
14
- * Outside that window the login popup renders the gateway's
15
- * "Response Verification Error" page and the app simply waits until the user
16
- * closes it. Nothing points at the cause.
17
- *
18
- * Asking the canister over the agent avoids the gateway, so the answer is about
19
- * the canister's assets rather than about how the gateway felt about them.
20
- */
21
-
22
- import { Actor, type HttpAgent } from "@icp-sdk/core/agent"
23
- import { IDL } from "@icp-sdk/core/candid"
24
- import type { AuthClientFlavor } from "./auth-client-compat.js"
25
-
26
- /** The `http_request` query every asset-serving canister exposes. */
27
- const httpInterface: IDL.InterfaceFactory = ({ IDL }) => {
28
- const HeaderField = IDL.Tuple(IDL.Text, IDL.Text)
29
- return IDL.Service({
30
- http_request: IDL.Func(
31
- [
32
- IDL.Record({
33
- method: IDL.Text,
34
- url: IDL.Text,
35
- headers: IDL.Vec(HeaderField),
36
- body: IDL.Vec(IDL.Nat8),
37
- }),
38
- ],
39
- [
40
- IDL.Record({
41
- status_code: IDL.Nat16,
42
- headers: IDL.Vec(HeaderField),
43
- body: IDL.Vec(IDL.Nat8),
44
- }),
45
- ],
46
- ["query"]
47
- ),
48
- })
49
- }
50
-
51
- interface HttpCapableActor {
52
- http_request: (request: {
53
- method: string
54
- url: string
55
- headers: [string, string][]
56
- body: Uint8Array
57
- }) => Promise<{ status_code: number }>
58
- }
59
-
60
- /** Where a given Internet Identity build expects the sign-in flow to start. */
61
- export type AuthorizePath = "/authorize" | "/#authorize"
62
-
63
- export interface LocalIiProbe {
64
- /** The path that works, or `null` when the canister serves no sign-in UI. */
65
- path: AuthorizePath | null
66
- /**
67
- * The probe could not reach a conclusion — the canister was unreachable, or
68
- * it does not expose `http_request` at all. Callers keep their existing
69
- * behaviour rather than blocking a login that might well work.
70
- */
71
- inconclusive: boolean
72
- }
73
-
74
- async function statusOf(
75
- actor: HttpCapableActor,
76
- url: string
77
- ): Promise<number | undefined> {
78
- try {
79
- const response = await actor.http_request({
80
- method: "GET",
81
- url,
82
- headers: [],
83
- body: new Uint8Array(),
84
- })
85
- return response.status_code
86
- } catch {
87
- return undefined
88
- }
89
- }
90
-
91
- /**
92
- * Determine which authorize path a locally deployed Internet Identity serves.
93
- *
94
- * Never throws. A probe that cannot answer returns `inconclusive`, because a
95
- * diagnostic that blocks a working setup is worse than the failure it explains.
96
- */
97
- export async function probeLocalInternetIdentity(
98
- agent: HttpAgent,
99
- canisterId: string
100
- ): Promise<LocalIiProbe> {
101
- let actor: HttpCapableActor
102
- try {
103
- actor = Actor.createActor<HttpCapableActor>(httpInterface, {
104
- agent,
105
- canisterId,
106
- })
107
- } catch {
108
- return { path: "/authorize", inconclusive: true }
109
- }
110
-
111
- const authorize = await statusOf(actor, "/authorize")
112
-
113
- // The canister does not answer http_request at all — not an asset canister,
114
- // not deployed, or the replica is down. Not our call to make.
115
- if (authorize === undefined) {
116
- return { path: "/authorize", inconclusive: true }
117
- }
118
-
119
- if (authorize >= 200 && authorize < 300) {
120
- return { path: "/authorize", inconclusive: false }
121
- }
122
-
123
- // Pre-2026 builds serve the SPA at the root and drive the flow from a
124
- // `#authorize` fragment, which is a path the gateway never sees.
125
- const root = await statusOf(actor, "/")
126
- if (root !== undefined && root >= 200 && root < 300) {
127
- return { path: "/#authorize", inconclusive: false }
128
- }
129
-
130
- return { path: null, inconclusive: false }
131
- }
132
-
133
- /**
134
- * The error a login gets when the installed build serves no sign-in UI, in
135
- * place of a 503 page inside a popup the app cannot see.
136
- *
137
- * The fix depends on the installed `@icp-sdk/auth` major. v8 signs in through
138
- * the page the canister serves, so an older build that still serves one works.
139
- * v10 signs in through `ii_session_delegation` and mints with
140
- * `app_prepare_delegation`, which Internet Identity gained only after its
141
- * frontend left the canister (#561). No build serves both, so a v10 app needs
142
- * the frontend from somewhere else, and older builds cannot help it.
143
- *
144
- * @param flavor - The installed client's contract. Defaults to `legacy` (v8),
145
- * whose advice this error gave before it knew about v10.
146
- */
147
- export function localInternetIdentityUnavailableError(
148
- canisterId: string,
149
- flavor: AuthClientFlavor = "legacy"
150
- ): Error {
151
- const problem =
152
- `[ic-reactor] The Internet Identity canister ${canisterId} does not serve a sign-in UI: ` +
153
- `neither /authorize nor / returned a page. `
154
-
155
- if (flavor === "session") {
156
- return new Error(
157
- problem +
158
- `With @icp-sdk/auth v10, local sign-in needs an Internet Identity frontend served ` +
159
- `separately, because no Internet Identity canister build both serves /authorize and ` +
160
- `supports v10 sessions. Set \`identityProvider\` to that frontend's authorize URL and ` +
161
- `\`internetIdentityId\` to the local Internet Identity canister that mints its delegations.`
162
- )
163
- }
164
-
165
- return new Error(
166
- problem +
167
- `Internet Identity builds from ` +
168
- `release-2026-03-23 onward moved their frontend out of the canister (the id.ai split) ` +
169
- `and cannot be used locally. Install internet_identity_dev.wasm from release-2026-03-16, ` +
170
- `the newest build that still self-serves /authorize, or point \`identityProvider\` at a ` +
171
- `provider you host yourself.`
172
- )
173
- }