@adonis-agora/authkit-server 0.65.2 → 0.66.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 (40) hide show
  1. package/build/commands/configure.js +12 -0
  2. package/build/index.d.ts +2 -0
  3. package/build/index.js +1 -0
  4. package/build/src/accounts/account_store.d.ts +14 -3
  5. package/build/src/accounts/lucid_store/core.js +5 -4
  6. package/build/src/audit/audit_sink.d.ts +14 -3
  7. package/build/src/audit/lucid_audit_sink.js +5 -4
  8. package/build/src/commands/expire_scan_command.js +9 -8
  9. package/build/src/define_config.d.ts +3 -2
  10. package/build/src/define_config.js +2 -1
  11. package/build/src/doctor/checks.js +1 -1
  12. package/build/src/host/account_export_service.js +4 -3
  13. package/build/src/host/admin_api/admin_orgs_service.js +6 -5
  14. package/build/src/host/admin_api/admin_users_service.d.ts +3 -3
  15. package/build/src/host/admin_api/admin_users_service.js +8 -7
  16. package/build/src/host/admin_api/api_misc_controller.d.ts +6 -4
  17. package/build/src/host/admin_api/api_misc_controller.js +6 -5
  18. package/build/src/host/admin_api/api_users_controller.d.ts +6 -4
  19. package/build/src/host/admin_api/api_users_controller.js +6 -6
  20. package/build/src/host/admin_console/console_audit_controller.d.ts +6 -4
  21. package/build/src/host/admin_console/console_audit_controller.js +6 -5
  22. package/build/src/host/admin_console/console_overview_controller.js +4 -1
  23. package/build/src/host/admin_console/console_users_controller.d.ts +6 -4
  24. package/build/src/host/admin_console/console_users_controller.js +6 -6
  25. package/build/src/host/admin_stats_service.js +7 -5
  26. package/build/src/host/assets/webauthn.js +2 -2
  27. package/build/src/host/branding.d.ts +7 -0
  28. package/build/src/host/branding.js +31 -0
  29. package/build/src/host/login_attempt.js +3 -2
  30. package/build/src/host/login_notify.js +3 -2
  31. package/build/src/host/session_context.js +3 -2
  32. package/build/src/host/ui-dist/assets/{index-Dp__4wlB.js → index-CkCQ94la.js} +3 -3
  33. package/build/src/host/ui-dist/index.html +1 -1
  34. package/build/src/pagination.d.ts +77 -0
  35. package/build/src/pagination.js +66 -0
  36. package/build/stubs/migrations/create_auth_users_table.stub +47 -0
  37. package/build/stubs/models/auth_user.stub +30 -1
  38. package/package.json +12 -12
  39. package/stubs/migrations/create_auth_users_table.stub +47 -0
  40. package/stubs/models/auth_user.stub +30 -1
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
11
- <script type="module" crossorigin src="/__AUTHKIT_BASE__/assets/index-Dp__4wlB.js"></script>
11
+ <script type="module" crossorigin src="/__AUTHKIT_BASE__/assets/index-CkCQ94la.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/__AUTHKIT_BASE__/assets/index-DTSmD4RU.css">
13
13
  </head>
14
14
  <body>
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Convenções de paginação por OFFSET compartilhadas por TODAS as listagens
3
+ * paginadas do authkit (contas, log de auditoria, console admin e Admin API).
4
+ *
5
+ * O shape `{ page, size }` espelha INTENCIONALMENTE o de `@adonis-agora/filter`
6
+ * (`FilterInput.page` / `FilterInput.size` → `ResolvedPagination { page, size }`),
7
+ * para que todos os pacotes `@adonis-agora/*` exponham a MESMA interface de
8
+ * paginação. O casamento é ESTRUTURAL de propósito: NÃO dependemos de
9
+ * `@adonis-agora/filter` — só seguimos o mesmo contrato.
10
+ *
11
+ * - `page` é 1-based; default {@link LIST_FIRST_PAGE}.
12
+ * - `size` é o tamanho da página; default {@link ADMIN_LIST_DEFAULT_SIZE} nas
13
+ * varreduras programáticas e {@link ADMIN_LIST_HTTP_DEFAULT_SIZE} na wire HTTP.
14
+ * - Na wire HTTP os parâmetros são `?page=1&size=25`.
15
+ */
16
+ /** Primeira página. A paginação é 1-based, como em `@adonis-agora/filter`. */
17
+ export declare const LIST_FIRST_PAGE = 1;
18
+ /**
19
+ * Tamanho de página default das varreduras programáticas internas (ex.: contagem
20
+ * de admins e agregação de orgs, que paginam o `accountStore` até o fim).
21
+ */
22
+ export declare const ADMIN_LIST_DEFAULT_SIZE = 100;
23
+ /**
24
+ * Tamanho de página default quando a request HTTP não manda `?size=`. Menor que
25
+ * {@link ADMIN_LIST_DEFAULT_SIZE} porque é o que a UI do console renderiza por página.
26
+ */
27
+ export declare const ADMIN_LIST_HTTP_DEFAULT_SIZE = 20;
28
+ /**
29
+ * Teto de `size` aceito de um caller NÃO CONFIÁVEL — ou seja, de query params
30
+ * HTTP ({@link parseListSize}). Antes não havia teto nenhum no console admin
31
+ * nem na Admin API além de um `Math.min` solto por controller; agora é um só
32
+ * número, para que um `?size=100000` não vire um scan de tabela inteira.
33
+ *
34
+ * NÃO é aplicado a chamadas programáticas de dentro da lib (ex.: o export
35
+ * LGPD/GDPR lê 1000 eventos de auditoria de uma vez): essas são confiáveis e
36
+ * usam {@link normalizeListSize}.
37
+ */
38
+ export declare const ADMIN_LIST_MAX_SIZE = 200;
39
+ /** Normaliza um `page` arbitrário para um inteiro 1-based válido. */
40
+ export declare function resolveListPage(page: number | undefined): number;
41
+ /**
42
+ * Normaliza um `size` de caller CONFIÁVEL para um inteiro `>= 1`, caindo em
43
+ * `fallback` quando ausente/inválido. Sem teto — veja {@link clampListSize}.
44
+ */
45
+ export declare function normalizeListSize(size: number | undefined, fallback?: number): number;
46
+ /**
47
+ * Idem {@link normalizeListSize}, mas limitado a {@link ADMIN_LIST_MAX_SIZE}.
48
+ * Use em TODA borda que recebe `size` de fora do processo.
49
+ */
50
+ export declare function clampListSize(size: number | undefined, fallback?: number): number;
51
+ /** {@link resolveListPage} a partir do valor cru de um query param. */
52
+ export declare function parseListPage(raw: unknown): number;
53
+ /** {@link clampListSize} a partir do valor cru de um query param. */
54
+ export declare function parseListSize(raw: unknown, fallback?: number): number;
55
+ /**
56
+ * Envelope de METADADOS de uma listagem paginada — a chave `meta` de toda
57
+ * resposta paginada do authkit (`{ meta, data }`).
58
+ *
59
+ * O nome `meta` e a co-locação de `{ page, size }` dentro dele são a convenção
60
+ * do `.paginate()` do Lucid (`{ meta, data }`) — que é onde o caminho por
61
+ * offset de `@adonis-agora/filter` desemboca — e por isso a convenção adotada
62
+ * por TODO o ecossistema `@adonis-agora/*`. Antes o authkit achatava
63
+ * `total`/`page`/`size` ao lado de `data`, sem envelope.
64
+ */
65
+ export interface ListMeta {
66
+ /** Página devolvida (1-based). */
67
+ page: number;
68
+ /** Tamanho de página efetivamente aplicado (já normalizado/limitado). */
69
+ size: number;
70
+ /** Total absoluto de itens que casam com o filtro, através de todas as páginas. */
71
+ total: number;
72
+ }
73
+ /** Resposta paginada padrão: `{ meta, data }`, na ordem do `.paginate()` do Lucid. */
74
+ export interface PaginatedResponse<T> {
75
+ meta: ListMeta;
76
+ data: T[];
77
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Convenções de paginação por OFFSET compartilhadas por TODAS as listagens
3
+ * paginadas do authkit (contas, log de auditoria, console admin e Admin API).
4
+ *
5
+ * O shape `{ page, size }` espelha INTENCIONALMENTE o de `@adonis-agora/filter`
6
+ * (`FilterInput.page` / `FilterInput.size` → `ResolvedPagination { page, size }`),
7
+ * para que todos os pacotes `@adonis-agora/*` exponham a MESMA interface de
8
+ * paginação. O casamento é ESTRUTURAL de propósito: NÃO dependemos de
9
+ * `@adonis-agora/filter` — só seguimos o mesmo contrato.
10
+ *
11
+ * - `page` é 1-based; default {@link LIST_FIRST_PAGE}.
12
+ * - `size` é o tamanho da página; default {@link ADMIN_LIST_DEFAULT_SIZE} nas
13
+ * varreduras programáticas e {@link ADMIN_LIST_HTTP_DEFAULT_SIZE} na wire HTTP.
14
+ * - Na wire HTTP os parâmetros são `?page=1&size=25`.
15
+ */
16
+ /** Primeira página. A paginação é 1-based, como em `@adonis-agora/filter`. */
17
+ export const LIST_FIRST_PAGE = 1;
18
+ /**
19
+ * Tamanho de página default das varreduras programáticas internas (ex.: contagem
20
+ * de admins e agregação de orgs, que paginam o `accountStore` até o fim).
21
+ */
22
+ export const ADMIN_LIST_DEFAULT_SIZE = 100;
23
+ /**
24
+ * Tamanho de página default quando a request HTTP não manda `?size=`. Menor que
25
+ * {@link ADMIN_LIST_DEFAULT_SIZE} porque é o que a UI do console renderiza por página.
26
+ */
27
+ export const ADMIN_LIST_HTTP_DEFAULT_SIZE = 20;
28
+ /**
29
+ * Teto de `size` aceito de um caller NÃO CONFIÁVEL — ou seja, de query params
30
+ * HTTP ({@link parseListSize}). Antes não havia teto nenhum no console admin
31
+ * nem na Admin API além de um `Math.min` solto por controller; agora é um só
32
+ * número, para que um `?size=100000` não vire um scan de tabela inteira.
33
+ *
34
+ * NÃO é aplicado a chamadas programáticas de dentro da lib (ex.: o export
35
+ * LGPD/GDPR lê 1000 eventos de auditoria de uma vez): essas são confiáveis e
36
+ * usam {@link normalizeListSize}.
37
+ */
38
+ export const ADMIN_LIST_MAX_SIZE = 200;
39
+ /** Normaliza um `page` arbitrário para um inteiro 1-based válido. */
40
+ export function resolveListPage(page) {
41
+ const n = Math.trunc(Number(page));
42
+ return Number.isFinite(n) && n >= LIST_FIRST_PAGE ? n : LIST_FIRST_PAGE;
43
+ }
44
+ /**
45
+ * Normaliza um `size` de caller CONFIÁVEL para um inteiro `>= 1`, caindo em
46
+ * `fallback` quando ausente/inválido. Sem teto — veja {@link clampListSize}.
47
+ */
48
+ export function normalizeListSize(size, fallback = ADMIN_LIST_DEFAULT_SIZE) {
49
+ const n = Math.trunc(Number(size));
50
+ return Number.isFinite(n) && n >= 1 ? n : Math.max(1, fallback);
51
+ }
52
+ /**
53
+ * Idem {@link normalizeListSize}, mas limitado a {@link ADMIN_LIST_MAX_SIZE}.
54
+ * Use em TODA borda que recebe `size` de fora do processo.
55
+ */
56
+ export function clampListSize(size, fallback = ADMIN_LIST_DEFAULT_SIZE) {
57
+ return Math.min(ADMIN_LIST_MAX_SIZE, normalizeListSize(size, fallback));
58
+ }
59
+ /** {@link resolveListPage} a partir do valor cru de um query param. */
60
+ export function parseListPage(raw) {
61
+ return resolveListPage(Number.parseInt(String(raw ?? ''), 10));
62
+ }
63
+ /** {@link clampListSize} a partir do valor cru de um query param. */
64
+ export function parseListSize(raw, fallback = ADMIN_LIST_HTTP_DEFAULT_SIZE) {
65
+ return clampListSize(Number.parseInt(String(raw ?? ''), 10), fallback);
66
+ }
@@ -0,0 +1,47 @@
1
+ {{{
2
+ exports({
3
+ to: app.makePath(migration.folder, entity.path, migration.fileName)
4
+ })
5
+ }}}
6
+ import { BaseSchema } from '@adonisjs/lucid/schema'
7
+
8
+ export default class extends BaseSchema {
9
+ protected tableName = 'auth_users'
10
+
11
+ async up() {
12
+ this.schema.createTable(this.tableName, (table) => {
13
+ /**
14
+ * String, NÃO increments(): o hook `@beforeCreate` do model scaffoldado
15
+ * (`models/auth_user.stub`) atribui um `randomUUID()` antes do insert.
16
+ * Uma coluna auto-increment aceitaria o INSERT mesmo assim, mas o valor
17
+ * do UUID seria descartado e o `id` do model voltaria como o rowid
18
+ * interno do banco — a conta ficaria inalcançável pelo id real na
19
+ * próxima request. Ver packages/authkit-server/stubs/models/auth_user.stub.
20
+ */
21
+ table.string('id').notNullable().primary()
22
+
23
+ // withAuthUser() — packages/authkit-server/src/mixins/with_auth_user.ts
24
+ table.string('email').notNullable().unique()
25
+ table.string('password').notNullable()
26
+ // Serializado como JSON string ("[]" quando vazio) pelo mixin.
27
+ table.json('global_roles').notNullable().defaultTo('[]')
28
+
29
+ // withCredentials() — packages/authkit-server/src/mixins/with_credentials.ts
30
+ table.timestamp('email_verified_at', { useTz: true }).nullable()
31
+ table.string('email_verification_token').nullable()
32
+ table.string('password_reset_token').nullable()
33
+ table.timestamp('password_reset_expires_at', { useTz: true }).nullable()
34
+
35
+ // Coluna própria do model scaffoldado (não vem de nenhum mixin): a tela
36
+ // de signup embutida sempre coleta "Nome" e o Lucid store passa esse
37
+ // valor direto para `AuthUser.create()` — sem esta coluna o primeiro
38
+ // signup quebra com "Cannot define 'fullName' on 'AuthUser' model,
39
+ // since it is not defined as a model property".
40
+ table.string('full_name').nullable()
41
+ })
42
+ }
43
+
44
+ async down() {
45
+ this.schema.dropTable(this.tableName)
46
+ }
47
+ }
@@ -1,7 +1,8 @@
1
1
  {{{
2
2
  exports({ to: app.makePath('app/models/auth_user.ts') })
3
3
  }}}
4
- import { BaseModel, column } from '@adonisjs/lucid/orm'
4
+ import { randomUUID } from 'node:crypto'
5
+ import { BaseModel, beforeCreate, column } from '@adonisjs/lucid/orm'
5
6
  import { compose } from '@adonisjs/core/helpers'
6
7
  import { withAuthUser, withCredentials } from '@adonis-agora/authkit-server'
7
8
 
@@ -17,6 +18,34 @@ import { withAuthUser, withCredentials } from '@adonis-agora/authkit-server'
17
18
  * (e aponte as migrations correspondentes para essa conexão).
18
19
  */
19
20
  export default class AuthUser extends compose(BaseModel, withAuthUser(), withCredentials()) {
21
+ /**
22
+ * Sem isto, o Lucid ignora o id atribuído pelo hook abaixo e sobrescreve
23
+ * `id` com o retorno bruto do INSERT (o rowid interno do SQLite/Postgres)
24
+ * assim que a linha é salva — mesmo resultado prático de não ter o hook: a
25
+ * conta some do próprio id que acabou de criar.
26
+ */
27
+ static selfAssignPrimaryKey = true
28
+
20
29
  @column({ isPrimary: true })
21
30
  declare id: string
31
+
32
+ /**
33
+ * Nem withAuthUser() nem withCredentials() geram o id — sem este hook o
34
+ * Lucid insere NULL nesta coluna string e o `id` do model volta como o
35
+ * rowid interno do banco, tornando a conta inalcançável pelo id real na
36
+ * próxima request.
37
+ */
38
+ @beforeCreate()
39
+ static assignUuid(user: AuthUser) {
40
+ user.id = randomUUID()
41
+ }
42
+
43
+ /**
44
+ * A tela de signup embutida sempre coleta um campo "Nome", e o Lucid store
45
+ * passa esse valor direto para `AuthUser.create()` — sem esta coluna o
46
+ * primeiro signup quebra com "Cannot define 'fullName' on 'AuthUser'
47
+ * model, since it is not defined as a model property".
48
+ */
49
+ @column()
50
+ declare fullName: string | null
22
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/authkit-server",
3
- "version": "0.65.2",
3
+ "version": "0.66.0",
4
4
  "description": "AdonisJS OIDC/OAuth2 provider (Identity Provider) toolkit: ejectable auth server with sessions, rate-limiting, MFA/TOTP, audit log, federated logout and OpenTelemetry metrics.",
5
5
  "license": "MIT",
6
6
  "author": "dudousxd",
@@ -103,19 +103,19 @@
103
103
  },
104
104
  "dependencies": {
105
105
  "@adonis-agora/authkit-core": "0.8.0",
106
- "@simplewebauthn/browser": "13.3.0",
107
- "@simplewebauthn/server": "13.3.3",
108
- "jose": "6.2.10",
106
+ "@simplewebauthn/browser": "14.0.0",
107
+ "@simplewebauthn/server": "14.0.1",
108
+ "jose": "6.2.12",
109
109
  "koa": "3.2.1",
110
110
  "koa-mount": "4.2.0",
111
- "oidc-provider": "9.12.0",
111
+ "oidc-provider": "9.12.2",
112
112
  "otplib": "13.5.0",
113
113
  "qrcode": "1.5.4"
114
114
  },
115
115
  "devDependencies": {
116
- "@adonis-agora/authkit-react": "0.20.1",
117
- "@adonis-agora/durable": "0.26.0",
118
- "@adonis-agora/telescope": "0.9.1",
116
+ "@adonis-agora/authkit-react": "0.21.0",
117
+ "@adonis-agora/durable": "0.38.3",
118
+ "@adonis-agora/telescope": "0.20.0",
119
119
  "@adonisjs/ally": "6.3.0",
120
120
  "@adonisjs/auth": "10.1.0",
121
121
  "@adonisjs/core": "7.5.0",
@@ -128,17 +128,17 @@
128
128
  "@japa/assert": "4.2.0",
129
129
  "@japa/runner": "5.3.0",
130
130
  "@poppinss/ts-exec": "1.4.4",
131
- "@tanstack/intent": "^0.3.8",
131
+ "@tanstack/intent": "^0.4.0",
132
132
  "@tanstack/react-query": "5.102.8",
133
133
  "@types/better-sqlite3": "9.6.0",
134
134
  "@types/ioredis-mock": "8.2.8",
135
135
  "@types/koa": "3.0.3",
136
136
  "@types/koa-mount": "4.0.5",
137
137
  "@types/luxon": "3.7.5",
138
- "@types/node": "26.4.0",
138
+ "@types/node": "26.5.0",
139
139
  "@types/qrcode": "1.5.6",
140
140
  "@types/react": "19.2.18",
141
- "@types/react-dom": "19.2.5",
141
+ "@types/react-dom": "19.2.7",
142
142
  "@vinejs/vine": "4.4.0",
143
143
  "@vitejs/plugin-react": "6.1.1",
144
144
  "better-sqlite3": "13.0.3",
@@ -150,7 +150,7 @@
150
150
  "nuqs": "2.10.1",
151
151
  "react": "19.2.8",
152
152
  "react-dom": "19.2.8",
153
- "react-error-boundary": "6.1.4",
153
+ "react-error-boundary": "6.1.5",
154
154
  "recharts": "3.10.1",
155
155
  "tailwindcss": "3.4.19",
156
156
  "typescript": "7.0.2",
@@ -0,0 +1,47 @@
1
+ {{{
2
+ exports({
3
+ to: app.makePath(migration.folder, entity.path, migration.fileName)
4
+ })
5
+ }}}
6
+ import { BaseSchema } from '@adonisjs/lucid/schema'
7
+
8
+ export default class extends BaseSchema {
9
+ protected tableName = 'auth_users'
10
+
11
+ async up() {
12
+ this.schema.createTable(this.tableName, (table) => {
13
+ /**
14
+ * String, NÃO increments(): o hook `@beforeCreate` do model scaffoldado
15
+ * (`models/auth_user.stub`) atribui um `randomUUID()` antes do insert.
16
+ * Uma coluna auto-increment aceitaria o INSERT mesmo assim, mas o valor
17
+ * do UUID seria descartado e o `id` do model voltaria como o rowid
18
+ * interno do banco — a conta ficaria inalcançável pelo id real na
19
+ * próxima request. Ver packages/authkit-server/stubs/models/auth_user.stub.
20
+ */
21
+ table.string('id').notNullable().primary()
22
+
23
+ // withAuthUser() — packages/authkit-server/src/mixins/with_auth_user.ts
24
+ table.string('email').notNullable().unique()
25
+ table.string('password').notNullable()
26
+ // Serializado como JSON string ("[]" quando vazio) pelo mixin.
27
+ table.json('global_roles').notNullable().defaultTo('[]')
28
+
29
+ // withCredentials() — packages/authkit-server/src/mixins/with_credentials.ts
30
+ table.timestamp('email_verified_at', { useTz: true }).nullable()
31
+ table.string('email_verification_token').nullable()
32
+ table.string('password_reset_token').nullable()
33
+ table.timestamp('password_reset_expires_at', { useTz: true }).nullable()
34
+
35
+ // Coluna própria do model scaffoldado (não vem de nenhum mixin): a tela
36
+ // de signup embutida sempre coleta "Nome" e o Lucid store passa esse
37
+ // valor direto para `AuthUser.create()` — sem esta coluna o primeiro
38
+ // signup quebra com "Cannot define 'fullName' on 'AuthUser' model,
39
+ // since it is not defined as a model property".
40
+ table.string('full_name').nullable()
41
+ })
42
+ }
43
+
44
+ async down() {
45
+ this.schema.dropTable(this.tableName)
46
+ }
47
+ }
@@ -1,7 +1,8 @@
1
1
  {{{
2
2
  exports({ to: app.makePath('app/models/auth_user.ts') })
3
3
  }}}
4
- import { BaseModel, column } from '@adonisjs/lucid/orm'
4
+ import { randomUUID } from 'node:crypto'
5
+ import { BaseModel, beforeCreate, column } from '@adonisjs/lucid/orm'
5
6
  import { compose } from '@adonisjs/core/helpers'
6
7
  import { withAuthUser, withCredentials } from '@adonis-agora/authkit-server'
7
8
 
@@ -17,6 +18,34 @@ import { withAuthUser, withCredentials } from '@adonis-agora/authkit-server'
17
18
  * (e aponte as migrations correspondentes para essa conexão).
18
19
  */
19
20
  export default class AuthUser extends compose(BaseModel, withAuthUser(), withCredentials()) {
21
+ /**
22
+ * Sem isto, o Lucid ignora o id atribuído pelo hook abaixo e sobrescreve
23
+ * `id` com o retorno bruto do INSERT (o rowid interno do SQLite/Postgres)
24
+ * assim que a linha é salva — mesmo resultado prático de não ter o hook: a
25
+ * conta some do próprio id que acabou de criar.
26
+ */
27
+ static selfAssignPrimaryKey = true
28
+
20
29
  @column({ isPrimary: true })
21
30
  declare id: string
31
+
32
+ /**
33
+ * Nem withAuthUser() nem withCredentials() geram o id — sem este hook o
34
+ * Lucid insere NULL nesta coluna string e o `id` do model volta como o
35
+ * rowid interno do banco, tornando a conta inalcançável pelo id real na
36
+ * próxima request.
37
+ */
38
+ @beforeCreate()
39
+ static assignUuid(user: AuthUser) {
40
+ user.id = randomUUID()
41
+ }
42
+
43
+ /**
44
+ * A tela de signup embutida sempre coleta um campo "Nome", e o Lucid store
45
+ * passa esse valor direto para `AuthUser.create()` — sem esta coluna o
46
+ * primeiro signup quebra com "Cannot define 'fullName' on 'AuthUser'
47
+ * model, since it is not defined as a model property".
48
+ */
49
+ @column()
50
+ declare fullName: string | null
22
51
  }