hazo_auth 7.0.2 → 8.0.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.
- package/README.md +34 -0
- package/SETUP_CHECKLIST.md +31 -0
- package/cli-src/lib/auth/auth_types.ts +3 -0
- package/cli-src/lib/auth/hazo_get_auth.server.ts +19 -0
- package/cli-src/lib/legal/legal_docs_config.server.ts +61 -0
- package/cli-src/lib/legal/legal_docs_reader.server.ts +36 -0
- package/cli-src/lib/legal/legal_docs_service.ts +196 -0
- package/cli-src/lib/legal/legal_docs_types.ts +31 -0
- package/cli-src/lib/services/registration_service.ts +16 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -0
- package/dist/components/layouts/index.d.ts +1 -0
- package/dist/components/layouts/index.d.ts.map +1 -1
- package/dist/components/layouts/index.js +2 -0
- package/dist/components/layouts/legal/index.d.ts +5 -0
- package/dist/components/layouts/legal/index.d.ts.map +1 -0
- package/dist/components/layouts/legal/index.js +4 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.d.ts +7 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.js +84 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts +9 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.js +11 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.d.ts +9 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.js +11 -0
- package/dist/components/layouts/legal/legal_doc_drawer.d.ts +8 -0
- package/dist/components/layouts/legal/legal_doc_drawer.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_drawer.js +55 -0
- package/dist/components/layouts/register/hooks/use_register_form.d.ts +5 -1
- package/dist/components/layouts/register/hooks/use_register_form.d.ts.map +1 -1
- package/dist/components/layouts/register/hooks/use_register_form.js +25 -10
- package/dist/components/layouts/register/index.d.ts.map +1 -1
- package/dist/components/layouts/register/index.js +21 -1
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +45 -7
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/input-otp.d.ts +2 -2
- package/dist/components/ui/sheet.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.d.ts +2 -0
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +0 -2
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +19 -0
- package/dist/lib/legal/legal_docs_config.server.d.ts +22 -0
- package/dist/lib/legal/legal_docs_config.server.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_config.server.js +52 -0
- package/dist/lib/legal/legal_docs_reader.server.d.ts +15 -0
- package/dist/lib/legal/legal_docs_reader.server.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_reader.server.js +24 -0
- package/dist/lib/legal/legal_docs_service.d.ts +49 -0
- package/dist/lib/legal/legal_docs_service.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_service.js +140 -0
- package/dist/lib/legal/legal_docs_types.d.ts +25 -0
- package/dist/lib/legal/legal_docs_types.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_types.js +2 -0
- package/dist/lib/services/registration_service.d.ts +5 -0
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +6 -0
- package/dist/page_components/index.d.ts +0 -5
- package/dist/page_components/index.d.ts.map +1 -1
- package/dist/page_components/index.js +0 -5
- package/dist/server/routes/index.d.ts +3 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +4 -0
- package/dist/server/routes/legal_docs_accept.d.ts +3 -0
- package/dist/server/routes/legal_docs_accept.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_accept.js +43 -0
- package/dist/server/routes/legal_docs_get.d.ts +3 -0
- package/dist/server/routes/legal_docs_get.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_get.js +49 -0
- package/dist/server/routes/legal_docs_publish.d.ts +3 -0
- package/dist/server/routes/legal_docs_publish.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_publish.js +35 -0
- package/dist/server/routes/register.d.ts.map +1 -1
- package/dist/server/routes/register.js +26 -0
- package/dist/server/routes/user_management_users.d.ts +2 -2
- package/dist/server/routes/user_management_users.d.ts.map +1 -1
- package/dist/server/routes/user_management_users.js +46 -2
- package/dist/strings.d.ts +2 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +3 -0
- package/package.json +5 -22
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// file_description: POST /api/hazo_auth/legal_docs/publish — admin endpoint to publish a new required version of a legal doc
|
|
2
|
+
import { NextResponse } from 'next/server';
|
|
3
|
+
import { hazo_get_auth } from '../../lib/auth/hazo_get_auth.server.js';
|
|
4
|
+
import { get_legal_docs_config } from '../../lib/legal/legal_docs_config.server.js';
|
|
5
|
+
import { read_legal_doc } from '../../lib/legal/legal_docs_reader.server.js';
|
|
6
|
+
import { publish_doc_version } from '../../lib/legal/legal_docs_service.js';
|
|
7
|
+
import { get_hazo_connect_instance } from '../../lib/hazo_connect_instance.server.js';
|
|
8
|
+
import { create_app_logger } from '../../lib/app_logger.js';
|
|
9
|
+
export async function legalDocsPublishPOST(request) {
|
|
10
|
+
const logger = create_app_logger();
|
|
11
|
+
try {
|
|
12
|
+
const auth = await hazo_get_auth(request, { required_permissions: ['admin_user_management'] });
|
|
13
|
+
if (!auth.permission_ok) {
|
|
14
|
+
return NextResponse.json({ ok: false, error: 'Forbidden' }, { status: 403 });
|
|
15
|
+
}
|
|
16
|
+
const body = await request.json().catch(() => null);
|
|
17
|
+
const doc_key = body === null || body === void 0 ? void 0 : body.doc_key;
|
|
18
|
+
if (!doc_key || typeof doc_key !== 'string') {
|
|
19
|
+
return NextResponse.json({ ok: false, error: 'doc_key is required' }, { status: 400 });
|
|
20
|
+
}
|
|
21
|
+
const config = get_legal_docs_config();
|
|
22
|
+
const doc_config = config.docs.find(d => d.key === doc_key);
|
|
23
|
+
if (!doc_config) {
|
|
24
|
+
return NextResponse.json({ ok: false, error: `Unknown doc key: ${doc_key}` }, { status: 400 });
|
|
25
|
+
}
|
|
26
|
+
const { hash } = read_legal_doc(doc_config.path);
|
|
27
|
+
const adapter = get_hazo_connect_instance();
|
|
28
|
+
const { published_at } = await publish_doc_version(adapter, doc_key, hash, auth.user.id);
|
|
29
|
+
return NextResponse.json({ ok: true, data: { doc_key, required_hash: hash, published_at } });
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
logger.error('legal_docs_publish: internal server error', { err });
|
|
33
|
+
return NextResponse.json({ ok: false, error: 'Internal server error' }, { status: 500 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/routes/register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/routes/register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAgI9C"}
|
|
@@ -6,12 +6,17 @@ import { create_app_logger } from "../../lib/app_logger.js";
|
|
|
6
6
|
import { register_user } from "../../lib/services/registration_service.js";
|
|
7
7
|
import { get_filename, get_line_number } from "../../lib/utils/api_route_helpers.js";
|
|
8
8
|
import { sanitize_error_for_user } from "../../lib/utils/error_sanitizer.js";
|
|
9
|
+
import { get_legal_docs_config } from "../../lib/legal/legal_docs_config.server.js";
|
|
10
|
+
import { read_legal_doc } from "../../lib/legal/legal_docs_reader.server.js";
|
|
11
|
+
import { get_client_ip } from "../../lib/auth/hazo_get_auth.server.js";
|
|
9
12
|
// section: api_handler
|
|
10
13
|
export async function POST(request) {
|
|
14
|
+
var _a;
|
|
11
15
|
const logger = create_app_logger();
|
|
12
16
|
try {
|
|
13
17
|
const body = await request.json();
|
|
14
18
|
const { name, email, password, url_on_logon } = body;
|
|
19
|
+
const legal_accepted = body === null || body === void 0 ? void 0 : body.legal_accepted;
|
|
15
20
|
// Validate input
|
|
16
21
|
if (!email || !password) {
|
|
17
22
|
logger.warn("registration_validation_failed", {
|
|
@@ -32,6 +37,24 @@ export async function POST(request) {
|
|
|
32
37
|
});
|
|
33
38
|
return NextResponse.json({ error: "Invalid email address format" }, { status: 400 });
|
|
34
39
|
}
|
|
40
|
+
// Validate legal acceptance if docs are configured
|
|
41
|
+
const legal_config = get_legal_docs_config();
|
|
42
|
+
if (legal_config.docs.length > 0) {
|
|
43
|
+
const missing_keys = legal_config.docs
|
|
44
|
+
.map(d => d.key)
|
|
45
|
+
.filter(key => !(legal_accepted === null || legal_accepted === void 0 ? void 0 : legal_accepted[key]));
|
|
46
|
+
if (missing_keys.length > 0) {
|
|
47
|
+
return NextResponse.json({ ok: false, error: 'legal_acceptance_required', missing_keys }, { status: 400 });
|
|
48
|
+
}
|
|
49
|
+
// Validate hashes match current file content
|
|
50
|
+
for (const doc_config of legal_config.docs) {
|
|
51
|
+
const submitted_hash = (_a = legal_accepted[doc_config.key]) === null || _a === void 0 ? void 0 : _a.hash;
|
|
52
|
+
const { hash: current_hash } = read_legal_doc(doc_config.path);
|
|
53
|
+
if (submitted_hash !== current_hash) {
|
|
54
|
+
return NextResponse.json({ ok: false, error: `Hash mismatch for "${doc_config.key}"` }, { status: 400 });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
35
58
|
// Get singleton hazo_connect instance (reuses same connection across all routes)
|
|
36
59
|
const hazoConnect = get_hazo_connect_instance();
|
|
37
60
|
// Register user using the registration service
|
|
@@ -40,6 +63,9 @@ export async function POST(request) {
|
|
|
40
63
|
password,
|
|
41
64
|
name,
|
|
42
65
|
url_on_logon,
|
|
66
|
+
legal_accepted,
|
|
67
|
+
ip: get_client_ip(request),
|
|
68
|
+
user_agent: request.headers.get('user-agent'),
|
|
43
69
|
});
|
|
44
70
|
if (!result.success) {
|
|
45
71
|
const status_code = result.error === "Email address already registered" ? 409 : 500;
|
|
@@ -26,6 +26,7 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
|
|
|
26
26
|
profile_source: {} | null;
|
|
27
27
|
user_type: string | null;
|
|
28
28
|
app_user_data: Record<string, unknown> | null;
|
|
29
|
+
legal_acceptance_status: "current" | "outdated" | "none";
|
|
29
30
|
}[];
|
|
30
31
|
}>>;
|
|
31
32
|
/**
|
|
@@ -47,8 +48,7 @@ export declare function POST(request: NextRequest): Promise<NextResponse<{
|
|
|
47
48
|
/**
|
|
48
49
|
* DELETE - Hard-delete a user from hazo_users (cascades to all related rows).
|
|
49
50
|
* Body: { user_id: string }
|
|
50
|
-
* Requires: admin_user_management permission
|
|
51
|
-
* pattern as PATCH/POST in this file which also don't re-auth).
|
|
51
|
+
* Requires: admin_user_management permission.
|
|
52
52
|
*/
|
|
53
53
|
export declare function DELETE(request: NextRequest): Promise<NextResponse<{
|
|
54
54
|
error: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user_management_users.d.ts","sourceRoot":"","sources":["../../../src/server/routes/user_management_users.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"user_management_users.d.ts","sourceRoot":"","sources":["../../../src/server/routes/user_management_users.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkBxD,eAAO,MAAM,OAAO,kBAAkB,CAAC;AA+BvC;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;IAsG7C;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,WAAW;;;;IAgI/C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;IA2E9C;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,WAAW;;;;IAmEhD"}
|
|
@@ -9,8 +9,35 @@ import { request_password_reset } from "../../lib/services/password_reset_servic
|
|
|
9
9
|
import { get_auth_cache } from "../../lib/auth/auth_cache.js";
|
|
10
10
|
import { get_auth_utility_config } from "../../lib/auth_utility_config.server.js";
|
|
11
11
|
import { is_user_types_enabled, get_all_user_types, get_user_types_config, } from "../../lib/user_types_config.server.js";
|
|
12
|
+
import { get_required_versions } from "../../lib/legal/legal_docs_service.js";
|
|
13
|
+
import { get_legal_docs_config } from "../../lib/legal/legal_docs_config.server.js";
|
|
14
|
+
import { hazo_get_auth } from "../../lib/auth/hazo_get_auth.server.js";
|
|
12
15
|
// section: route_config
|
|
13
16
|
export const dynamic = 'force-dynamic';
|
|
17
|
+
// section: helpers
|
|
18
|
+
/**
|
|
19
|
+
* Compute a user's legal compliance status given their raw legal_acceptance JSON,
|
|
20
|
+
* the currently-required hashes, and the configured doc keys.
|
|
21
|
+
*/
|
|
22
|
+
function compute_legal_status(raw, required, docs) {
|
|
23
|
+
if (docs.length === 0)
|
|
24
|
+
return 'none';
|
|
25
|
+
if (Object.keys(required).length === 0)
|
|
26
|
+
return 'none';
|
|
27
|
+
let map = {};
|
|
28
|
+
try {
|
|
29
|
+
map = typeof raw === 'string' ? JSON.parse(raw) : (raw !== null && raw !== void 0 ? raw : {});
|
|
30
|
+
}
|
|
31
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
32
|
+
const all_current = docs.every(doc => {
|
|
33
|
+
var _a;
|
|
34
|
+
const req = required[doc.key];
|
|
35
|
+
if (!req)
|
|
36
|
+
return true;
|
|
37
|
+
return ((_a = map[doc.key]) === null || _a === void 0 ? void 0 : _a.hash) === req.required_hash;
|
|
38
|
+
});
|
|
39
|
+
return all_current ? 'current' : 'outdated';
|
|
40
|
+
}
|
|
14
41
|
// section: api_handler
|
|
15
42
|
/**
|
|
16
43
|
* GET - Fetch all users with details or a specific user by id
|
|
@@ -42,6 +69,13 @@ export async function GET(request) {
|
|
|
42
69
|
badge_color: t.badge_color,
|
|
43
70
|
}))
|
|
44
71
|
: [];
|
|
72
|
+
// Load legal docs required versions for compliance status
|
|
73
|
+
const legal_config = get_legal_docs_config();
|
|
74
|
+
let required_versions = {};
|
|
75
|
+
if (legal_config.docs.length > 0) {
|
|
76
|
+
const adapter = get_hazo_connect_instance();
|
|
77
|
+
required_versions = await get_required_versions(adapter, legal_config.docs.map(d => d.key));
|
|
78
|
+
}
|
|
45
79
|
return NextResponse.json({
|
|
46
80
|
success: true,
|
|
47
81
|
user_types_enabled,
|
|
@@ -74,6 +108,7 @@ export async function GET(request) {
|
|
|
74
108
|
profile_source: user.profile_source || null,
|
|
75
109
|
user_type: user.user_type || null,
|
|
76
110
|
app_user_data,
|
|
111
|
+
legal_acceptance_status: compute_legal_status(user.legal_acceptance, required_versions, legal_config.docs),
|
|
77
112
|
};
|
|
78
113
|
}),
|
|
79
114
|
}, { status: 200 });
|
|
@@ -244,14 +279,23 @@ export async function POST(request) {
|
|
|
244
279
|
/**
|
|
245
280
|
* DELETE - Hard-delete a user from hazo_users (cascades to all related rows).
|
|
246
281
|
* Body: { user_id: string }
|
|
247
|
-
* Requires: admin_user_management permission
|
|
248
|
-
* pattern as PATCH/POST in this file which also don't re-auth).
|
|
282
|
+
* Requires: admin_user_management permission.
|
|
249
283
|
*/
|
|
250
284
|
export async function DELETE(request) {
|
|
251
285
|
const logger = create_app_logger();
|
|
252
286
|
try {
|
|
287
|
+
const auth = await hazo_get_auth(request, { required_permissions: ['admin_user_management'] });
|
|
288
|
+
if (!auth.user) {
|
|
289
|
+
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
|
|
290
|
+
}
|
|
291
|
+
if (!auth.permission_ok) {
|
|
292
|
+
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
|
293
|
+
}
|
|
253
294
|
const body = await request.json();
|
|
254
295
|
const { user_id } = body;
|
|
296
|
+
if (auth.user.id === user_id) {
|
|
297
|
+
return NextResponse.json({ error: 'Cannot delete your own account' }, { status: 400 });
|
|
298
|
+
}
|
|
255
299
|
if (!user_id) {
|
|
256
300
|
return NextResponse.json({ error: "user_id is required" }, { status: 400 });
|
|
257
301
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAEA,cAAc,iBAAiB,CAAC"}
|
package/dist/strings.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_auth",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.1",
|
|
4
4
|
"description": "Zero-config authentication UI components for Next.js with RBAC, OAuth, scope-based multi-tenancy, and invitations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"authentication",
|
|
@@ -157,26 +157,6 @@
|
|
|
157
157
|
"types": "./dist/page_components/index.d.ts",
|
|
158
158
|
"import": "./dist/page_components/index.js"
|
|
159
159
|
},
|
|
160
|
-
"./page_components/login": {
|
|
161
|
-
"types": "./dist/page_components/login.d.ts",
|
|
162
|
-
"import": "./dist/page_components/login.js"
|
|
163
|
-
},
|
|
164
|
-
"./page_components/register": {
|
|
165
|
-
"types": "./dist/page_components/register.d.ts",
|
|
166
|
-
"import": "./dist/page_components/register.js"
|
|
167
|
-
},
|
|
168
|
-
"./page_components/forgot_password": {
|
|
169
|
-
"types": "./dist/page_components/forgot_password.d.ts",
|
|
170
|
-
"import": "./dist/page_components/forgot_password.js"
|
|
171
|
-
},
|
|
172
|
-
"./page_components/reset_password": {
|
|
173
|
-
"types": "./dist/page_components/reset_password.d.ts",
|
|
174
|
-
"import": "./dist/page_components/reset_password.js"
|
|
175
|
-
},
|
|
176
|
-
"./page_components/verify_email": {
|
|
177
|
-
"types": "./dist/page_components/verify_email.d.ts",
|
|
178
|
-
"import": "./dist/page_components/verify_email.js"
|
|
179
|
-
},
|
|
180
160
|
"./page_components/dev_lock": {
|
|
181
161
|
"types": "./dist/page_components/dev_lock.d.ts",
|
|
182
162
|
"import": "./dist/page_components/dev_lock.js"
|
|
@@ -198,6 +178,8 @@
|
|
|
198
178
|
"!dist/components/layouts/rbac_test/**/*",
|
|
199
179
|
"!dist/components/layouts/auto_test/**/*",
|
|
200
180
|
"!dist/components/layouts/test_scenarios/**/*",
|
|
181
|
+
"!dist/components/layouts/legal_docs_test/**/*",
|
|
182
|
+
"!dist/components/layouts/legal_register_test/**/*",
|
|
201
183
|
"!dist/lib/auto_test/**/*",
|
|
202
184
|
"bin/**/*",
|
|
203
185
|
"cli-src/**/*",
|
|
@@ -238,6 +220,7 @@
|
|
|
238
220
|
"jose": "^5.9.6",
|
|
239
221
|
"jsonwebtoken": "^9.0.2",
|
|
240
222
|
"mime-types": "^3.0.1",
|
|
223
|
+
"react-markdown": "^10.1.0",
|
|
241
224
|
"server-only": "^0.0.1",
|
|
242
225
|
"tailwind-merge": "^3.5.0",
|
|
243
226
|
"tw-animate-css": "^1.4.0",
|
|
@@ -371,6 +354,7 @@
|
|
|
371
354
|
"@storybook/addon-onboarding": "^10.0.6",
|
|
372
355
|
"@storybook/addon-vitest": "^10.0.6",
|
|
373
356
|
"@storybook/nextjs": "^10.0.6",
|
|
357
|
+
"@tailwindcss/postcss": "^4.2.4",
|
|
374
358
|
"@testing-library/dom": "^10.4.1",
|
|
375
359
|
"@testing-library/jest-dom": "^6.6.3",
|
|
376
360
|
"@testing-library/react": "^16.0.1",
|
|
@@ -392,7 +376,6 @@
|
|
|
392
376
|
"eslint": "^9.39.1",
|
|
393
377
|
"eslint-config-next": "^16.0.4",
|
|
394
378
|
"eslint-plugin-storybook": "^10.0.6",
|
|
395
|
-
"@tailwindcss/postcss": "^4.2.4",
|
|
396
379
|
"hazo_config": "^2.1.0",
|
|
397
380
|
"hazo_connect": "^2.4.0",
|
|
398
381
|
"hazo_logs": "^1.0.13",
|