hazo_admin 0.5.0 → 0.6.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.
- package/CHANGE_LOG.md +14 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/issues/index.d.ts +1 -1
- package/dist/issues/index.d.ts.map +1 -1
- package/dist/issues/index.js +1 -1
- package/dist/issues/store.d.ts +11 -0
- package/dist/issues/store.d.ts.map +1 -1
- package/dist/issues/store.js +175 -1
- package/package.json +5 -5
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# hazo_admin Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.0] - 2026-06-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`createIssueStoreFromConnect(adapter)`** — PostgREST-native `IssueStore` implementation that uses `createCrudService` + `adapter.claimRows` instead of raw SQL. Enables the issue store to operate over PostgREST without a direct DB connection.
|
|
7
|
+
- Capability sniff: if the adapter exposes a `.raw()` method (direct-DB adapter), delegates to the existing `createIssueStore` transparently.
|
|
8
|
+
- `createOrBumpIssue`: atomic CAS bump via `claimRows`; falls back to `crud.insert` on no-match.
|
|
9
|
+
- `listIssues`: uses `crud.query()` builder with `.where()` / `.order()` / `.limit()` / `.offset()`.
|
|
10
|
+
- `getIssue`: `crud.findById`.
|
|
11
|
+
- `transitionStatus` / `setAssignee` / `resolveIssue`: `crud.updateById` with the same legal-transition guard as `createIssueStore`.
|
|
12
|
+
- `archiveClosedOlderThan`: batch `claimRows` with `status: closed` + `resolved_at: { lte: cutoff }`.
|
|
13
|
+
- Lazily imports `hazo_connect/server` (avoids loading the full hazo_connect stack for raw adapters in test environments).
|
|
14
|
+
- Exported from `hazo_admin`, `hazo_admin/issues`.
|
|
15
|
+
- 20 new Jest tests in `src/issues/__tests__/store_connect.test.ts`.
|
|
16
|
+
|
|
3
17
|
## [0.5.0] - 2026-06-19
|
|
4
18
|
|
|
5
19
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { adminGate, withAdminGate } from './lib/admin_gate.server.js';
|
|
2
2
|
export type { AdminGateResult } from './lib/admin_gate.server.js';
|
|
3
3
|
export { HAZO_ADMIN_PERMISSIONS } from './index.client.js';
|
|
4
|
-
export { createIssueStore, registerIssueType, getIssueType, listIssueTypes, loadIssueRoutingConfig, getRecipientStrategy, issueArchiveJobHandler, registerIssueArchiveJob, ADMIN_ISSUE_ARCHIVE_JOB_TYPE, } from './issues/index.js';
|
|
4
|
+
export { createIssueStore, createIssueStoreFromConnect, registerIssueType, getIssueType, listIssueTypes, loadIssueRoutingConfig, getRecipientStrategy, issueArchiveJobHandler, registerIssueArchiveJob, ADMIN_ISSUE_ARCHIVE_JOB_TYPE, } from './issues/index.js';
|
|
5
5
|
export type { IssueRecord, IssueStore, CreateIssueInput, IssueTypeDef, IssueActionDef, IssueActionCtx, IssueActionResult, RecipientResult, NotifyPayload, IssueRoutingConfig, RecipientStrategy, } from './issues/index.js';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { adminGate, withAdminGate } from './lib/admin_gate.server.js';
|
|
2
2
|
export { HAZO_ADMIN_PERMISSIONS } from './index.client.js';
|
|
3
|
-
export { createIssueStore, registerIssueType, getIssueType, listIssueTypes, loadIssueRoutingConfig, getRecipientStrategy, issueArchiveJobHandler, registerIssueArchiveJob, ADMIN_ISSUE_ARCHIVE_JOB_TYPE, } from './issues/index.js';
|
|
3
|
+
export { createIssueStore, createIssueStoreFromConnect, registerIssueType, getIssueType, listIssueTypes, loadIssueRoutingConfig, getRecipientStrategy, issueArchiveJobHandler, registerIssueArchiveJob, ADMIN_ISSUE_ARCHIVE_JOB_TYPE, } from './issues/index.js';
|
package/dist/issues/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createIssueStore } from './store.js';
|
|
1
|
+
export { createIssueStore, createIssueStoreFromConnect } from './store.js';
|
|
2
2
|
export type { IssueRecord, IssueStore, CreateIssueInput } from './store.js';
|
|
3
3
|
export { registerIssueType, getIssueType, listIssueTypes } from './registry.js';
|
|
4
4
|
export type { IssueTypeDef, IssueActionDef, IssueActionCtx, IssueActionResult, RecipientResult, NotifyPayload, } from './registry.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/issues/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/issues/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChF,YAAY,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC"}
|
package/dist/issues/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createIssueStore } from './store.js';
|
|
1
|
+
export { createIssueStore, createIssueStoreFromConnect } from './store.js';
|
|
2
2
|
export { registerIssueType, getIssueType, listIssueTypes } from './registry.js';
|
|
3
3
|
export { loadIssueRoutingConfig, getRecipientStrategy } from './routing.js';
|
|
4
4
|
export { issueArchiveJobHandler, registerIssueArchiveJob, ADMIN_ISSUE_ARCHIVE_JOB_TYPE, } from './archive_handler.js';
|
package/dist/issues/store.d.ts
CHANGED
|
@@ -57,5 +57,16 @@ export interface IssueStore {
|
|
|
57
57
|
}): Promise<IssueRecord>;
|
|
58
58
|
archiveClosedOlderThan(cutoffDate: Date): Promise<number>;
|
|
59
59
|
}
|
|
60
|
+
export declare function toIssueRecord(raw: Record<string, unknown>): IssueRecord;
|
|
60
61
|
export declare function createIssueStore(adapter: any): IssueStore;
|
|
62
|
+
/**
|
|
63
|
+
* PostgREST-native implementation of IssueStore.
|
|
64
|
+
* Uses createCrudService + adapter.claimRows so no raw SQL is issued.
|
|
65
|
+
* Falls back to createIssueStore when the adapter exposes a raw() method
|
|
66
|
+
* (i.e. it is a direct-DB adapter that already handles SQL).
|
|
67
|
+
*
|
|
68
|
+
* Returns a Promise because it lazily imports hazo_connect/server on the
|
|
69
|
+
* CRUD path (avoiding loading the full hazo_connect stack for raw adapters).
|
|
70
|
+
*/
|
|
71
|
+
export declare function createIssueStoreFromConnect(adapter: any): Promise<IssueStore>;
|
|
61
72
|
//# sourceMappingURL=store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/issues/store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/issues/store.ts"],"names":[],"mappings":"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"}
|
package/dist/issues/store.js
CHANGED
|
@@ -2,7 +2,7 @@ import 'server-only';
|
|
|
2
2
|
import { createLogger } from 'hazo_core';
|
|
3
3
|
const log = createLogger('hazo_admin:issues');
|
|
4
4
|
const TABLE = 'hazo_admin_issues';
|
|
5
|
-
function toIssueRecord(raw) {
|
|
5
|
+
export function toIssueRecord(raw) {
|
|
6
6
|
return {
|
|
7
7
|
id: raw.id,
|
|
8
8
|
scope_id: raw.scope_id,
|
|
@@ -225,3 +225,177 @@ export function createIssueStore(adapter) {
|
|
|
225
225
|
archiveClosedOlderThan,
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* PostgREST-native implementation of IssueStore.
|
|
230
|
+
* Uses createCrudService + adapter.claimRows so no raw SQL is issued.
|
|
231
|
+
* Falls back to createIssueStore when the adapter exposes a raw() method
|
|
232
|
+
* (i.e. it is a direct-DB adapter that already handles SQL).
|
|
233
|
+
*
|
|
234
|
+
* Returns a Promise because it lazily imports hazo_connect/server on the
|
|
235
|
+
* CRUD path (avoiding loading the full hazo_connect stack for raw adapters).
|
|
236
|
+
*/
|
|
237
|
+
export async function createIssueStoreFromConnect(adapter) {
|
|
238
|
+
// Capability sniff: direct-DB adapters have a raw() method — use the raw store.
|
|
239
|
+
if (typeof adapter.raw === 'function') {
|
|
240
|
+
return createIssueStore(adapter);
|
|
241
|
+
}
|
|
242
|
+
// Lazy import to avoid pulling hazo_connect into server-only environments
|
|
243
|
+
// that don't have it installed (e.g., direct-SQL test adapters).
|
|
244
|
+
const { createCrudService } = await import('hazo_connect/server');
|
|
245
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
246
|
+
const crud = createCrudService(adapter, TABLE);
|
|
247
|
+
function now() {
|
|
248
|
+
return new Date().toISOString();
|
|
249
|
+
}
|
|
250
|
+
async function createOrBumpIssue(input) {
|
|
251
|
+
log.debug('createIssueStoreFromConnect:createOrBumpIssue', { dedupe_key: input.dedupe_key });
|
|
252
|
+
// Atomically bump an existing open issue via claimRows.
|
|
253
|
+
const bumped = await adapter.claimRows({
|
|
254
|
+
table: TABLE,
|
|
255
|
+
where: {
|
|
256
|
+
dedupe_key: input.dedupe_key,
|
|
257
|
+
status: { in: ['new', 'wip'] },
|
|
258
|
+
},
|
|
259
|
+
set: {
|
|
260
|
+
occurrence_count: { increment: 1 },
|
|
261
|
+
last_seen_at: { $now: true },
|
|
262
|
+
updated_at: { $now: true },
|
|
263
|
+
},
|
|
264
|
+
returning: ['*'],
|
|
265
|
+
});
|
|
266
|
+
if (bumped && bumped.length > 0) {
|
|
267
|
+
return { issue: toIssueRecord(bumped[0]), isNew: false };
|
|
268
|
+
}
|
|
269
|
+
// No open issue found — insert a new one.
|
|
270
|
+
const ts = now();
|
|
271
|
+
const inserted = await crud.insert({
|
|
272
|
+
scope_id: input.scope_id,
|
|
273
|
+
type: input.type,
|
|
274
|
+
status: 'new',
|
|
275
|
+
subject_user_id: input.subject_user_id,
|
|
276
|
+
assigned_to: null,
|
|
277
|
+
recipient_scope_id: input.recipient_scope_id ?? null,
|
|
278
|
+
title: input.title,
|
|
279
|
+
summary: input.summary,
|
|
280
|
+
payload: input.payload,
|
|
281
|
+
dedupe_key: input.dedupe_key,
|
|
282
|
+
occurrence_count: 1,
|
|
283
|
+
first_seen_at: ts,
|
|
284
|
+
last_seen_at: ts,
|
|
285
|
+
resolution: null,
|
|
286
|
+
resolution_role_id: null,
|
|
287
|
+
resolution_reason: null,
|
|
288
|
+
resolved_by: null,
|
|
289
|
+
resolved_at: null,
|
|
290
|
+
created_at: ts,
|
|
291
|
+
updated_at: ts,
|
|
292
|
+
});
|
|
293
|
+
const row = Array.isArray(inserted) ? inserted[0] : inserted;
|
|
294
|
+
if (!row)
|
|
295
|
+
throw new Error('hazo_admin:issues — INSERT returned no row');
|
|
296
|
+
return { issue: toIssueRecord(row), isNew: true };
|
|
297
|
+
}
|
|
298
|
+
async function listIssues(opts) {
|
|
299
|
+
const { adminScopeIds, isGlobalAdmin = false, status, assignedTo, type, limit = 50, offset = 0, } = opts;
|
|
300
|
+
if (!isGlobalAdmin && (!adminScopeIds || adminScopeIds.length === 0)) {
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
const qb = crud.query();
|
|
304
|
+
if (!isGlobalAdmin && adminScopeIds && adminScopeIds.length > 0) {
|
|
305
|
+
qb.where('scope_id', 'in', adminScopeIds);
|
|
306
|
+
}
|
|
307
|
+
if (status !== undefined) {
|
|
308
|
+
const statuses = Array.isArray(status) ? status : [status];
|
|
309
|
+
qb.where('status', 'in', statuses);
|
|
310
|
+
}
|
|
311
|
+
if (assignedTo !== undefined) {
|
|
312
|
+
if (assignedTo === null) {
|
|
313
|
+
qb.where('assigned_to', 'is', null);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
qb.where('assigned_to', 'eq', assignedTo);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (type !== undefined) {
|
|
320
|
+
qb.where('type', 'eq', type);
|
|
321
|
+
}
|
|
322
|
+
qb.order('last_seen_at', 'desc').limit(limit).offset(offset);
|
|
323
|
+
const rows = await qb.execute('GET');
|
|
324
|
+
return (Array.isArray(rows) ? rows : []).map((r) => toIssueRecord(r));
|
|
325
|
+
}
|
|
326
|
+
async function getIssue(id) {
|
|
327
|
+
const row = await crud.findById(id);
|
|
328
|
+
if (!row)
|
|
329
|
+
return null;
|
|
330
|
+
return toIssueRecord(row);
|
|
331
|
+
}
|
|
332
|
+
async function transitionStatus(id, status, actorUserId) {
|
|
333
|
+
const issue = await getIssue(id);
|
|
334
|
+
if (!issue)
|
|
335
|
+
throw new Error(`hazo_admin:issues — issue ${id} not found`);
|
|
336
|
+
assertTransition(issue.status, status);
|
|
337
|
+
const ts = now();
|
|
338
|
+
const patch = { status, updated_at: ts };
|
|
339
|
+
if (status === 'wip' && issue.assigned_to === null) {
|
|
340
|
+
patch.assigned_to = actorUserId;
|
|
341
|
+
}
|
|
342
|
+
const updated = await crud.updateById(id, patch);
|
|
343
|
+
const row = Array.isArray(updated) ? updated[0] : updated;
|
|
344
|
+
if (!row)
|
|
345
|
+
throw new Error(`hazo_admin:issues — UPDATE returned no row for ${id}`);
|
|
346
|
+
return toIssueRecord(row);
|
|
347
|
+
}
|
|
348
|
+
async function setAssignee(id, userId) {
|
|
349
|
+
const ts = now();
|
|
350
|
+
const updated = await crud.updateById(id, { assigned_to: userId, updated_at: ts });
|
|
351
|
+
const row = Array.isArray(updated) ? updated[0] : updated;
|
|
352
|
+
if (!row)
|
|
353
|
+
throw new Error(`hazo_admin:issues — issue ${id} not found`);
|
|
354
|
+
return toIssueRecord(row);
|
|
355
|
+
}
|
|
356
|
+
async function resolveIssue(id, opts) {
|
|
357
|
+
const ts = now();
|
|
358
|
+
const updated = await crud.updateById(id, {
|
|
359
|
+
status: 'closed',
|
|
360
|
+
resolution: opts.resolution,
|
|
361
|
+
resolution_role_id: opts.role_id ?? null,
|
|
362
|
+
resolution_reason: opts.reason ?? null,
|
|
363
|
+
resolved_by: opts.resolved_by,
|
|
364
|
+
resolved_at: ts,
|
|
365
|
+
updated_at: ts,
|
|
366
|
+
});
|
|
367
|
+
const row = Array.isArray(updated) ? updated[0] : updated;
|
|
368
|
+
if (!row)
|
|
369
|
+
throw new Error(`hazo_admin:issues — issue ${id} not found`);
|
|
370
|
+
return toIssueRecord(row);
|
|
371
|
+
}
|
|
372
|
+
async function archiveClosedOlderThan(cutoffDate) {
|
|
373
|
+
const cutoff = cutoffDate.toISOString();
|
|
374
|
+
log.debug('createIssueStoreFromConnect:archiveClosedOlderThan', { cutoff });
|
|
375
|
+
const archived = await adapter.claimRows({
|
|
376
|
+
table: TABLE,
|
|
377
|
+
where: {
|
|
378
|
+
status: 'closed',
|
|
379
|
+
resolved_at: { lte: cutoff },
|
|
380
|
+
},
|
|
381
|
+
set: {
|
|
382
|
+
status: 'archived',
|
|
383
|
+
updated_at: { $now: true },
|
|
384
|
+
},
|
|
385
|
+
returning: ['id'],
|
|
386
|
+
limit: 1000,
|
|
387
|
+
});
|
|
388
|
+
const count = (archived ?? []).length;
|
|
389
|
+
log.debug('createIssueStoreFromConnect:archiveClosedOlderThan — archived', { count });
|
|
390
|
+
return count;
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
createOrBumpIssue,
|
|
394
|
+
listIssues,
|
|
395
|
+
getIssue,
|
|
396
|
+
transitionStatus,
|
|
397
|
+
setAssignee,
|
|
398
|
+
resolveIssue,
|
|
399
|
+
archiveClosedOlderThan,
|
|
400
|
+
};
|
|
401
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Standard site-admin package — auth-gated admin shell + panel kit + drop-in /admin preset",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"hazo_core": "^1.2.0",
|
|
55
|
-
"hazo_ui": "^4.
|
|
55
|
+
"hazo_ui": "^4.4.0",
|
|
56
56
|
"react": "^18.0.0 || ^19.0.0",
|
|
57
57
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
58
58
|
"next": "^14.0.0 || ^16.0.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"hazo_logs": "^2.0.2",
|
|
61
61
|
"hazo_debug": "^3.1.1",
|
|
62
62
|
"hazo_connect": "^3.7.0",
|
|
63
|
-
"hazo_config": "^2.
|
|
63
|
+
"hazo_config": "^2.2.0",
|
|
64
64
|
"hazo_api": "^2.3.1",
|
|
65
65
|
"hazo_files": "^3.0.0",
|
|
66
66
|
"hazo_jobs": "^0.12.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"hazo_secure": "^1.2.0",
|
|
71
71
|
"hazo_blog": "^0.2.0",
|
|
72
72
|
"hazo_testing": "^0.3.1",
|
|
73
|
-
"hazo_notify": "^6.1.
|
|
73
|
+
"hazo_notify": "^6.1.4",
|
|
74
74
|
"lucide-react": "^0.553.0"
|
|
75
75
|
},
|
|
76
76
|
"peerDependenciesMeta": {
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"@types/react": "^19.0.0",
|
|
132
132
|
"@types/react-dom": "^19.0.0",
|
|
133
133
|
"hazo_core": "^1.2.0",
|
|
134
|
-
"hazo_ui": "^4.
|
|
134
|
+
"hazo_ui": "^4.4.0",
|
|
135
135
|
"hazo_auth": "^10.3.0",
|
|
136
136
|
"hazo_testing": "^0.3.1",
|
|
137
137
|
"react": "^19.0.0",
|