@kernhq/module-hr 0.2.0 → 0.4.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 (82) hide show
  1. package/dist/contract/approvals.d.ts +281 -0
  2. package/dist/contract/approvals.d.ts.map +1 -0
  3. package/dist/contract/approvals.js +127 -0
  4. package/dist/contract/approvals.js.map +1 -0
  5. package/dist/contract/attendance.d.ts +240 -0
  6. package/dist/contract/attendance.d.ts.map +1 -0
  7. package/dist/contract/attendance.js +154 -0
  8. package/dist/contract/attendance.js.map +1 -0
  9. package/dist/contract/capabilities.d.ts +1 -1
  10. package/dist/contract/capabilities.d.ts.map +1 -1
  11. package/dist/contract/capabilities.js +80 -0
  12. package/dist/contract/capabilities.js.map +1 -1
  13. package/dist/contract/events.d.ts +60 -0
  14. package/dist/contract/events.d.ts.map +1 -1
  15. package/dist/contract/events.js +60 -0
  16. package/dist/contract/events.js.map +1 -1
  17. package/dist/contract/index.d.ts +3 -0
  18. package/dist/contract/index.d.ts.map +1 -1
  19. package/dist/contract/index.js +3 -0
  20. package/dist/contract/index.js.map +1 -1
  21. package/dist/contract/leave.d.ts +168 -0
  22. package/dist/contract/leave.d.ts.map +1 -0
  23. package/dist/contract/leave.js +150 -0
  24. package/dist/contract/leave.js.map +1 -0
  25. package/dist/contract/permissions.d.ts +100 -0
  26. package/dist/contract/permissions.d.ts.map +1 -1
  27. package/dist/contract/permissions.js +119 -0
  28. package/dist/contract/permissions.js.map +1 -1
  29. package/dist/contract/router.d.ts +2440 -0
  30. package/dist/contract/router.d.ts.map +1 -1
  31. package/dist/contract/router.js +393 -0
  32. package/dist/contract/router.js.map +1 -1
  33. package/dist/policy/time.d.ts +48 -0
  34. package/dist/policy/time.d.ts.map +1 -0
  35. package/dist/policy/time.js +144 -0
  36. package/dist/policy/time.js.map +1 -0
  37. package/dist/policy/working-time.d.ts +98 -0
  38. package/dist/policy/working-time.d.ts.map +1 -0
  39. package/dist/policy/working-time.js +190 -0
  40. package/dist/policy/working-time.js.map +1 -0
  41. package/dist/server/index.d.ts.map +1 -1
  42. package/dist/server/index.js +2 -0
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/jobs.d.ts +16 -0
  45. package/dist/server/jobs.d.ts.map +1 -0
  46. package/dist/server/jobs.js +166 -0
  47. package/dist/server/jobs.js.map +1 -0
  48. package/dist/server/router.d.ts +2731 -58
  49. package/dist/server/router.d.ts.map +1 -1
  50. package/dist/server/router.js +1306 -3
  51. package/dist/server/router.js.map +1 -1
  52. package/dist/server/schema.d.ts +3344 -1
  53. package/dist/server/schema.d.ts.map +1 -1
  54. package/dist/server/schema.js +325 -0
  55. package/dist/server/schema.js.map +1 -1
  56. package/dist/server/services/approvals.d.ts +163 -0
  57. package/dist/server/services/approvals.d.ts.map +1 -0
  58. package/dist/server/services/approvals.js +325 -0
  59. package/dist/server/services/approvals.js.map +1 -0
  60. package/dist/server/services/attendance.d.ts +150 -0
  61. package/dist/server/services/attendance.d.ts.map +1 -0
  62. package/dist/server/services/attendance.js +239 -0
  63. package/dist/server/services/attendance.js.map +1 -0
  64. package/dist/server/services/ledger.d.ts +135 -0
  65. package/dist/server/services/ledger.d.ts.map +1 -0
  66. package/dist/server/services/ledger.js +178 -0
  67. package/dist/server/services/ledger.js.map +1 -0
  68. package/dist/server/services/people.d.ts +3 -3
  69. package/migrations/0002_leave.sql +237 -0
  70. package/migrations/0003_attendance.sql +206 -0
  71. package/migrations/meta/0002_snapshot.json +3009 -0
  72. package/migrations/meta/0003_snapshot.json +3681 -0
  73. package/migrations/meta/_journal.json +14 -0
  74. package/package.json +1 -1
  75. package/src/contract/approvals.ts +149 -0
  76. package/src/contract/attendance.ts +180 -0
  77. package/src/contract/capabilities.ts +80 -0
  78. package/src/contract/events.ts +81 -0
  79. package/src/contract/index.ts +3 -0
  80. package/src/contract/leave.ts +171 -0
  81. package/src/contract/permissions.ts +122 -0
  82. package/src/contract/router.ts +481 -0
@@ -0,0 +1,166 @@
1
+ import { and, eq, inArray, isNull, lte, sql } from 'drizzle-orm';
2
+ import { attendanceDays, offices, punches, schedules } from './schema.js';
3
+ import { AttendanceService } from './services/attendance.js';
4
+ import { todayIn } from './services/db.js';
5
+ import { ResolveService } from './services/resolve.js';
6
+ /**
7
+ * HR's scheduled work.
8
+ *
9
+ * One rule runs through all of it: **a cron expression fires in UTC, and this module's users do
10
+ * not live there.** A nightly job at 00:00 UTC closes an Amsterdam shift at 01:00 and an Istanbul
11
+ * one at 03:00, and gets Tehran's half-hour offset wrong in a way nobody would ever guess from the
12
+ * code. So the jobs run hourly and fan out per office, deciding for each whether *that office's*
13
+ * local boundary has passed.
14
+ */
15
+ export function hrJobs() {
16
+ return [
17
+ {
18
+ /**
19
+ * Keep the punch partitions rolling.
20
+ *
21
+ * Runs monthly, creates a year ahead, and goes through the SQL function that also enables
22
+ * row-level security on what it creates. A partition made with a bare `CREATE TABLE ...
23
+ * PARTITION OF` is readable directly by any role holding SELECT on it, whatever the parent's
24
+ * policy says — which an integration test caught rather than a review.
25
+ *
26
+ * A missing partition still would not lose a punch: the DEFAULT partition catches it. This
27
+ * exists so that never becomes normal.
28
+ */
29
+ name: 'ensure-partitions',
30
+ cron: '0 3 1 * *',
31
+ handler: async (_input, { kernel }) => {
32
+ await kernel.database.db.execute(sql `
33
+ do $$
34
+ declare
35
+ m date := (date_trunc('month', now()) - interval '1 month')::date;
36
+ stop date := (date_trunc('month', now()) + interval '12 months')::date;
37
+ begin
38
+ while m < stop loop
39
+ perform "mod_hr".ensure_punch_partition(m);
40
+ m := (m + interval '1 month')::date;
41
+ end loop;
42
+ end $$;
43
+ `);
44
+ kernel.log.info({ module: 'hr' }, 'punch partitions ensured');
45
+ },
46
+ },
47
+ {
48
+ /**
49
+ * Close shifts somebody forgot to clock out of.
50
+ *
51
+ * Hourly, and per office rather than globally: "it is past 3am" is a different moment in every
52
+ * office, and a single UTC-timed sweep would close a Tehran shift mid-afternoon.
53
+ *
54
+ * The auto clock-out is written as a punch like any other, with `method: 'manual'` and a note,
55
+ * so the sheet shows that a machine closed the day rather than the person. That distinction is
56
+ * what a regularization request is later arguing about.
57
+ */
58
+ name: 'auto-clock-out',
59
+ cron: '5 * * * *',
60
+ handler: async (_input, { kernel }) => {
61
+ const attendance = new AttendanceService();
62
+ const resolve = new ResolveService();
63
+ const workspaces = await activeWorkspaces(kernel);
64
+ for (const workspaceId of workspaces)
65
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
66
+ const withAuto = await tx
67
+ .select({ id: schedules.id, after: schedules.autoClockOutAfterMinutes })
68
+ .from(schedules)
69
+ .where(and(eq(schedules.workspaceId, workspaceId), isNull(schedules.archivedAt), sql `${schedules.autoClockOutAfterMinutes} is not null`));
70
+ if (!withAuto.length)
71
+ return;
72
+ // Anyone with an `in` and no matching `out`, older than the longest configured window.
73
+ const longest = Math.max(...withAuto.map((s) => s.after ?? 0));
74
+ const cutoff = new Date(Date.now() - longest * 60_000);
75
+ const open = await tx
76
+ .select({
77
+ personId: punches.personId,
78
+ businessDate: punches.businessDate,
79
+ at: punches.at,
80
+ })
81
+ .from(punches)
82
+ .where(and(eq(punches.workspaceId, workspaceId), eq(punches.direction, 'in'), isNull(punches.voidedByPunchId), lte(punches.at, cutoff)));
83
+ for (const row of open) {
84
+ const rows = await attendance.punchesOn(tx, workspaceId, row.personId, row.businessDate);
85
+ const stillOpen = rows.reduce((acc, r) => (r.direction === 'in' ? true : r.direction === 'out' ? false : acc), false);
86
+ if (!stillOpen)
87
+ continue;
88
+ const resolution = await resolve.forPerson(tx, workspaceId, row.personId);
89
+ const schedule = await attendance.scheduleFor(tx, workspaceId, row.personId, row.businessDate);
90
+ if (!schedule.autoClockOutAfterMinutes)
91
+ continue;
92
+ if (Date.now() - row.at.getTime() < schedule.autoClockOutAfterMinutes * 60_000)
93
+ continue;
94
+ await tx.insert(punches).values({
95
+ workspaceId,
96
+ personId: row.personId,
97
+ direction: 'out',
98
+ at: new Date(row.at.getTime() + schedule.autoClockOutAfterMinutes * 60_000),
99
+ businessDate: row.businessDate,
100
+ timezone: resolution.timezone,
101
+ method: 'manual',
102
+ trust: 'trusted',
103
+ note: 'Closed automatically: no clock-out recorded',
104
+ });
105
+ await attendance.recomputeDay(tx, workspaceId, row.personId, row.businessDate, resolution.timezone, schedule);
106
+ kernel.log.info({ module: 'hr', personId: row.personId, businessDate: row.businessDate }, 'auto clock-out');
107
+ }
108
+ });
109
+ },
110
+ },
111
+ {
112
+ /**
113
+ * Rebuild recent unlocked days.
114
+ *
115
+ * Punches recompute their own day inline, so this exists for what that path cannot see: a
116
+ * calendar edited after the fact, a schedule changed retroactively, an enqueue that never
117
+ * ran. Anything it finds and changes is a bug worth knowing about rather than routine
118
+ * maintenance — which is why it logs a count instead of running silently.
119
+ *
120
+ * Locked days are never touched: a closed month must not move underneath a payroll already
121
+ * filed.
122
+ */
123
+ name: 'reconcile-days',
124
+ cron: '30 2 * * *',
125
+ handler: async (_input, { kernel }) => {
126
+ const attendance = new AttendanceService();
127
+ const resolve = new ResolveService();
128
+ const workspaces = await activeWorkspaces(kernel);
129
+ const WINDOW_DAYS = 14;
130
+ for (const workspaceId of workspaces)
131
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
132
+ const since = new Date(Date.now() - WINDOW_DAYS * 86_400_000).toISOString().slice(0, 10);
133
+ const days = await tx
134
+ .select({
135
+ personId: attendanceDays.personId,
136
+ businessDate: attendanceDays.businessDate,
137
+ })
138
+ .from(attendanceDays)
139
+ .where(and(eq(attendanceDays.workspaceId, workspaceId), eq(attendanceDays.locked, false), sql `${attendanceDays.businessDate} >= ${since}`));
140
+ let touched = 0;
141
+ for (const day of days) {
142
+ const resolution = await resolve.forPerson(tx, workspaceId, day.personId, day.businessDate);
143
+ const schedule = await attendance.scheduleFor(tx, workspaceId, day.personId, day.businessDate);
144
+ const r = await attendance.recomputeDay(tx, workspaceId, day.personId, day.businessDate, resolution.timezone, schedule);
145
+ if (!r.locked)
146
+ touched++;
147
+ }
148
+ if (touched)
149
+ kernel.log.info({ module: 'hr', workspaceId, touched }, 'days reconciled');
150
+ });
151
+ },
152
+ },
153
+ ];
154
+ }
155
+ /**
156
+ * Workspaces with HR switched on.
157
+ *
158
+ * Read from this module's own tables rather than asked of core on every tick: a workspace with an
159
+ * office has HR enabled, and one job run should not be N broker calls.
160
+ */
161
+ async function activeWorkspaces(kernel) {
162
+ const { rows } = await kernel.database.pool.query(`select distinct workspace_id from mod_hr.offices where archived_at is null`);
163
+ return rows.map((r) => r.workspace_id);
164
+ }
165
+ export { inArray, offices, todayIn };
166
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/server/jobs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO;QACL;YACE;;;;;;;;;;eAUG;YACH,IAAI,EAAE,mBAAmB;YACzB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBACpC,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAA;;;;;;;;;;;SAWnC,CAAC,CAAA;gBACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,0BAA0B,CAAC,CAAA;YAC/D,CAAC;SACF;QAED;YACE;;;;;;;;;eASG;YACH,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBACpC,MAAM,UAAU,GAAG,IAAI,iBAAiB,EAAE,CAAA;gBAC1C,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAA;gBACpC,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAA;gBAEjD,KAAK,MAAM,WAAW,IAAI,UAAU;oBAClC,MAAM,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;wBAC5D,MAAM,QAAQ,GAAG,MAAM,EAAE;6BACtB,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,wBAAwB,EAAE,CAAC;6BACvE,IAAI,CAAC,SAAS,CAAC;6BACf,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,EACtC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,EAC5B,GAAG,CAAA,GAAG,SAAS,CAAC,wBAAwB,cAAc,CACvD,CACF,CAAA;wBACH,IAAI,CAAC,QAAQ,CAAC,MAAM;4BAAE,OAAM;wBAE5B,uFAAuF;wBACvF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAA;wBAC9D,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,MAAM,CAAC,CAAA;wBACtD,MAAM,IAAI,GAAG,MAAM,EAAE;6BAClB,MAAM,CAAC;4BACN,QAAQ,EAAE,OAAO,CAAC,QAAQ;4BAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;4BAClC,EAAE,EAAE,OAAO,CAAC,EAAE;yBACf,CAAC;6BACD,IAAI,CAAC,OAAO,CAAC;6BACb,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,EACpC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,EAC3B,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAC/B,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CACxB,CACF,CAAA;wBAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;4BACvB,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,CAAA;4BACxF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAC3B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,EAC/E,KAAK,CACN,CAAA;4BACD,IAAI,CAAC,SAAS;gCAAE,SAAQ;4BAExB,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;4BACzE,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,CAAA;4BAC9F,IAAI,CAAC,QAAQ,CAAC,wBAAwB;gCAAE,SAAQ;4BAChD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,wBAAwB,GAAG,MAAM;gCAAE,SAAQ;4BAExF,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;gCAC9B,WAAW;gCACX,QAAQ,EAAE,GAAG,CAAC,QAAQ;gCACtB,SAAS,EAAE,KAAK;gCAChB,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,wBAAwB,GAAG,MAAM,CAAC;gCAC3E,YAAY,EAAE,GAAG,CAAC,YAAY;gCAC9B,QAAQ,EAAE,UAAU,CAAC,QAAQ;gCAC7B,MAAM,EAAE,QAAQ;gCAChB,KAAK,EAAE,SAAS;gCAChB,IAAI,EAAE,6CAA6C;6BACpD,CAAC,CAAA;4BACF,MAAM,UAAU,CAAC,YAAY,CAC3B,EAAE,EACF,WAAW,EACX,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,YAAY,EAChB,UAAU,CAAC,QAAQ,EACnB,QAAQ,CACT,CAAA;4BACD,MAAM,CAAC,GAAG,CAAC,IAAI,CACb,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,EACxE,gBAAgB,CACjB,CAAA;wBACH,CAAC;oBACH,CAAC,CAAC,CAAA;YACN,CAAC;SACF;QAED;YACE;;;;;;;;;;eAUG;YACH,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBACpC,MAAM,UAAU,GAAG,IAAI,iBAAiB,EAAE,CAAA;gBAC1C,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAA;gBACpC,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAA;gBACjD,MAAM,WAAW,GAAG,EAAE,CAAA;gBAEtB,KAAK,MAAM,WAAW,IAAI,UAAU;oBAClC,MAAM,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;wBAC5D,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,GAAG,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;wBACxF,MAAM,IAAI,GAAG,MAAM,EAAE;6BAClB,MAAM,CAAC;4BACN,QAAQ,EAAE,cAAc,CAAC,QAAQ;4BACjC,YAAY,EAAE,cAAc,CAAC,YAAY;yBAC1C,CAAC;6BACD,IAAI,CAAC,cAAc,CAAC;6BACpB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,cAAc,CAAC,WAAW,EAAE,WAAW,CAAC,EAC3C,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,EAChC,GAAG,CAAA,GAAG,cAAc,CAAC,YAAY,OAAO,KAAK,EAAE,CAChD,CACF,CAAA;wBAEH,IAAI,OAAO,GAAG,CAAC,CAAA;wBACf,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;4BACvB,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,CAAA;4BAC3F,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,CAAA;4BAC9F,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,YAAY,CACrC,EAAE,EACF,WAAW,EACX,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,YAAY,EAChB,UAAU,CAAC,QAAQ,EACnB,QAAQ,CACT,CAAA;4BACD,IAAI,CAAC,CAAC,CAAC,MAAM;gCAAE,OAAO,EAAE,CAAA;wBAC1B,CAAC;wBACD,IAAI,OAAO;4BAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,iBAAiB,CAAC,CAAA;oBACzF,CAAC,CAAC,CAAA;YACN,CAAC;SACF;KACF,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,gBAAgB,CAAC,MAAc;IAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAC/C,4EAA4E,CAC7E,CAAA;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAA;AACxC,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA"}