@block-auth.io/angular 0.1.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 (148) hide show
  1. package/.turbo/turbo-build.log +37 -0
  2. package/.turbo/turbo-lint.log +4 -0
  3. package/.turbo/turbo-test.log +82 -0
  4. package/.turbo/turbo-typecheck.log +4 -0
  5. package/CHANGELOG.md +10 -0
  6. package/ng-package.json +6 -0
  7. package/package.json +50 -0
  8. package/scripts/css-to-ts.mjs +14 -0
  9. package/src/lib/__tests__/integration.test.ts +45 -0
  10. package/src/lib/block-auth.service.test.ts +69 -0
  11. package/src/lib/block-auth.service.ts +81 -0
  12. package/src/lib/components/btns/btn-base-provider.component.test.ts +22 -0
  13. package/src/lib/components/btns/btn-base-provider.component.ts +55 -0
  14. package/src/lib/components/btns/btn-block-auth.component.test.ts +79 -0
  15. package/src/lib/components/btns/btn-block-auth.component.ts +104 -0
  16. package/src/lib/components/btns/btn-gmail.component.test.ts +16 -0
  17. package/src/lib/components/btns/btn-gmail.component.ts +41 -0
  18. package/src/lib/components/btns/btn-google-base.component.test.ts +16 -0
  19. package/src/lib/components/btns/btn-google-base.component.ts +73 -0
  20. package/src/lib/components/btns/btn-metamask.component.test.ts +20 -0
  21. package/src/lib/components/btns/btn-metamask.component.ts +98 -0
  22. package/src/lib/components/btns/btn-microsoft.component.test.ts +16 -0
  23. package/src/lib/components/btns/btn-microsoft.component.ts +77 -0
  24. package/src/lib/components/btns/btn-phantom.component.test.ts +20 -0
  25. package/src/lib/components/btns/btn-phantom.component.ts +93 -0
  26. package/src/lib/components/btns/btn-signin.component.test.ts +55 -0
  27. package/src/lib/components/btns/btn-signin.component.ts +108 -0
  28. package/src/lib/components/btns/btn-wallet-connect.component.test.ts +20 -0
  29. package/src/lib/components/btns/btn-wallet-connect.component.ts +109 -0
  30. package/src/lib/components/btns/index.ts +11 -0
  31. package/src/lib/components/contents/content-signin.component.test.ts +33 -0
  32. package/src/lib/components/contents/content-signin.component.ts +68 -0
  33. package/src/lib/components/contents/content-signup.component.test.ts +78 -0
  34. package/src/lib/components/contents/content-signup.component.ts +225 -0
  35. package/src/lib/components/dds/change-account.component.test.ts +66 -0
  36. package/src/lib/components/dds/change-account.component.ts +112 -0
  37. package/src/lib/components/dds/dropdown-profile-btn.component.test.ts +66 -0
  38. package/src/lib/components/dds/dropdown-profile-btn.component.ts +185 -0
  39. package/src/lib/components/dds/dropdown-profile-item.component.test.ts +75 -0
  40. package/src/lib/components/dds/dropdown-profile-item.component.ts +74 -0
  41. package/src/lib/components/dds/dropdown-profile.component.test.ts +98 -0
  42. package/src/lib/components/dds/dropdown-profile.component.ts +258 -0
  43. package/src/lib/components/flow-block-auth.component.test.ts +93 -0
  44. package/src/lib/components/flow-block-auth.component.ts +226 -0
  45. package/src/lib/components/modals/modal-overlay.component.test.ts +81 -0
  46. package/src/lib/components/modals/modal-overlay.component.ts +43 -0
  47. package/src/lib/components/modals/modal-restoration-expired.component.test.ts +71 -0
  48. package/src/lib/components/modals/modal-restoration-expired.component.ts +83 -0
  49. package/src/lib/components/modals/modal-restore.component.test.ts +74 -0
  50. package/src/lib/components/modals/modal-restore.component.ts +104 -0
  51. package/src/lib/components/modals/modal-signin.component.test.ts +66 -0
  52. package/src/lib/components/modals/modal-signin.component.ts +44 -0
  53. package/src/lib/components/modals/modal-signup.component.test.ts +56 -0
  54. package/src/lib/components/modals/modal-signup.component.ts +40 -0
  55. package/src/lib/components/sections/footer-section.component.test.ts +19 -0
  56. package/src/lib/components/sections/footer-section.component.ts +49 -0
  57. package/src/lib/components/sections/internals/providers-buttons.component.test.ts +27 -0
  58. package/src/lib/components/sections/internals/providers-buttons.component.ts +290 -0
  59. package/src/lib/components/sections/internals/providers-filters.component.test.ts +25 -0
  60. package/src/lib/components/sections/internals/providers-filters.component.ts +139 -0
  61. package/src/lib/components/sections/internals/selected-button.component.test.ts +36 -0
  62. package/src/lib/components/sections/internals/selected-button.component.ts +92 -0
  63. package/src/lib/components/sections/login-section.component.test.ts +49 -0
  64. package/src/lib/components/sections/login-section.component.ts +120 -0
  65. package/src/lib/components/sections/providers-section.component.test.ts +22 -0
  66. package/src/lib/components/sections/providers-section.component.ts +187 -0
  67. package/src/lib/components/txns/internals/permissions-required-modal.component.test.ts +14 -0
  68. package/src/lib/components/txns/internals/permissions-required-modal.component.ts +35 -0
  69. package/src/lib/components/txns/internals/solicited-data-modal.component.test.ts +72 -0
  70. package/src/lib/components/txns/internals/solicited-data-modal.component.ts +187 -0
  71. package/src/lib/components/txns/internals/txn-error.component.test.ts +100 -0
  72. package/src/lib/components/txns/internals/txn-error.component.ts +121 -0
  73. package/src/lib/components/txns/internals/txn-type.component.test.ts +52 -0
  74. package/src/lib/components/txns/internals/txn-type.component.ts +51 -0
  75. package/src/lib/components/txns/internals/txn.component.test.ts +188 -0
  76. package/src/lib/components/txns/internals/txn.component.ts +298 -0
  77. package/src/lib/components/txns/txns-overview.component.test.ts +107 -0
  78. package/src/lib/components/txns/txns-overview.component.ts +229 -0
  79. package/src/lib/icons/ai-outline-disconnect.component.ts +21 -0
  80. package/src/lib/icons/bi-logo-gmail.component.ts +21 -0
  81. package/src/lib/icons/cg-profile.component.ts +31 -0
  82. package/src/lib/icons/check-icon.component.ts +27 -0
  83. package/src/lib/icons/ci-clock2.component.ts +31 -0
  84. package/src/lib/icons/fa-arrow-right.component.ts +21 -0
  85. package/src/lib/icons/fa-times.component.ts +21 -0
  86. package/src/lib/icons/fa-undo.component.ts +21 -0
  87. package/src/lib/icons/icons.test.ts +25 -0
  88. package/src/lib/icons/index.ts +22 -0
  89. package/src/lib/icons/io-ios-arrow-back.component.ts +21 -0
  90. package/src/lib/icons/io-ios-arrow-forward.component.ts +21 -0
  91. package/src/lib/icons/io-md-arrow-back.component.ts +19 -0
  92. package/src/lib/icons/io-md-arrow-forward.component.ts +19 -0
  93. package/src/lib/icons/pi-warning-fill.component.ts +21 -0
  94. package/src/lib/icons/tb-arrows-exchange2.component.ts +30 -0
  95. package/src/lib/icons/tb-copy-check-filled.component.ts +21 -0
  96. package/src/lib/icons/tb-copy.component.ts +34 -0
  97. package/src/lib/icons/ti-vendor-microsoft.component.ts +21 -0
  98. package/src/lib/inject-store.test.ts +26 -0
  99. package/src/lib/inject-store.ts +16 -0
  100. package/src/lib/primitives/arrow-icon.component.test.ts +12 -0
  101. package/src/lib/primitives/arrow-icon.component.ts +38 -0
  102. package/src/lib/primitives/btn-arrow-dir.component.test.ts +22 -0
  103. package/src/lib/primitives/btn-arrow-dir.component.ts +65 -0
  104. package/src/lib/primitives/btn-base.component.test.ts +38 -0
  105. package/src/lib/primitives/btn-base.component.ts +140 -0
  106. package/src/lib/primitives/divider-base.component.test.ts +12 -0
  107. package/src/lib/primitives/divider-base.component.ts +74 -0
  108. package/src/lib/primitives/dropdown-arrow.component.test.ts +12 -0
  109. package/src/lib/primitives/dropdown-arrow.component.ts +46 -0
  110. package/src/lib/primitives/dropdown-base.component.test.ts +49 -0
  111. package/src/lib/primitives/dropdown-base.component.ts +329 -0
  112. package/src/lib/primitives/helpers.ts +151 -0
  113. package/src/lib/primitives/horizontal-loader.component.test.ts +19 -0
  114. package/src/lib/primitives/horizontal-loader.component.ts +70 -0
  115. package/src/lib/primitives/icon-base.component.test.ts +19 -0
  116. package/src/lib/primitives/icon-base.component.ts +58 -0
  117. package/src/lib/primitives/img-loader.component.test.ts +12 -0
  118. package/src/lib/primitives/img-loader.component.ts +118 -0
  119. package/src/lib/primitives/index.ts +29 -0
  120. package/src/lib/primitives/input-base.component.test.ts +20 -0
  121. package/src/lib/primitives/input-base.component.ts +210 -0
  122. package/src/lib/primitives/lifetime-slider-base.component.test.ts +88 -0
  123. package/src/lib/primitives/lifetime-slider-base.component.ts +75 -0
  124. package/src/lib/primitives/oval-spinner.component.test.ts +9 -0
  125. package/src/lib/primitives/oval-spinner.component.ts +158 -0
  126. package/src/lib/primitives/pickers/dropdown-picker-animated-base.component.ts +220 -0
  127. package/src/lib/primitives/pickers/picker-button.component.ts +74 -0
  128. package/src/lib/primitives/pickers/time-picker-dropdown-selector.component.ts +188 -0
  129. package/src/lib/primitives/pickers/time-picker.component.ts +124 -0
  130. package/src/lib/primitives/pickers/time-unit-selector.component.ts +82 -0
  131. package/src/lib/primitives/progressbar.component.test.ts +18 -0
  132. package/src/lib/primitives/progressbar.component.ts +93 -0
  133. package/src/lib/primitives/providersStyles.ts +268 -0
  134. package/src/lib/primitives/slider-base.component.ts +56 -0
  135. package/src/lib/provide-block-auth.test.ts +43 -0
  136. package/src/lib/provide-block-auth.ts +43 -0
  137. package/src/lib/runtime/block-auth-runtime.directive.test.ts +89 -0
  138. package/src/lib/runtime/block-auth-runtime.directive.ts +68 -0
  139. package/src/lib/teleport-body.directive.test.ts +29 -0
  140. package/src/lib/teleport-body.directive.ts +21 -0
  141. package/src/lib/testing.ts +15 -0
  142. package/src/lib/transitions.css +23 -0
  143. package/src/public-api.ts +85 -0
  144. package/src/vitest.setup.ts +8 -0
  145. package/tailwind.config.cjs +6 -0
  146. package/tsconfig.json +15 -0
  147. package/tsconfig.lib.json +9 -0
  148. package/vite.config.mts +44 -0
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { Component } from '@angular/core';
3
+ import { render, screen } from '@testing-library/angular';
4
+
5
+ vi.mock('@wagmi/core', () => ({
6
+ createConfig: vi.fn(() => ({ connectors: [] })),
7
+ connect: vi.fn(), disconnect: vi.fn(), getAccount: vi.fn(() => ({})),
8
+ signMessage: vi.fn(), watchAccount: vi.fn(() => () => undefined), http: vi.fn(),
9
+ }));
10
+ vi.mock('@wagmi/connectors', () => ({
11
+ injected: vi.fn(() => ({ id: 'injected' })), walletConnect: vi.fn(() => ({ id: 'walletConnect' })),
12
+ }));
13
+
14
+ import { provideBlockAuth, injectBlockAuthClient } from './provide-block-auth';
15
+
16
+ @Component({
17
+ selector: 'ba-probe', standalone: true,
18
+ template: `<span data-testid="probe">{{ kind }}</span>`,
19
+ })
20
+ class ProbeComponent {
21
+ client = injectBlockAuthClient();
22
+ kind = typeof this.client.web3.signup;
23
+ }
24
+
25
+ describe('provideBlockAuth', () => {
26
+ it('provee el client y injectBlockAuthClient lo inyecta', async () => {
27
+ await render(ProbeComponent, {
28
+ providers: [provideBlockAuth({ apiKey: 'k', apiSecret: 's', walletConnectProjectId: 'pid' })],
29
+ });
30
+ expect(screen.getByTestId('probe').textContent).toBe('function');
31
+ });
32
+
33
+ it('inyecta el stylesheet del SDK en head una sola vez', async () => {
34
+ await render(ProbeComponent, {
35
+ providers: [provideBlockAuth({ apiKey: 'k', apiSecret: 's', walletConnectProjectId: 'pid' })],
36
+ });
37
+ expect(document.head.querySelectorAll('style[data-blockauth]').length).toBe(1);
38
+ });
39
+
40
+ it('injectBlockAuthClient sin provider lanza error claro', async () => {
41
+ await expect(render(ProbeComponent)).rejects.toThrow(/provideBlockAuth/);
42
+ });
43
+ });
@@ -0,0 +1,43 @@
1
+ import { DOCUMENT } from '@angular/common';
2
+ import { InjectionToken, inject, makeEnvironmentProviders, type EnvironmentProviders } from '@angular/core';
3
+ import { createBlockAuthClient, type BlockAuthClient, type BlockAuthClientConfig } from '@block-auth.io/core';
4
+ import { BLOCKAUTH_CSS } from '../styles.generated';
5
+
6
+ const BLOCK_AUTH_CLIENT = new InjectionToken<BlockAuthClient>('BLOCK_AUTH_CLIENT');
7
+
8
+ function injectSdkStyles(doc: Document): void {
9
+ if (doc.head.querySelector('style[data-blockauth]')) return;
10
+ const style = doc.createElement('style');
11
+ style.setAttribute('data-blockauth', '');
12
+ style.textContent = BLOCKAUTH_CSS;
13
+ doc.head.appendChild(style);
14
+ }
15
+
16
+ /**
17
+ * Registra el BlockAuthClient en el injector (equivalente Angular del plugin
18
+ * createBlockAuth de Vue / <BlockAuthProvider> de React) e inyecta el
19
+ * stylesheet del SDK en <head> (idempotente: solo una vez por documento).
20
+ */
21
+ export function provideBlockAuth(config: BlockAuthClientConfig): EnvironmentProviders {
22
+ return makeEnvironmentProviders([
23
+ {
24
+ provide: BLOCK_AUTH_CLIENT,
25
+ useFactory: () => {
26
+ const doc = inject(DOCUMENT);
27
+ if (doc?.head) injectSdkStyles(doc); // SSR-safe: only touch <head> in browser
28
+ return createBlockAuthClient(config);
29
+ },
30
+ },
31
+ ]);
32
+ }
33
+
34
+ export function injectBlockAuthClient(): BlockAuthClient {
35
+ const client = inject(BLOCK_AUTH_CLIENT, { optional: true });
36
+ if (!client) {
37
+ throw new Error('[block-auth] injectBlockAuthClient() requiere provideBlockAuth(config) en los providers');
38
+ }
39
+ return client;
40
+ }
41
+
42
+ // Test-only alias so lib/testing.ts (Task A7) can fabricate a fake-client provider.
43
+ export const BLOCK_AUTH_CLIENT_INTERNAL: InjectionToken<BlockAuthClient> = BLOCK_AUTH_CLIENT;
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { Component } from '@angular/core';
3
+ import { render } from '@testing-library/angular';
4
+ import { map } from 'nanostores';
5
+
6
+ // Se mockea `@block-auth.io/core` PARCIALMENTE (patrón de content-signup.test):
7
+ // se conserva todo lo real y solo se espían `createAuthRuntime` (devuelve un
8
+ // runtime de mentira `{ start, stop }`) y `startExtensionSync` (devuelve un
9
+ // cleanup espía). Así `injectStore(client.state.$ws)` usa el nanostore real y
10
+ // no hace falta un client real ni los mocks de `@wagmi/*`.
11
+ const startSpy = vi.fn();
12
+ const stopSpy = vi.fn();
13
+ const extensionCleanupSpy = vi.fn();
14
+ const startExtensionSyncSpy = vi.fn(() => extensionCleanupSpy);
15
+ const createAuthRuntimeSpy = vi.fn(() => ({ start: startSpy, stop: stopSpy, logout: vi.fn() }));
16
+
17
+ vi.mock('@block-auth.io/core', async (importOriginal) => {
18
+ const actual = await importOriginal<typeof import('@block-auth.io/core')>();
19
+ return {
20
+ ...actual,
21
+ createAuthRuntime: () => createAuthRuntimeSpy(),
22
+ startExtensionSync: () => startExtensionSyncSpy(),
23
+ };
24
+ });
25
+
26
+ import { BlockAuthRuntimeDirective } from './block-auth-runtime.directive';
27
+ import { provideBlockAuthClientForTest } from '../testing';
28
+
29
+ function makeClient(ready = false) {
30
+ return { state: { $ws: map<{ ready: boolean }>({ ready }) } } as any;
31
+ }
32
+
33
+ @Component({
34
+ standalone: true,
35
+ imports: [BlockAuthRuntimeDirective],
36
+ template: `<ng-container baRuntime></ng-container>`,
37
+ })
38
+ class HostComponent {}
39
+
40
+ describe('BlockAuthRuntimeDirective', () => {
41
+ beforeEach(() => {
42
+ startSpy.mockClear();
43
+ stopSpy.mockClear();
44
+ extensionCleanupSpy.mockClear();
45
+ createAuthRuntimeSpy.mockClear();
46
+ startExtensionSyncSpy.mockClear();
47
+ });
48
+
49
+ it('al montar arranca startExtensionSync + createAuthRuntime + start', async () => {
50
+ await render(HostComponent, { providers: [provideBlockAuthClientForTest(makeClient())] });
51
+ expect(startExtensionSyncSpy).toHaveBeenCalledTimes(1);
52
+ expect(createAuthRuntimeSpy).toHaveBeenCalledTimes(1);
53
+ expect(startSpy).toHaveBeenCalledTimes(1);
54
+ });
55
+
56
+ it('al destruir llama runtime.stop() y el cleanup de startExtensionSync', async () => {
57
+ const { fixture } = await render(HostComponent, {
58
+ providers: [provideBlockAuthClientForTest(makeClient())],
59
+ });
60
+ fixture.destroy();
61
+ expect(stopSpy).toHaveBeenCalledTimes(1);
62
+ expect(extensionCleanupSpy).toHaveBeenCalledTimes(1);
63
+ });
64
+
65
+ it('no reconecta en el `false` inicial (antes de la primera conexión)', async () => {
66
+ const { fixture } = await render(HostComponent, {
67
+ providers: [provideBlockAuthClientForTest(makeClient(false))],
68
+ });
69
+ expect(startSpy).toHaveBeenCalledTimes(1);
70
+ fixture.detectChanges();
71
+ expect(startSpy).toHaveBeenCalledTimes(1);
72
+ });
73
+
74
+ it('reconecta (re-start) cuando `ready` pasa de true a false tras el teardown', async () => {
75
+ const client = makeClient(false);
76
+ const { fixture } = await render(HostComponent, {
77
+ providers: [provideBlockAuthClientForTest(client)],
78
+ });
79
+ expect(startSpy).toHaveBeenCalledTimes(1); // conexión inicial
80
+
81
+ client.state.$ws.setKey('ready', true);
82
+ fixture.detectChanges();
83
+ expect(startSpy).toHaveBeenCalledTimes(1); // sin start extra al conectar
84
+
85
+ client.state.$ws.setKey('ready', false);
86
+ fixture.detectChanges();
87
+ expect(startSpy).toHaveBeenCalledTimes(2); // reconexión
88
+ });
89
+ });
@@ -0,0 +1,68 @@
1
+ import { PLATFORM_ID, Directive, OnDestroy, OnInit, effect, inject, output } from '@angular/core';
2
+ import { isPlatformBrowser } from '@angular/common';
3
+ import { createAuthRuntime, startExtensionSync, type AuthRuntime } from '@block-auth.io/core';
4
+ import { injectBlockAuthClient } from '../provide-block-auth';
5
+ import { injectStore } from '../inject-store';
6
+
7
+ // Inicializador fino del runtime dirigido por el core, como DIRECTIVA sin
8
+ // plantilla (`[baRuntime]`).
9
+ //
10
+ // Al montar arranca `startExtensionSync()` (lo que en React hace el
11
+ // `BlockAuthProvider` en su efecto de montaje — fusionado acá igual que en el
12
+ // composable de Vue) y `createAuthRuntime(client, callbacks).start()` (connect →
13
+ // signin → cablea todos los handlers `sdk:*`/`provider:*`/`user:txns*`/`signin:*`
14
+ // → dirige el estado nanostores que lee la UI). Al destruir llama `runtime.stop()`
15
+ // (limpia el timeout de unsign + el intervalo de polling de user:txns y
16
+ // desconecta) y el cleanup de `startExtensionSync()`.
17
+ //
18
+ // El `input` no reactivo no existe acá: los callbacks son OUTPUTS
19
+ // (`success`/`errored`/`loggedOut`) que `FlowBlockAuth` reenvía a sus propios
20
+ // outputs.
21
+ @Directive({ selector: '[baRuntime]', standalone: true })
22
+ export class BlockAuthRuntimeDirective implements OnInit, OnDestroy {
23
+ success = output<string | undefined>();
24
+ errored = output<void>();
25
+ loggedOut = output<void>();
26
+
27
+ private platformId = inject(PLATFORM_ID);
28
+ private client = injectBlockAuthClient();
29
+ private ws = injectStore(this.client.state.$ws);
30
+ private runtime: AuthRuntime | null = null;
31
+ private wasReady = false;
32
+ private stopExtensionSync: (() => void) | null = null;
33
+
34
+ constructor() {
35
+ // Reconexión tras el teardown del DeletionService (ready true→false),
36
+ // port del segundo useEffect de BlockAuthRuntime (react). Es no-op hasta
37
+ // que exista un runtime (p.ej. en SSR, donde ngOnInit sale temprano).
38
+ effect(() => {
39
+ const ready = this.ws().ready;
40
+ if (ready) { this.wasReady = true; return; }
41
+ if (this.wasReady && this.runtime) {
42
+ this.wasReady = false;
43
+ void this.runtime.start();
44
+ }
45
+ });
46
+ }
47
+
48
+ ngOnInit(): void {
49
+ // Angular Universal corre ngOnInit en el servidor; el trabajo de browser
50
+ // (startExtensionSync / runtime.start) nunca debe correr server-side.
51
+ if (!isPlatformBrowser(this.platformId)) return;
52
+ this.stopExtensionSync = startExtensionSync();
53
+ this.runtime = createAuthRuntime(this.client, {
54
+ onSuccess: (address) => this.success.emit(address),
55
+ onError: () => this.errored.emit(),
56
+ onLogout: () => this.loggedOut.emit(),
57
+ });
58
+ void this.runtime.start();
59
+ }
60
+
61
+ ngOnDestroy(): void {
62
+ this.runtime?.stop();
63
+ this.runtime = null;
64
+ this.wasReady = false;
65
+ this.stopExtensionSync?.();
66
+ this.stopExtensionSync = null;
67
+ }
68
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Component } from '@angular/core';
3
+ import { render, screen } from '@testing-library/angular';
4
+ import { TeleportBodyDirective } from './teleport-body.directive';
5
+
6
+ @Component({
7
+ selector: 'ba-teleport-host',
8
+ standalone: true,
9
+ imports: [TeleportBodyDirective],
10
+ template: `<div baTeleportBody data-testid="tp">content</div>`,
11
+ })
12
+ class TeleportHostComponent {}
13
+
14
+ describe('TeleportBodyDirective', () => {
15
+ it('mueve el host element a document.body al montar', async () => {
16
+ const { fixture } = await render(TeleportHostComponent);
17
+ const node = screen.getByTestId('tp');
18
+ expect(node.parentElement).toBe(document.body);
19
+ fixture.destroy();
20
+ });
21
+
22
+ it('elimina el nodo de document.body al destruir', async () => {
23
+ const { fixture } = await render(TeleportHostComponent);
24
+ const node = screen.getByTestId('tp');
25
+ expect(document.body.contains(node)).toBe(true);
26
+ fixture.destroy();
27
+ expect(document.body.contains(node)).toBe(false);
28
+ });
29
+ });
@@ -0,0 +1,21 @@
1
+ import { DOCUMENT } from '@angular/common';
2
+ import { Directive, ElementRef, OnDestroy, OnInit, inject } from '@angular/core';
3
+
4
+ /**
5
+ * Mueve el host element a document.body al iniciar (equivalente a
6
+ * createPortal(..., document.body) / <Teleport to="body">) y lo elimina al
7
+ * destruir. Necesario para que el overlay fixed de los modales no quede
8
+ * atrapado en ancestros con transform.
9
+ */
10
+ @Directive({ selector: '[baTeleportBody]', standalone: true })
11
+ export class TeleportBodyDirective implements OnInit, OnDestroy {
12
+ private readonly el = inject(ElementRef<HTMLElement>);
13
+ private readonly doc = inject(DOCUMENT);
14
+
15
+ ngOnInit(): void {
16
+ this.doc.body.appendChild(this.el.nativeElement);
17
+ }
18
+ ngOnDestroy(): void {
19
+ this.el.nativeElement.remove();
20
+ }
21
+ }
@@ -0,0 +1,15 @@
1
+ import type { Provider } from '@angular/core';
2
+ import type { BlockAuthClient } from '@block-auth.io/core';
3
+ // El token es privado de provide-block-auth; se reexporta desde allí con un
4
+ // nombre interno (`BLOCK_AUTH_CLIENT_INTERNAL`) para poder fabricar providers
5
+ // de test que inyecten un client falso sin arrancar el client real.
6
+ import { BLOCK_AUTH_CLIENT_INTERNAL } from './provide-block-auth';
7
+
8
+ /**
9
+ * Helper SOLO de tests (no se exporta en public-api): registra un client
10
+ * parcial/falso bajo el token de BlockAuth, de modo que
11
+ * `injectBlockAuthClient()` lo devuelva en lugar del client real.
12
+ */
13
+ export function provideBlockAuthClientForTest(client: Partial<BlockAuthClient>): Provider {
14
+ return { provide: BLOCK_AUTH_CLIENT_INTERNAL, useValue: client };
15
+ }
@@ -0,0 +1,23 @@
1
+ /* fade — sustituye a motion.div animate={{opacity}} */
2
+ .bas-fade-enter-active, .bas-fade-leave-active { transition: opacity 0.2s ease; }
3
+ .bas-fade-enter-from, .bas-fade-leave-to { opacity: 0; }
4
+
5
+ /* pop — sustituye al scale+fade de DropdownBase/DropdownProfile */
6
+ .bas-pop-enter-active, .bas-pop-leave-active {
7
+ transition: opacity 0.15s ease, transform 0.15s ease;
8
+ transform-origin: top;
9
+ }
10
+ .bas-pop-enter-from, .bas-pop-leave-to { opacity: 0; transform: scale(0.95); }
11
+
12
+ /* slide-up — entrada de modales (bottom sheet en móvil) */
13
+ .bas-slide-up-enter-active, .bas-slide-up-leave-active { transition: transform 0.25s ease, opacity 0.25s ease; }
14
+ .bas-slide-up-enter-from, .bas-slide-up-leave-to { transform: translateY(16px); opacity: 0; }
15
+
16
+ /* expand — alto animado de ProvidersFilters/TxnsOverview */
17
+ .bas-expand-enter-active, .bas-expand-leave-active { transition: max-height 0.25s ease, opacity 0.25s ease; overflow: hidden; }
18
+ .bas-expand-enter-from, .bas-expand-leave-to { max-height: 0; opacity: 0; }
19
+ .bas-expand-enter-to, .bas-expand-leave-from { max-height: 600px; }
20
+
21
+ /* spinner oval — sustituye a react-loader-spinner */
22
+ @keyframes bas-spin { to { transform: rotate(360deg); } }
23
+ .bas-oval-spin { animation: bas-spin 0.8s linear infinite; }
@@ -0,0 +1,85 @@
1
+ // API pública de @block-auth.io/angular. Espejo de packages/react/src/index.ts
2
+ // (los estilos se inyectan en runtime vía provideBlockAuth, no acá — ng-packagr
3
+ // no hace inline de CSS como tsup). testing.ts NO se exporta (helper de tests).
4
+
5
+ // Provider / DI
6
+ export { provideBlockAuth, injectBlockAuthClient } from './lib/provide-block-auth';
7
+ export { injectStore } from './lib/inject-store';
8
+ export { BlockAuthService, type Locale, type TextKey } from './lib/block-auth.service';
9
+
10
+ // Runtime / directivas
11
+ export { BlockAuthRuntimeDirective } from './lib/runtime/block-auth-runtime.directive';
12
+ export { TeleportBodyDirective } from './lib/teleport-body.directive';
13
+
14
+ // Orchestrator
15
+ export { FlowBlockAuthComponent } from './lib/components/flow-block-auth.component';
16
+
17
+ // User profile / role helpers (re-exportados del core, como el viejo `main.js`)
18
+ export {
19
+ loadUserProfile,
20
+ getUserRoles,
21
+ getUserRoleNames,
22
+ getUserRoleSlugs,
23
+ hasRole,
24
+ hasRoleBySlug,
25
+ hasAnyRole,
26
+ hasAnyRoleBySlug,
27
+ type BlockAuthUserProfile,
28
+ type BlockAuthUserRole,
29
+ } from '@block-auth.io/core';
30
+
31
+ // Buttons (barrel existente)
32
+ export {
33
+ BtnBaseProviderComponent,
34
+ BtnBlockAuthComponent,
35
+ BtnGmailComponent,
36
+ BtnGoogleBaseComponent,
37
+ BtnMetamaskComponent,
38
+ BtnMicrosoftComponent,
39
+ BtnPhantomComponent,
40
+ BtnSigninComponent,
41
+ BtnWalletConnectComponent,
42
+ } from './lib/components/btns';
43
+
44
+ // Contents
45
+ export { ContentSigninComponent } from './lib/components/contents/content-signin.component';
46
+ export { ContentSignupComponent } from './lib/components/contents/content-signup.component';
47
+
48
+ // Dropdown / profile
49
+ export {
50
+ DropdownProfileComponent,
51
+ type DropdownProfileAdditionalItem,
52
+ } from './lib/components/dds/dropdown-profile.component';
53
+ export { DropdownProfileBtnComponent } from './lib/components/dds/dropdown-profile-btn.component';
54
+ export { DropdownProfileItemComponent } from './lib/components/dds/dropdown-profile-item.component';
55
+ export { ChangeAccountComponent } from './lib/components/dds/change-account.component';
56
+
57
+ // Modals
58
+ export { ModalOverlayComponent } from './lib/components/modals/modal-overlay.component';
59
+ export { ModalSigninComponent } from './lib/components/modals/modal-signin.component';
60
+ export { ModalSignupComponent } from './lib/components/modals/modal-signup.component';
61
+ export { ModalRestoreComponent } from './lib/components/modals/modal-restore.component';
62
+ export { ModalRestorationExpiredComponent } from './lib/components/modals/modal-restoration-expired.component';
63
+
64
+ // Sections (+ internals)
65
+ export { LoginSectionComponent } from './lib/components/sections/login-section.component';
66
+ export { ProvidersSectionComponent } from './lib/components/sections/providers-section.component';
67
+ export { FooterSectionComponent } from './lib/components/sections/footer-section.component';
68
+ export { ProvidersButtonsComponent } from './lib/components/sections/internals/providers-buttons.component';
69
+ export { SelectedButtonComponent } from './lib/components/sections/internals/selected-button.component';
70
+ export {
71
+ ProvidersFiltersComponent,
72
+ makeWebTypeItems,
73
+ type WebTypeItem,
74
+ } from './lib/components/sections/internals/providers-filters.component';
75
+
76
+ // Transactions (+ internals)
77
+ export { TxnsOverviewComponent } from './lib/components/txns/txns-overview.component';
78
+ export { TxnComponent } from './lib/components/txns/internals/txn.component';
79
+ export { TxnTypeComponent } from './lib/components/txns/internals/txn-type.component';
80
+ export { TxnErrorComponent } from './lib/components/txns/internals/txn-error.component';
81
+ export { PermissionsRequiredModalComponent } from './lib/components/txns/internals/permissions-required-modal.component';
82
+ export {
83
+ SolicitedDataModalComponent,
84
+ type ParamChange,
85
+ } from './lib/components/txns/internals/solicited-data-modal.component';
@@ -0,0 +1,8 @@
1
+ import '@analogjs/vite-plugin-angular/setup-vitest';
2
+ import { getTestBed } from '@angular/core/testing';
3
+ import {
4
+ BrowserDynamicTestingModule,
5
+ platformBrowserDynamicTesting,
6
+ } from '@angular/platform-browser-dynamic/testing';
7
+
8
+ getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
@@ -0,0 +1,6 @@
1
+ const preset = require('@block-auth.io/ui-styles/tailwind');
2
+
3
+ module.exports = {
4
+ presets: [preset],
5
+ content: ['./src/**/*.ts', './src/**/*.html'],
6
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "useDefineForClassFields": false,
8
+ "importHelpers": true,
9
+ "skipLibCheck": true,
10
+ "types": ["vitest/globals"]
11
+ },
12
+ "include": ["src/**/*.ts"],
13
+ "exclude": ["dist"],
14
+ "angularCompilerOptions": { "strictTemplates": true }
15
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": { "declaration": true, "types": [] },
4
+ "exclude": ["dist", "src/**/*.test.ts", "src/vitest.setup.ts"],
5
+ "angularCompilerOptions": {
6
+ "strictTemplates": true,
7
+ "compilationMode": "partial"
8
+ }
9
+ }
@@ -0,0 +1,44 @@
1
+ /// <reference types="vitest" />
2
+ import { defineConfig } from 'vite';
3
+ import angular from '@analogjs/vite-plugin-angular';
4
+
5
+ export default defineConfig({
6
+ // Sin tsconfig.spec.json propio (el paquete solo tiene tsconfig.json +
7
+ // tsconfig.lib.json): apuntar el plugin al tsconfig base evita el warning
8
+ // "Unable to resolve tsconfig at .../tsconfig.spec.json".
9
+ //
10
+ // `experimental.useAngularCompilationAPI: true` es OBLIGATORIO: sin él el
11
+ // plugin preserva los decoradores para que TestBed compile en JIT, y el
12
+ // compilador JIT NO soporta signal inputs (`input()` no se registra como
13
+ // binding → NG0303 "Can't bind to ...") ni queries/decoradores de campo
14
+ // (`@ViewChild`/`@Input` → "Standard Angular field decorators are not
15
+ // supported in JIT mode"). Con esta opción el plugin usa la API de
16
+ // compilación real de Angular (calidad AOT), que emite los metadatos de
17
+ // signal input/output y hace que el binding entre componentes funcione.
18
+ plugins: [angular({ tsconfig: './tsconfig.json', experimental: { useAngularCompilationAPI: true } })],
19
+ // pnpm crea DOS variantes peer de `@wagmi/core@3.5.2` (una por typescript
20
+ // 5.8.3 y otra por 5.9.3): `@block-auth.io/core` importa una y el paquete
21
+ // angular la otra. Sin dedupe, `vi.mock('@wagmi/core')` de los tests solo
22
+ // parchea la copia resuelta desde el test → la copia que usa core queda real
23
+ // (ProviderNotFoundError). dedupe colapsa ambas en una sola copia para que el
24
+ // mock alcance también a core (idéntico efecto al del monorepo de react/vue).
25
+ resolve: { dedupe: ['@wagmi/core', '@wagmi/connectors'] },
26
+ test: {
27
+ // Requerido por el patch de zone.js que instala
28
+ // `@analogjs/vite-plugin-angular/setup-vitest` (envuelve describe/it/
29
+ // beforeEach en globalThis) y por `"types": ["vitest/globals"]` del
30
+ // tsconfig — mismo convenio que packages/vue.
31
+ globals: true,
32
+ environment: 'jsdom',
33
+ setupFiles: ['./src/vitest.setup.ts'],
34
+ include: ['src/**/*.test.ts'],
35
+ css: false,
36
+ passWithNoTests: true,
37
+ // `@block-auth.io/core` (dist ESM del workspace) debe pasar por el resolver
38
+ // de vite (inline) en vez de importarse nativamente, para que su
39
+ // `import '@wagmi/core'` respete el `resolve.dedupe` de arriba y quede
40
+ // interceptable por `vi.mock('@wagmi/core')` de los tests de integración
41
+ // (importado nativamente saltaría tanto el dedupe como el mock).
42
+ server: { deps: { inline: [/@block-auth\.io[/\\]core/, /packages[/\\]core[/\\]dist/] } },
43
+ },
44
+ });