@knpkv/jcf-web 0.2.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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +315 -0
  3. package/dist/client/assets/AgentSettingsPanel-NhTzwiq6.js +1 -0
  4. package/dist/client/assets/Schema-DUFTUil2.js +2 -0
  5. package/dist/client/assets/UrlParams-ZN2s8OM_.js +6 -0
  6. package/dist/client/assets/agentSettings-nw9lCgBQ.js +1 -0
  7. package/dist/client/assets/api-BQCWhiKV.js +4 -0
  8. package/dist/client/assets/decoding-CebF86HD.js +1 -0
  9. package/dist/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  10. package/dist/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  11. package/dist/client/assets/index-Ch0W9DhA.css +1 -0
  12. package/dist/client/assets/index-N2vhoKYl.js +51 -0
  13. package/dist/client/assets/rolldown-runtime-hePW80VL.js +1 -0
  14. package/dist/client/assets/transport-AcByYfLh.js +1 -0
  15. package/dist/client/index.html +19 -0
  16. package/dist/index.d.ts +11 -0
  17. package/dist/index.js +12 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/main.d.ts +2 -0
  20. package/dist/main.js +32 -0
  21. package/dist/main.js.map +1 -0
  22. package/dist/server/Api.d.ts +789 -0
  23. package/dist/server/Api.js +98 -0
  24. package/dist/server/Api.js.map +1 -0
  25. package/dist/server/Confirm.d.ts +94 -0
  26. package/dist/server/Confirm.js +243 -0
  27. package/dist/server/Confirm.js.map +1 -0
  28. package/dist/server/Handlers.d.ts +14 -0
  29. package/dist/server/Handlers.js +157 -0
  30. package/dist/server/Handlers.js.map +1 -0
  31. package/dist/server/HttpApplication.d.ts +7 -0
  32. package/dist/server/HttpApplication.js +62 -0
  33. package/dist/server/HttpApplication.js.map +1 -0
  34. package/dist/server/OwnerSession.d.ts +62 -0
  35. package/dist/server/OwnerSession.js +219 -0
  36. package/dist/server/OwnerSession.js.map +1 -0
  37. package/dist/server/RowDescriptions.d.ts +19 -0
  38. package/dist/server/RowDescriptions.js +42 -0
  39. package/dist/server/RowDescriptions.js.map +1 -0
  40. package/dist/server/SavedEntryOperations.d.ts +27 -0
  41. package/dist/server/SavedEntryOperations.js +171 -0
  42. package/dist/server/SavedEntryOperations.js.map +1 -0
  43. package/dist/server/Server.d.ts +13 -0
  44. package/dist/server/Server.js +29 -0
  45. package/dist/server/Server.js.map +1 -0
  46. package/dist/server/WeekPlan.d.ts +110 -0
  47. package/dist/server/WeekPlan.js +258 -0
  48. package/dist/server/WeekPlan.js.map +1 -0
  49. package/dist/server/WeekPlans.d.ts +30 -0
  50. package/dist/server/WeekPlans.js +190 -0
  51. package/dist/server/WeekPlans.js.map +1 -0
  52. package/dist/server/WeekRead.d.ts +435 -0
  53. package/dist/server/WeekRead.js +189 -0
  54. package/dist/server/WeekRead.js.map +1 -0
  55. package/dist/shared/contracts.d.ts +901 -0
  56. package/dist/shared/contracts.js +365 -0
  57. package/dist/shared/contracts.js.map +1 -0
  58. package/dist/shared/writePlanning.d.ts +6 -0
  59. package/dist/shared/writePlanning.js +19 -0
  60. package/dist/shared/writePlanning.js.map +1 -0
  61. package/dist/tsconfig.tsbuildinfo +1 -0
  62. package/package.json +60 -0
@@ -0,0 +1,435 @@
1
+ /** Read one week with progress owned by the request. Disconnecting cancels its work. */
2
+ import type { ConfigService, IssueFacts, ReconcileService } from "@knpkv/jira-clockify";
3
+ import { Effect, Stream } from "effect";
4
+ import { ApiError, PlanExpiredError, type ReadProgress, type WeekPlanResponse, type WeekScopeName } from "../shared/contracts.js";
5
+ import type { WeekPlansContract } from "./WeekPlans.js";
6
+ /** Translate events for the authenticated owner; request text contains supplied session evidence. */
7
+ export declare const sessionProgress: (event: ReconcileService.SessionProposalProgress) => ReadProgress;
8
+ /** Return retained evidence without touching sessions or providers. A miss never starts attribution. */
9
+ export declare const savedWeekPlan: (options: {
10
+ readonly query: {
11
+ readonly monday?: string | undefined;
12
+ readonly only?: WeekScopeName | undefined;
13
+ };
14
+ readonly plans: WeekPlansContract;
15
+ }) => Effect.Effect<{
16
+ monday: string;
17
+ scope: "both" | "clockify" | "jira";
18
+ plan: {
19
+ readonly attributorAvailable: boolean;
20
+ readonly attributorCalls: number;
21
+ readonly days: readonly string[];
22
+ readonly excludedDays: readonly {
23
+ readonly day: string;
24
+ readonly reason: string;
25
+ }[];
26
+ readonly monday: string;
27
+ readonly planId: string;
28
+ readonly rows: readonly {
29
+ readonly clockifyDescription: string | null;
30
+ readonly clockifySeconds: number;
31
+ readonly day: string;
32
+ readonly intervals: readonly {
33
+ readonly entry?: {
34
+ readonly revision: string;
35
+ readonly id: string;
36
+ readonly source: "clockify" | "jira";
37
+ readonly ticketKey: string | null;
38
+ readonly startMs: number;
39
+ readonly endMs: number;
40
+ readonly description: string | null;
41
+ };
42
+ readonly endMs: number;
43
+ readonly source: "clockify" | "jira";
44
+ readonly startMs: number;
45
+ }[];
46
+ readonly jiraSeconds: number;
47
+ readonly proposal?: {
48
+ readonly activeSeconds: number;
49
+ readonly blocks: readonly {
50
+ readonly consumed: {
51
+ readonly clockify: number;
52
+ readonly jira: number;
53
+ };
54
+ readonly clockifyRefusal?: "unlinked-overlap";
55
+ readonly endMs: number;
56
+ readonly seconds: number;
57
+ readonly startMs: number;
58
+ readonly sourceStartMs?: number | undefined;
59
+ }[];
60
+ readonly clockifyDelta: number;
61
+ readonly confidence: number | null;
62
+ readonly jiraDelta: number;
63
+ readonly maxSeconds: number;
64
+ readonly sessionCount: number;
65
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
66
+ } | undefined;
67
+ readonly rowId: string;
68
+ readonly ticketKey: string;
69
+ readonly ticketTitle: string | null;
70
+ }[];
71
+ readonly unlinkedClockify: readonly {
72
+ readonly entry?: {
73
+ readonly revision: string;
74
+ readonly id: string;
75
+ readonly source: "clockify" | "jira";
76
+ readonly ticketKey: string | null;
77
+ readonly startMs: number;
78
+ readonly endMs: number;
79
+ readonly description: string | null;
80
+ };
81
+ readonly day: string;
82
+ readonly seconds: number;
83
+ readonly description: string | null;
84
+ readonly startMs: number;
85
+ readonly endMs: number;
86
+ }[];
87
+ readonly sessionCount: number;
88
+ readonly sessionScanAvailable?: boolean | undefined;
89
+ readonly scope: "both" | "clockify" | "jira";
90
+ readonly sessionRootCount: number;
91
+ readonly notMine: readonly {
92
+ readonly assignee: string | null;
93
+ readonly day: string;
94
+ readonly seconds: number;
95
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
96
+ readonly ticketKey: string;
97
+ readonly ticketTitle: string | null;
98
+ }[];
99
+ readonly ownershipChecked: boolean;
100
+ readonly ownership: "any" | "assigned";
101
+ readonly unattributed: readonly {
102
+ readonly cwds: readonly string[];
103
+ readonly day: string;
104
+ readonly seconds: number;
105
+ readonly sessionCount: number;
106
+ }[];
107
+ readonly withheld: readonly {
108
+ readonly confidence: number | null;
109
+ readonly day: string;
110
+ readonly seconds: number;
111
+ readonly ticketKey: string;
112
+ readonly ticketTitle: string | null;
113
+ }[];
114
+ } | null;
115
+ }, never, never>;
116
+ /** Shared by the JSON and streaming routes so both retain identical held evidence. */
117
+ export declare const readWeekPlan: (options: {
118
+ readonly query: {
119
+ readonly monday?: string | undefined;
120
+ readonly only?: WeekScopeName | undefined;
121
+ };
122
+ readonly report: (progress: ReadProgress) => Effect.Effect<void>;
123
+ readonly reconcile: ReconcileService.ReconcileServiceContract;
124
+ readonly issues: IssueFacts.IssueFacts["Service"];
125
+ readonly config: ConfigService.ConfigService["Service"];
126
+ readonly plans: WeekPlansContract;
127
+ }) => Effect.Effect<{
128
+ readonly attributorAvailable: boolean;
129
+ readonly attributorCalls: number;
130
+ readonly days: readonly string[];
131
+ readonly excludedDays: readonly {
132
+ readonly day: string;
133
+ readonly reason: string;
134
+ }[];
135
+ readonly monday: string;
136
+ readonly planId: string;
137
+ readonly rows: readonly {
138
+ readonly clockifyDescription: string | null;
139
+ readonly clockifySeconds: number;
140
+ readonly day: string;
141
+ readonly intervals: readonly {
142
+ readonly entry?: {
143
+ readonly revision: string;
144
+ readonly id: string;
145
+ readonly source: "clockify" | "jira";
146
+ readonly ticketKey: string | null;
147
+ readonly startMs: number;
148
+ readonly endMs: number;
149
+ readonly description: string | null;
150
+ };
151
+ readonly endMs: number;
152
+ readonly source: "clockify" | "jira";
153
+ readonly startMs: number;
154
+ }[];
155
+ readonly jiraSeconds: number;
156
+ readonly proposal?: {
157
+ readonly activeSeconds: number;
158
+ readonly blocks: readonly {
159
+ readonly consumed: {
160
+ readonly clockify: number;
161
+ readonly jira: number;
162
+ };
163
+ readonly clockifyRefusal?: "unlinked-overlap";
164
+ readonly endMs: number;
165
+ readonly seconds: number;
166
+ readonly startMs: number;
167
+ readonly sourceStartMs?: number | undefined;
168
+ }[];
169
+ readonly clockifyDelta: number;
170
+ readonly confidence: number | null;
171
+ readonly jiraDelta: number;
172
+ readonly maxSeconds: number;
173
+ readonly sessionCount: number;
174
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
175
+ } | undefined;
176
+ readonly rowId: string;
177
+ readonly ticketKey: string;
178
+ readonly ticketTitle: string | null;
179
+ }[];
180
+ readonly unlinkedClockify: readonly {
181
+ readonly entry?: {
182
+ readonly revision: string;
183
+ readonly id: string;
184
+ readonly source: "clockify" | "jira";
185
+ readonly ticketKey: string | null;
186
+ readonly startMs: number;
187
+ readonly endMs: number;
188
+ readonly description: string | null;
189
+ };
190
+ readonly day: string;
191
+ readonly seconds: number;
192
+ readonly description: string | null;
193
+ readonly startMs: number;
194
+ readonly endMs: number;
195
+ }[];
196
+ readonly sessionCount: number;
197
+ readonly sessionScanAvailable?: boolean | undefined;
198
+ readonly scope: "both" | "clockify" | "jira";
199
+ readonly sessionRootCount: number;
200
+ readonly notMine: readonly {
201
+ readonly assignee: string | null;
202
+ readonly day: string;
203
+ readonly seconds: number;
204
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
205
+ readonly ticketKey: string;
206
+ readonly ticketTitle: string | null;
207
+ }[];
208
+ readonly ownershipChecked: boolean;
209
+ readonly ownership: "any" | "assigned";
210
+ readonly unattributed: readonly {
211
+ readonly cwds: readonly string[];
212
+ readonly day: string;
213
+ readonly seconds: number;
214
+ readonly sessionCount: number;
215
+ }[];
216
+ readonly withheld: readonly {
217
+ readonly confidence: number | null;
218
+ readonly day: string;
219
+ readonly seconds: number;
220
+ readonly ticketKey: string;
221
+ readonly ticketTitle: string | null;
222
+ }[];
223
+ }, ApiError | PlanExpiredError, never>;
224
+ /** Read logged time without session discovery, attribution, or ownership lookups. */
225
+ export declare const readRecordedWeekPlan: (options: {
226
+ readonly query: {
227
+ readonly monday?: string | undefined;
228
+ readonly only?: WeekScopeName | undefined;
229
+ };
230
+ readonly report: (progress: ReadProgress) => Effect.Effect<void>;
231
+ readonly reconcile: ReconcileService.ReconcileServiceContract;
232
+ readonly config: ConfigService.ConfigService["Service"];
233
+ readonly plans: WeekPlansContract;
234
+ }) => Effect.Effect<{
235
+ readonly attributorAvailable: boolean;
236
+ readonly attributorCalls: number;
237
+ readonly days: readonly string[];
238
+ readonly excludedDays: readonly {
239
+ readonly day: string;
240
+ readonly reason: string;
241
+ }[];
242
+ readonly monday: string;
243
+ readonly planId: string;
244
+ readonly rows: readonly {
245
+ readonly clockifyDescription: string | null;
246
+ readonly clockifySeconds: number;
247
+ readonly day: string;
248
+ readonly intervals: readonly {
249
+ readonly entry?: {
250
+ readonly revision: string;
251
+ readonly id: string;
252
+ readonly source: "clockify" | "jira";
253
+ readonly ticketKey: string | null;
254
+ readonly startMs: number;
255
+ readonly endMs: number;
256
+ readonly description: string | null;
257
+ };
258
+ readonly endMs: number;
259
+ readonly source: "clockify" | "jira";
260
+ readonly startMs: number;
261
+ }[];
262
+ readonly jiraSeconds: number;
263
+ readonly proposal?: {
264
+ readonly activeSeconds: number;
265
+ readonly blocks: readonly {
266
+ readonly consumed: {
267
+ readonly clockify: number;
268
+ readonly jira: number;
269
+ };
270
+ readonly clockifyRefusal?: "unlinked-overlap";
271
+ readonly endMs: number;
272
+ readonly seconds: number;
273
+ readonly startMs: number;
274
+ readonly sourceStartMs?: number | undefined;
275
+ }[];
276
+ readonly clockifyDelta: number;
277
+ readonly confidence: number | null;
278
+ readonly jiraDelta: number;
279
+ readonly maxSeconds: number;
280
+ readonly sessionCount: number;
281
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
282
+ } | undefined;
283
+ readonly rowId: string;
284
+ readonly ticketKey: string;
285
+ readonly ticketTitle: string | null;
286
+ }[];
287
+ readonly unlinkedClockify: readonly {
288
+ readonly entry?: {
289
+ readonly revision: string;
290
+ readonly id: string;
291
+ readonly source: "clockify" | "jira";
292
+ readonly ticketKey: string | null;
293
+ readonly startMs: number;
294
+ readonly endMs: number;
295
+ readonly description: string | null;
296
+ };
297
+ readonly day: string;
298
+ readonly seconds: number;
299
+ readonly description: string | null;
300
+ readonly startMs: number;
301
+ readonly endMs: number;
302
+ }[];
303
+ readonly sessionCount: number;
304
+ readonly sessionScanAvailable?: boolean | undefined;
305
+ readonly scope: "both" | "clockify" | "jira";
306
+ readonly sessionRootCount: number;
307
+ readonly notMine: readonly {
308
+ readonly assignee: string | null;
309
+ readonly day: string;
310
+ readonly seconds: number;
311
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
312
+ readonly ticketKey: string;
313
+ readonly ticketTitle: string | null;
314
+ }[];
315
+ readonly ownershipChecked: boolean;
316
+ readonly ownership: "any" | "assigned";
317
+ readonly unattributed: readonly {
318
+ readonly cwds: readonly string[];
319
+ readonly day: string;
320
+ readonly seconds: number;
321
+ readonly sessionCount: number;
322
+ }[];
323
+ readonly withheld: readonly {
324
+ readonly confidence: number | null;
325
+ readonly day: string;
326
+ readonly seconds: number;
327
+ readonly ticketKey: string;
328
+ readonly ticketTitle: string | null;
329
+ }[];
330
+ }, ApiError | PlanExpiredError, never>;
331
+ /** Refresh provider facts against the retained week; no session reader or attributor is called. */
332
+ export declare const refreshWeekPlan: (options: {
333
+ readonly planId: string;
334
+ readonly report: (progress: ReadProgress) => Effect.Effect<void>;
335
+ readonly reconcile: ReconcileService.ReconcileServiceContract;
336
+ readonly plans: WeekPlansContract;
337
+ }) => Effect.Effect<{
338
+ readonly attributorAvailable: boolean;
339
+ readonly attributorCalls: number;
340
+ readonly days: readonly string[];
341
+ readonly excludedDays: readonly {
342
+ readonly day: string;
343
+ readonly reason: string;
344
+ }[];
345
+ readonly monday: string;
346
+ readonly planId: string;
347
+ readonly rows: readonly {
348
+ readonly clockifyDescription: string | null;
349
+ readonly clockifySeconds: number;
350
+ readonly day: string;
351
+ readonly intervals: readonly {
352
+ readonly entry?: {
353
+ readonly revision: string;
354
+ readonly id: string;
355
+ readonly source: "clockify" | "jira";
356
+ readonly ticketKey: string | null;
357
+ readonly startMs: number;
358
+ readonly endMs: number;
359
+ readonly description: string | null;
360
+ };
361
+ readonly endMs: number;
362
+ readonly source: "clockify" | "jira";
363
+ readonly startMs: number;
364
+ }[];
365
+ readonly jiraSeconds: number;
366
+ readonly proposal?: {
367
+ readonly activeSeconds: number;
368
+ readonly blocks: readonly {
369
+ readonly consumed: {
370
+ readonly clockify: number;
371
+ readonly jira: number;
372
+ };
373
+ readonly clockifyRefusal?: "unlinked-overlap";
374
+ readonly endMs: number;
375
+ readonly seconds: number;
376
+ readonly startMs: number;
377
+ readonly sourceStartMs?: number | undefined;
378
+ }[];
379
+ readonly clockifyDelta: number;
380
+ readonly confidence: number | null;
381
+ readonly jiraDelta: number;
382
+ readonly maxSeconds: number;
383
+ readonly sessionCount: number;
384
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
385
+ } | undefined;
386
+ readonly rowId: string;
387
+ readonly ticketKey: string;
388
+ readonly ticketTitle: string | null;
389
+ }[];
390
+ readonly unlinkedClockify: readonly {
391
+ readonly entry?: {
392
+ readonly revision: string;
393
+ readonly id: string;
394
+ readonly source: "clockify" | "jira";
395
+ readonly ticketKey: string | null;
396
+ readonly startMs: number;
397
+ readonly endMs: number;
398
+ readonly description: string | null;
399
+ };
400
+ readonly day: string;
401
+ readonly seconds: number;
402
+ readonly description: string | null;
403
+ readonly startMs: number;
404
+ readonly endMs: number;
405
+ }[];
406
+ readonly sessionCount: number;
407
+ readonly sessionScanAvailable?: boolean | undefined;
408
+ readonly scope: "both" | "clockify" | "jira";
409
+ readonly sessionRootCount: number;
410
+ readonly notMine: readonly {
411
+ readonly assignee: string | null;
412
+ readonly day: string;
413
+ readonly seconds: number;
414
+ readonly signal: "agent" | "branch" | "none" | "path" | "standing";
415
+ readonly ticketKey: string;
416
+ readonly ticketTitle: string | null;
417
+ }[];
418
+ readonly ownershipChecked: boolean;
419
+ readonly ownership: "any" | "assigned";
420
+ readonly unattributed: readonly {
421
+ readonly cwds: readonly string[];
422
+ readonly day: string;
423
+ readonly seconds: number;
424
+ readonly sessionCount: number;
425
+ }[];
426
+ readonly withheld: readonly {
427
+ readonly confidence: number | null;
428
+ readonly day: string;
429
+ readonly seconds: number;
430
+ readonly ticketKey: string;
431
+ readonly ticketTitle: string | null;
432
+ }[];
433
+ }, ApiError | PlanExpiredError, never>;
434
+ /** Bounded NDJSON transport; typed read failures are terminal events after headers have been sent. */
435
+ export declare const streamWeekRead: (read: (report: (progress: ReadProgress) => Effect.Effect<void>) => Effect.Effect<WeekPlanResponse, ApiError | PlanExpiredError>) => Stream.Stream<Uint8Array<ArrayBuffer>, never, never>;
@@ -0,0 +1,189 @@
1
+ import { Time } from "@knpkv/jira-clockify";
2
+ import { Clock, Effect, Queue, Stream } from "effect";
3
+ import { ApiError, PlanExpiredError } from "../shared/contracts.js";
4
+ import { retainedEntryRevision } from "./SavedEntryOperations.js";
5
+ import { buildWeekPlan, sidesOfScope } from "./WeekPlan.js";
6
+ /** Translate events for the authenticated owner; request text contains supplied session evidence. */
7
+ export const sessionProgress = (event) => {
8
+ switch (event._tag) {
9
+ case "AgentActivity":
10
+ return {
11
+ stage: "attribution",
12
+ message: `Agent batch ${event.batch} of ${event.batches}`,
13
+ activity: { batch: event.batch, batches: event.batches, kind: event.kind, text: event.text }
14
+ };
15
+ case "SessionsRead":
16
+ return {
17
+ stage: "attribution",
18
+ message: `${event.count} sessions read. Matching branches and directory mappings.`
19
+ };
20
+ case "AttributingSessions":
21
+ return {
22
+ stage: "attribution",
23
+ message: `Asking the coding agent about ${event.count} sessions in ${event.calls} batches.`,
24
+ completed: 0,
25
+ total: event.count
26
+ };
27
+ case "SessionAttributed":
28
+ return {
29
+ stage: "attribution",
30
+ message: `${event.done} of ${event.total} sessions checked. ${event.outcome._tag === "Placed"
31
+ ? `Matched ${event.outcome.ticketKey}.`
32
+ : event.outcome._tag === "Declined"
33
+ ? "No confident ticket match."
34
+ : "Agent unavailable for this session; its hours will remain visible."}`,
35
+ completed: event.done,
36
+ total: event.total
37
+ };
38
+ case "SessionsAttributed":
39
+ return {
40
+ stage: "attribution",
41
+ message: `${event.placed} matched, ${event.declined} unmatched, ${event.unavailable} unavailable. Calculating credited time.`
42
+ };
43
+ case "ReadingRecordedTime":
44
+ return {
45
+ stage: "recorded",
46
+ message: `Reading ${event.sides.clockify && event.sides.jira
47
+ ? "Clockify entries and Jira worklogs"
48
+ : event.sides.clockify
49
+ ? "Clockify entries"
50
+ : "Jira worklogs"}${event.sides.clockify ? " and checking running timers" : ""}.`
51
+ };
52
+ }
53
+ };
54
+ /** Return retained evidence without touching sessions or providers. A miss never starts attribution. */
55
+ export const savedWeekPlan = Effect.fn("WeekRead.saved")(function* (options) {
56
+ const anchor = options.query.monday === undefined
57
+ ? new Date(yield* Clock.currentTimeMillis)
58
+ : new Date(`${options.query.monday}T00:00:00`);
59
+ const monday = Time.localDay(Time.isoWeekPeriod(anchor).from);
60
+ const scope = options.query.only ?? "both";
61
+ const held = yield* options.plans.forWeek(monday, scope);
62
+ return { monday, scope, plan: held?.plan ?? null };
63
+ });
64
+ /** Shared by the JSON and streaming routes so both retain identical held evidence. */
65
+ export const readWeekPlan = Effect.fn("WeekRead.read")(function* (options) {
66
+ const { config, issues, plans, query, reconcile, report } = options;
67
+ const generation = yield* plans.readGeneration;
68
+ const anchor = query.monday === undefined
69
+ ? new Date(yield* Clock.currentTimeMillis)
70
+ : new Date(`${query.monday}T00:00:00`);
71
+ const period = Time.isoWeekPeriod(anchor);
72
+ const scope = query.only ?? "both";
73
+ const sides = sidesOfScope(scope);
74
+ yield* report({ stage: "sessions", message: "Reading coding sessions from your configured directories." });
75
+ const result = yield* reconcile.proposeFromSessions(period, {
76
+ sides,
77
+ onProgress: (event) => report(sessionProgress(event))
78
+ }).pipe(Effect.mapError((error) => new ApiError({ message: error.message })));
79
+ const settings = yield* config.get;
80
+ const keys = [...new Set([...result.proposals, ...result.recorded, ...result.withheld].map((row) => row.ticketKey))];
81
+ yield* report({
82
+ stage: "issues",
83
+ message: sides.jira
84
+ ? `Looking up titles and assignees for ${keys.length} Jira tickets.`
85
+ : "Skipping Jira titles and ownership in Clockify-only mode."
86
+ });
87
+ const looked = sides.jira ? yield* issues.lookup(keys) : { checked: false, facts: new Map() };
88
+ yield* report({ stage: "calendar", message: "Placing logged time and proposed blocks on the calendar." });
89
+ const held = buildWeekPlan({
90
+ createdAtMillis: yield* Clock.currentTimeMillis,
91
+ monday: period.from,
92
+ ownership: {
93
+ checked: looked.checked,
94
+ facts: looked.facts,
95
+ mode: settings.sessionOwnership,
96
+ overrides: settings.sessionOwnershipOverrides
97
+ },
98
+ planId: yield* plans.nextPlanId.pipe(Effect.mapError(() => new ApiError({ message: "Could not create a week plan. Retry the read." }))),
99
+ report: result,
100
+ scope
101
+ });
102
+ return (yield* plans.keep(held, generation)).plan;
103
+ });
104
+ /** Read logged time without session discovery, attribution, or ownership lookups. */
105
+ export const readRecordedWeekPlan = Effect.fn("WeekRead.recordedOnly")(function* (options) {
106
+ const { config, plans, query, reconcile, report } = options;
107
+ const generation = yield* plans.readGeneration;
108
+ const anchor = query.monday === undefined
109
+ ? new Date(yield* Clock.currentTimeMillis)
110
+ : new Date(`${query.monday}T00:00:00`);
111
+ const period = Time.isoWeekPeriod(anchor);
112
+ const scope = query.only ?? "both";
113
+ const previous = yield* plans.forWeek(Time.localDay(period.from), scope);
114
+ if (previous !== undefined) {
115
+ return yield* refreshWeekPlan({ planId: previous.planId, report, reconcile, plans });
116
+ }
117
+ const sides = sidesOfScope(scope);
118
+ yield* report(sessionProgress({ _tag: "ReadingRecordedTime", sides }));
119
+ const settings = yield* config.get;
120
+ const result = yield* reconcile.refreshRecordedTime(period, {
121
+ attributed: [],
122
+ proposals: [],
123
+ recorded: [],
124
+ unlinkedClockify: [],
125
+ sides,
126
+ withheld: [],
127
+ unattributed: [],
128
+ excludedDays: [],
129
+ attributorAvailable: true,
130
+ sessionCount: 0,
131
+ sessionRootCount: settings.sessionRoots.length,
132
+ attributorCalls: 0,
133
+ digests: new Map()
134
+ }).pipe(Effect.mapError((error) => new ApiError({ message: error.message })));
135
+ yield* report({ stage: "calendar", message: "Placing logged time on the calendar. Sessions have not been scanned." });
136
+ const held = buildWeekPlan({
137
+ createdAtMillis: yield* Clock.currentTimeMillis,
138
+ monday: period.from,
139
+ planId: yield* plans.nextPlanId.pipe(Effect.mapError(() => new ApiError({ message: "Could not create a week plan. Retry the read." }))),
140
+ report: result,
141
+ scope,
142
+ sessionScanAvailable: false
143
+ });
144
+ return (yield* plans.keep(held, generation)).plan;
145
+ });
146
+ /** Refresh provider facts against the retained week; no session reader or attributor is called. */
147
+ export const refreshWeekPlan = Effect.fn("WeekRead.refreshRecorded")(function* (options) {
148
+ const { plans, reconcile, report } = options;
149
+ const generation = yield* plans.readGeneration;
150
+ const previous = yield* plans.find(options.planId);
151
+ if (previous === undefined) {
152
+ return yield* new PlanExpiredError({
153
+ message: "This week is no longer held. Use Rescan sessions to read its sessions again."
154
+ });
155
+ }
156
+ const period = Time.isoWeekPeriod(new Date(`${previous.plan.monday}T00:00:00`));
157
+ const status = sessionProgress({ _tag: "ReadingRecordedTime", sides: previous.report.sides });
158
+ const scanned = previous.plan.sessionScanAvailable !== false;
159
+ yield* report({ ...status, message: scanned ? `${status.message} Keeping your session matches.` : status.message });
160
+ const refreshed = yield* reconcile.refreshRecordedTime(period, previous.report, {
161
+ jiraIssueKeys: [...previous.jiraReceiptIssueKeys]
162
+ }).pipe(Effect.mapError((error) => new ApiError({ message: error.message })));
163
+ yield* report({
164
+ stage: "calendar",
165
+ message: scanned
166
+ ? "Updating logged time and remaining proposals. Session matches are unchanged."
167
+ : "Updating logged time. Sessions have not been scanned."
168
+ });
169
+ const snapshotRevision = yield* plans.nextPlanId.pipe(Effect.mapError(() => new ApiError({ message: "Could not create an entry revision. Retry the refresh." })));
170
+ const held = buildWeekPlan({
171
+ planId: previous.planId,
172
+ entryRevision: (entry) => retainedEntryRevision(previous, entry, snapshotRevision),
173
+ createdAtMillis: previous.createdAtMillis,
174
+ monday: period.from,
175
+ report: refreshed,
176
+ consumption: previous.consumption,
177
+ jiraReceiptIssueKeys: previous.jiraReceiptIssueKeys,
178
+ scope: previous.plan.scope,
179
+ ownership: previous.ownership,
180
+ sessionScanAvailable: previous.plan.sessionScanAvailable
181
+ });
182
+ return (yield* plans.replace(previous, held, generation)).plan;
183
+ });
184
+ /** Bounded NDJSON transport; typed read failures are terminal events after headers have been sent. */
185
+ export const streamWeekRead = (read) => Stream.callback((queue) => read((progress) => Queue.offer(queue, { _tag: "Progress", progress }).pipe(Effect.asVoid)).pipe(Effect.flatMap((plan) => Queue.offer(queue, { _tag: "Complete", plan })), Effect.catchTags({
186
+ ApiError: (error) => Queue.offer(queue, { _tag: "Failed", message: error.message }),
187
+ PlanExpiredError: (error) => Queue.offer(queue, { _tag: "Failed", message: error.message })
188
+ }), Effect.andThen(Queue.end(queue))), { bufferSize: 16, strategy: "suspend" }).pipe(Stream.map((event) => new TextEncoder().encode(`${JSON.stringify(event)}\n`)));
189
+ //# sourceMappingURL=WeekRead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WeekRead.js","sourceRoot":"","sources":["../../src/server/WeekRead.ts"],"names":[],"mappings":"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"}