@knpkv/jcf-web 0.3.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.
@@ -414,11 +414,7 @@ declare const JcfWebApi_base: HttpApi.HttpApi<"JcfWebApi", HttpApiGroup.HttpApiG
414
414
  readonly provider: Schema.Literal<"codex">;
415
415
  readonly model: Schema.NullOr<Schema.String>;
416
416
  readonly effort: Schema.NullOr<Schema.Literals<readonly ["minimal", "low", "medium", "high", "xhigh"]>>;
417
- }>]>>, never, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"mine", "POST", "/api/config/mine", never, never, Schema.toCodecJson<Schema.Struct<{
418
- readonly ticketKey: Schema.String;
419
- }>>, never, Schema.toCodecJson<Schema.Struct<{
420
- readonly ownershipOverrides: Schema.$Array<Schema.String>;
421
- }>>, Schema.toCodecJson<typeof ApiError | typeof ProposalRejectedError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"saveAgent", "POST", "/api/config/agent", never, never, Schema.toCodecJson<Schema.Union<readonly [Schema.Struct<{
417
+ }>]>>, never, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"saveAgent", "POST", "/api/config/agent", never, never, Schema.toCodecJson<Schema.Union<readonly [Schema.Struct<{
422
418
  readonly provider: Schema.Literal<"claude">;
423
419
  readonly model: Schema.NullOr<Schema.String>;
424
420
  readonly effort: Schema.NullOr<Schema.Literals<readonly ["low", "medium", "high", "xhigh", "max"]>>;
@@ -434,7 +430,11 @@ declare const JcfWebApi_base: HttpApi.HttpApi<"JcfWebApi", HttpApiGroup.HttpApiG
434
430
  readonly provider: Schema.Literal<"codex">;
435
431
  readonly model: Schema.NullOr<Schema.String>;
436
432
  readonly effort: Schema.NullOr<Schema.Literals<readonly ["minimal", "low", "medium", "high", "xhigh"]>>;
437
- }>]>>, Schema.toCodecJson<typeof ApiError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"standing", "POST", "/api/config/standing", never, never, Schema.toCodecJson<Schema.Struct<{
433
+ }>]>>, Schema.toCodecJson<typeof ApiError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"mine", "POST", "/api/config/mine", never, never, Schema.toCodecJson<Schema.Struct<{
434
+ readonly ticketKey: Schema.String;
435
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
436
+ readonly ownershipOverrides: Schema.$Array<Schema.String>;
437
+ }>>, Schema.toCodecJson<typeof ApiError | typeof ProposalRejectedError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"standing", "POST", "/api/config/standing", never, never, Schema.toCodecJson<Schema.Struct<{
438
438
  readonly cwd: Schema.String;
439
439
  readonly ticketKey: Schema.String;
440
440
  }>>, never, Schema.toCodecJson<Schema.Struct<{
@@ -468,17 +468,23 @@ declare const JcfWebApi_base: HttpApi.HttpApi<"JcfWebApi", HttpApiGroup.HttpApiG
468
468
  readonly endMs: Schema.Number;
469
469
  readonly description: Schema.NullOr<Schema.String>;
470
470
  }>;
471
- }>>, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError | typeof ProposalRejectedError>, OwnerSessionAuth, never>, false> | HttpApiGroup.HttpApiGroup<"rows", HttpApiEndpoint.HttpApiEndpoint<"confirm", "POST", "/api/rows/confirm", never, never, Schema.toCodecJson<Schema.Struct<{
472
- readonly blocks: Schema.optional<Schema.$Array<Schema.Number>>;
473
- readonly note: Schema.optional<Schema.String>;
471
+ }>>, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError | typeof ProposalRejectedError>, OwnerSessionAuth, never>, false> | HttpApiGroup.HttpApiGroup<"rows", HttpApiEndpoint.HttpApiEndpoint<"describe", "POST", "/api/rows/describe", never, never, Schema.toCodecJson<Schema.Struct<{
474
472
  readonly planId: Schema.String;
475
473
  readonly rowId: Schema.String;
474
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
475
+ readonly planId: Schema.String;
476
+ readonly rowId: Schema.String;
477
+ readonly note: Schema.NullOr<Schema.String>;
478
+ }>>, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"manual", "POST", "/api/rows/manual", never, never, Schema.toCodecJson<Schema.Struct<{
479
+ readonly day: Schema.String;
480
+ readonly note: Schema.optional<Schema.String>;
481
+ readonly seconds: Schema.Number;
476
482
  readonly targets: Schema.optional<Schema.Struct<{
477
483
  readonly clockify: Schema.Boolean;
478
484
  readonly jira: Schema.Boolean;
479
485
  }>>;
480
- readonly seconds: Schema.optional<Schema.Number>;
481
- readonly ticketKey: Schema.optional<Schema.String>;
486
+ readonly startClock: Schema.optional<Schema.String>;
487
+ readonly ticketKey: Schema.String;
482
488
  }>>, never, Schema.toCodecJson<Schema.Struct<{
483
489
  readonly clockify: Schema.Union<readonly [Schema.TaggedStruct<"Written", {
484
490
  readonly seconds: Schema.Number;
@@ -518,23 +524,17 @@ declare const JcfWebApi_base: HttpApi.HttpApi<"JcfWebApi", HttpApiGroup.HttpApiG
518
524
  readonly message: Schema.String;
519
525
  }>, Schema.TaggedStruct<"NotLoggedIn", {}>]>;
520
526
  readonly lines: Schema.$Array<Schema.String>;
521
- }>>, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError | typeof ProposalRejectedError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"describe", "POST", "/api/rows/describe", never, never, Schema.toCodecJson<Schema.Struct<{
522
- readonly planId: Schema.String;
523
- readonly rowId: Schema.String;
524
- }>>, never, Schema.toCodecJson<Schema.Struct<{
527
+ }>>, Schema.toCodecJson<typeof ApiError | typeof ProposalRejectedError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"confirm", "POST", "/api/rows/confirm", never, never, Schema.toCodecJson<Schema.Struct<{
528
+ readonly blocks: Schema.optional<Schema.$Array<Schema.Number>>;
529
+ readonly note: Schema.optional<Schema.String>;
525
530
  readonly planId: Schema.String;
526
531
  readonly rowId: Schema.String;
527
- readonly note: Schema.NullOr<Schema.String>;
528
- }>>, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"manual", "POST", "/api/rows/manual", never, never, Schema.toCodecJson<Schema.Struct<{
529
- readonly day: Schema.String;
530
- readonly note: Schema.optional<Schema.String>;
531
- readonly seconds: Schema.Number;
532
532
  readonly targets: Schema.optional<Schema.Struct<{
533
533
  readonly clockify: Schema.Boolean;
534
534
  readonly jira: Schema.Boolean;
535
535
  }>>;
536
- readonly startClock: Schema.optional<Schema.String>;
537
- readonly ticketKey: Schema.String;
536
+ readonly seconds: Schema.optional<Schema.Number>;
537
+ readonly ticketKey: Schema.optional<Schema.String>;
538
538
  }>>, never, Schema.toCodecJson<Schema.Struct<{
539
539
  readonly clockify: Schema.Union<readonly [Schema.TaggedStruct<"Written", {
540
540
  readonly seconds: Schema.Number;
@@ -574,7 +574,110 @@ declare const JcfWebApi_base: HttpApi.HttpApi<"JcfWebApi", HttpApiGroup.HttpApiG
574
574
  readonly message: Schema.String;
575
575
  }>, Schema.TaggedStruct<"NotLoggedIn", {}>]>;
576
576
  readonly lines: Schema.$Array<Schema.String>;
577
- }>>, Schema.toCodecJson<typeof ApiError | typeof ProposalRejectedError>, OwnerSessionAuth, never>, false> | HttpApiGroup.HttpApiGroup<"week", HttpApiEndpoint.HttpApiEndpoint<"read", "GET", "/api/week/", never, Schema.toCodecStringTree<Schema.Struct<{
577
+ }>>, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError | typeof ProposalRejectedError>, OwnerSessionAuth, never>, false> | HttpApiGroup.HttpApiGroup<"week", HttpApiEndpoint.HttpApiEndpoint<"saved", "GET", "/api/week/saved", never, Schema.toCodecStringTree<Schema.Struct<{
578
+ readonly monday: Schema.optional<Schema.String>;
579
+ readonly only: Schema.optional<Schema.Literals<readonly ["both", "clockify", "jira"]>>;
580
+ }>>, never, never, Schema.toCodecJson<Schema.Struct<{
581
+ readonly monday: Schema.String;
582
+ readonly scope: Schema.Literals<readonly ["both", "clockify", "jira"]>;
583
+ readonly plan: Schema.NullOr<Schema.Struct<{
584
+ readonly attributorAvailable: Schema.Boolean;
585
+ readonly attributorCalls: Schema.Number;
586
+ readonly days: Schema.$Array<Schema.String>;
587
+ readonly excludedDays: Schema.$Array<Schema.Struct<{
588
+ readonly day: Schema.String;
589
+ readonly reason: Schema.String;
590
+ }>>;
591
+ readonly monday: Schema.String;
592
+ readonly planId: Schema.String;
593
+ readonly rows: Schema.$Array<Schema.Struct<{
594
+ readonly clockifyDescription: Schema.NullOr<Schema.String>;
595
+ readonly clockifySeconds: Schema.Number;
596
+ readonly day: Schema.String;
597
+ readonly intervals: Schema.$Array<Schema.Struct<{
598
+ readonly entry: Schema.optionalKey<Schema.Struct<{
599
+ readonly revision: Schema.String;
600
+ readonly id: Schema.String;
601
+ readonly source: Schema.Literals<readonly ["jira", "clockify"]>;
602
+ readonly ticketKey: Schema.NullOr<Schema.String>;
603
+ readonly startMs: Schema.Number;
604
+ readonly endMs: Schema.Number;
605
+ readonly description: Schema.NullOr<Schema.String>;
606
+ }>>;
607
+ readonly endMs: Schema.Number;
608
+ readonly source: Schema.Literals<readonly ["clockify", "jira"]>;
609
+ readonly startMs: Schema.Number;
610
+ }>>;
611
+ readonly jiraSeconds: Schema.Number;
612
+ readonly proposal: Schema.optional<Schema.Struct<{
613
+ readonly activeSeconds: Schema.Number;
614
+ readonly blocks: Schema.$Array<Schema.Struct<{
615
+ readonly consumed: Schema.Struct<{
616
+ readonly clockify: Schema.Number;
617
+ readonly jira: Schema.Number;
618
+ }>;
619
+ readonly clockifyRefusal: Schema.optionalKey<Schema.Literal<"unlinked-overlap">>;
620
+ readonly endMs: Schema.Number;
621
+ readonly seconds: Schema.Number;
622
+ readonly startMs: Schema.Number;
623
+ readonly sourceStartMs: Schema.optional<Schema.Number>;
624
+ }>>;
625
+ readonly clockifyDelta: Schema.Number;
626
+ readonly confidence: Schema.NullOr<Schema.Number>;
627
+ readonly jiraDelta: Schema.Number;
628
+ readonly maxSeconds: Schema.Number;
629
+ readonly sessionCount: Schema.Number;
630
+ readonly signal: Schema.Literals<readonly ["branch", "path", "standing", "agent", "none"]>;
631
+ }>>;
632
+ readonly rowId: Schema.String;
633
+ readonly ticketKey: Schema.String;
634
+ readonly ticketTitle: Schema.NullOr<Schema.String>;
635
+ }>>;
636
+ readonly unlinkedClockify: Schema.$Array<Schema.Struct<{
637
+ readonly entry: Schema.optionalKey<Schema.Struct<{
638
+ readonly revision: Schema.String;
639
+ readonly id: Schema.String;
640
+ readonly source: Schema.Literals<readonly ["jira", "clockify"]>;
641
+ readonly ticketKey: Schema.NullOr<Schema.String>;
642
+ readonly startMs: Schema.Number;
643
+ readonly endMs: Schema.Number;
644
+ readonly description: Schema.NullOr<Schema.String>;
645
+ }>>;
646
+ readonly day: Schema.String;
647
+ readonly seconds: Schema.Number;
648
+ readonly description: Schema.NullOr<Schema.String>;
649
+ readonly startMs: Schema.Number;
650
+ readonly endMs: Schema.Number;
651
+ }>>;
652
+ readonly sessionCount: Schema.Number;
653
+ readonly sessionScanAvailable: Schema.optional<Schema.Boolean>;
654
+ readonly scope: Schema.Literals<readonly ["both", "clockify", "jira"]>;
655
+ readonly sessionRootCount: Schema.Number;
656
+ readonly notMine: Schema.$Array<Schema.Struct<{
657
+ readonly assignee: Schema.NullOr<Schema.String>;
658
+ readonly day: Schema.String;
659
+ readonly seconds: Schema.Number;
660
+ readonly signal: Schema.Literals<readonly ["branch", "path", "standing", "agent", "none"]>;
661
+ readonly ticketKey: Schema.String;
662
+ readonly ticketTitle: Schema.NullOr<Schema.String>;
663
+ }>>;
664
+ readonly ownershipChecked: Schema.Boolean;
665
+ readonly ownership: Schema.Literals<readonly ["assigned", "any"]>;
666
+ readonly unattributed: Schema.$Array<Schema.Struct<{
667
+ readonly cwds: Schema.$Array<Schema.String>;
668
+ readonly day: Schema.String;
669
+ readonly seconds: Schema.Number;
670
+ readonly sessionCount: Schema.Number;
671
+ }>>;
672
+ readonly withheld: Schema.$Array<Schema.Struct<{
673
+ readonly confidence: Schema.NullOr<Schema.Number>;
674
+ readonly day: Schema.String;
675
+ readonly seconds: Schema.Number;
676
+ readonly ticketKey: Schema.String;
677
+ readonly ticketTitle: Schema.NullOr<Schema.String>;
678
+ }>>;
679
+ }>>;
680
+ }>>, never, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"read", "GET", "/api/week/", never, Schema.toCodecStringTree<Schema.Struct<{
578
681
  readonly monday: Schema.optional<Schema.String>;
579
682
  readonly only: Schema.optional<Schema.Literals<readonly ["both", "clockify", "jira"]>>;
580
683
  }>>, never, never, Schema.toCodecJson<Schema.Struct<{
@@ -678,110 +781,7 @@ declare const JcfWebApi_base: HttpApi.HttpApi<"JcfWebApi", HttpApiGroup.HttpApiG
678
781
  }>>, never, never, HttpApiSchema.StreamUint8Array, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"recordedOnly", "GET", "/api/week/recorded-only", never, Schema.toCodecStringTree<Schema.Struct<{
679
782
  readonly monday: Schema.optional<Schema.String>;
680
783
  readonly only: Schema.optional<Schema.Literals<readonly ["both", "clockify", "jira"]>>;
681
- }>>, never, never, HttpApiSchema.StreamUint8Array, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"saved", "GET", "/api/week/saved", never, Schema.toCodecStringTree<Schema.Struct<{
682
- readonly monday: Schema.optional<Schema.String>;
683
- readonly only: Schema.optional<Schema.Literals<readonly ["both", "clockify", "jira"]>>;
684
- }>>, never, never, Schema.toCodecJson<Schema.Struct<{
685
- readonly monday: Schema.String;
686
- readonly scope: Schema.Literals<readonly ["both", "clockify", "jira"]>;
687
- readonly plan: Schema.NullOr<Schema.Struct<{
688
- readonly attributorAvailable: Schema.Boolean;
689
- readonly attributorCalls: Schema.Number;
690
- readonly days: Schema.$Array<Schema.String>;
691
- readonly excludedDays: Schema.$Array<Schema.Struct<{
692
- readonly day: Schema.String;
693
- readonly reason: Schema.String;
694
- }>>;
695
- readonly monday: Schema.String;
696
- readonly planId: Schema.String;
697
- readonly rows: Schema.$Array<Schema.Struct<{
698
- readonly clockifyDescription: Schema.NullOr<Schema.String>;
699
- readonly clockifySeconds: Schema.Number;
700
- readonly day: Schema.String;
701
- readonly intervals: Schema.$Array<Schema.Struct<{
702
- readonly entry: Schema.optionalKey<Schema.Struct<{
703
- readonly revision: Schema.String;
704
- readonly id: Schema.String;
705
- readonly source: Schema.Literals<readonly ["jira", "clockify"]>;
706
- readonly ticketKey: Schema.NullOr<Schema.String>;
707
- readonly startMs: Schema.Number;
708
- readonly endMs: Schema.Number;
709
- readonly description: Schema.NullOr<Schema.String>;
710
- }>>;
711
- readonly endMs: Schema.Number;
712
- readonly source: Schema.Literals<readonly ["clockify", "jira"]>;
713
- readonly startMs: Schema.Number;
714
- }>>;
715
- readonly jiraSeconds: Schema.Number;
716
- readonly proposal: Schema.optional<Schema.Struct<{
717
- readonly activeSeconds: Schema.Number;
718
- readonly blocks: Schema.$Array<Schema.Struct<{
719
- readonly consumed: Schema.Struct<{
720
- readonly clockify: Schema.Number;
721
- readonly jira: Schema.Number;
722
- }>;
723
- readonly clockifyRefusal: Schema.optionalKey<Schema.Literal<"unlinked-overlap">>;
724
- readonly endMs: Schema.Number;
725
- readonly seconds: Schema.Number;
726
- readonly startMs: Schema.Number;
727
- readonly sourceStartMs: Schema.optional<Schema.Number>;
728
- }>>;
729
- readonly clockifyDelta: Schema.Number;
730
- readonly confidence: Schema.NullOr<Schema.Number>;
731
- readonly jiraDelta: Schema.Number;
732
- readonly maxSeconds: Schema.Number;
733
- readonly sessionCount: Schema.Number;
734
- readonly signal: Schema.Literals<readonly ["branch", "path", "standing", "agent", "none"]>;
735
- }>>;
736
- readonly rowId: Schema.String;
737
- readonly ticketKey: Schema.String;
738
- readonly ticketTitle: Schema.NullOr<Schema.String>;
739
- }>>;
740
- readonly unlinkedClockify: Schema.$Array<Schema.Struct<{
741
- readonly entry: Schema.optionalKey<Schema.Struct<{
742
- readonly revision: Schema.String;
743
- readonly id: Schema.String;
744
- readonly source: Schema.Literals<readonly ["jira", "clockify"]>;
745
- readonly ticketKey: Schema.NullOr<Schema.String>;
746
- readonly startMs: Schema.Number;
747
- readonly endMs: Schema.Number;
748
- readonly description: Schema.NullOr<Schema.String>;
749
- }>>;
750
- readonly day: Schema.String;
751
- readonly seconds: Schema.Number;
752
- readonly description: Schema.NullOr<Schema.String>;
753
- readonly startMs: Schema.Number;
754
- readonly endMs: Schema.Number;
755
- }>>;
756
- readonly sessionCount: Schema.Number;
757
- readonly sessionScanAvailable: Schema.optional<Schema.Boolean>;
758
- readonly scope: Schema.Literals<readonly ["both", "clockify", "jira"]>;
759
- readonly sessionRootCount: Schema.Number;
760
- readonly notMine: Schema.$Array<Schema.Struct<{
761
- readonly assignee: Schema.NullOr<Schema.String>;
762
- readonly day: Schema.String;
763
- readonly seconds: Schema.Number;
764
- readonly signal: Schema.Literals<readonly ["branch", "path", "standing", "agent", "none"]>;
765
- readonly ticketKey: Schema.String;
766
- readonly ticketTitle: Schema.NullOr<Schema.String>;
767
- }>>;
768
- readonly ownershipChecked: Schema.Boolean;
769
- readonly ownership: Schema.Literals<readonly ["assigned", "any"]>;
770
- readonly unattributed: Schema.$Array<Schema.Struct<{
771
- readonly cwds: Schema.$Array<Schema.String>;
772
- readonly day: Schema.String;
773
- readonly seconds: Schema.Number;
774
- readonly sessionCount: Schema.Number;
775
- }>>;
776
- readonly withheld: Schema.$Array<Schema.Struct<{
777
- readonly confidence: Schema.NullOr<Schema.Number>;
778
- readonly day: Schema.String;
779
- readonly seconds: Schema.Number;
780
- readonly ticketKey: Schema.String;
781
- readonly ticketTitle: Schema.NullOr<Schema.String>;
782
- }>>;
783
- }>>;
784
- }>>, never, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"stream", "GET", "/api/week/stream", never, Schema.toCodecStringTree<Schema.Struct<{
784
+ }>>, never, never, HttpApiSchema.StreamUint8Array, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError>, OwnerSessionAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"stream", "GET", "/api/week/stream", never, Schema.toCodecStringTree<Schema.Struct<{
785
785
  readonly monday: Schema.optional<Schema.String>;
786
786
  readonly only: Schema.optional<Schema.Literals<readonly ["both", "clockify", "jira"]>>;
787
787
  }>>, never, never, HttpApiSchema.StreamUint8Array, Schema.toCodecJson<typeof ApiError | typeof PlanExpiredError>, OwnerSessionAuth, never>, false>>;
@@ -1,8 +1,18 @@
1
1
  /** The same authenticated routes, bootstrap exchange and static client in production and tests. */
2
2
  import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
3
- import { FileSystem, Layer, Path } from "effect";
4
- import { HttpRouter } from "effect/http";
5
- /** True only when `candidate` is `root` itself or one of its path-segment descendants. */
6
- export declare const isWithinDirectory: (path: Path.Path, root: string, candidate: string) => boolean;
3
+ import { Layer, Path } from "effect";
4
+ /**
5
+ * Serves the built client at `root`, from the same origin as the API so the session cookie applies to
6
+ * both. Extensionless HTML navigations fall back to `index.html`; `no-cache` makes the browser
7
+ * revalidate, so a rebuilt `index.html` never points at hashed assets that no longer exist.
8
+ */
9
+ export declare const staticClient: (root: string) => Layer.Layer<never, import("effect/PlatformError").PlatformError, import("effect/FileSystem").FileSystem | import("effect/http/HttpPlatform").HttpPlatform | import("effect/http/HttpRouter").HttpRouter | Path.Path>;
10
+ /**
11
+ * The client `vite build` writes next to the compiled server. It reads host files, so give it the
12
+ * host's FileSystem even where the engine runs on another one.
13
+ */
14
+ export declare const StaticRouter: Layer.Layer<never, import("effect/PlatformError").BadArgument | import("effect/PlatformError").PlatformError, import("effect/FileSystem").FileSystem | import("effect/http/HttpPlatform").HttpPlatform | import("effect/http/HttpRouter").HttpRouter | Path.Path>;
15
+ /** The authenticated API and the bootstrap exchange: everything that runs on the engine's services. */
16
+ export declare const apiApplication: Layer.Layer<never, never, import("@knpkv/jira-clockify/services/ConfigService.js").ConfigService | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("effect/http/HttpRouter").HttpRouter | import("@knpkv/jira-clockify/services/IssueFacts.js").IssueFacts | import("@knpkv/jira-api-client").JiraApiClient | import("@knpkv/jira-cli/JiraAuth").JiraAuth | OwnerSession.OwnerSession | Path.Path | import("@knpkv/jira-clockify/services/ReconcileService.js").ReconcileService | import("effect/http/HttpRouter").Request<"Error", import("effect/http/HttpBody").HttpBodyError> | import("@knpkv/jira-clockify/services/SavedEntries.js").SavedEntries | import("@knpkv/jira-clockify/services/SessionAttributor.js").SessionAttributor>;
7
17
  /** Supply engine services and owner secrets; listener and platform belong to the executable. */
8
- export declare const application: Layer.Layer<never, never, import("@knpkv/jira-clockify/services/ConfigService.js").ConfigService | import("effect/Crypto").Crypto | FileSystem.FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpRouter.HttpRouter | import("@knpkv/jira-clockify/services/IssueFacts.js").IssueFacts | import("@knpkv/jira-api-client").JiraApiClient | import("@knpkv/jira-cli/JiraAuth").JiraAuth | OwnerSession.OwnerSession | Path.Path | import("@knpkv/jira-clockify/services/ReconcileService.js").ReconcileService | HttpRouter.Request<"Error", import("effect/PlatformError").BadArgument> | HttpRouter.Request<"Error", import("effect/http/HttpBody").HttpBodyError> | HttpRouter.Request<"Error", import("effect/PlatformError").PlatformError> | HttpRouter.Request<"Requires", FileSystem.FileSystem> | HttpRouter.Request<"Requires", Path.Path> | import("@knpkv/jira-clockify/services/SavedEntries.js").SavedEntries | import("@knpkv/jira-clockify/services/SessionAttributor.js").SessionAttributor>;
18
+ export declare const application: Layer.Layer<never, never, import("@knpkv/jira-clockify/services/ConfigService.js").ConfigService | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("effect/http/HttpRouter").HttpRouter | import("@knpkv/jira-clockify/services/IssueFacts.js").IssueFacts | import("@knpkv/jira-api-client").JiraApiClient | import("@knpkv/jira-cli/JiraAuth").JiraAuth | OwnerSession.OwnerSession | Path.Path | import("@knpkv/jira-clockify/services/ReconcileService.js").ReconcileService | import("effect/http/HttpRouter").Request<"Error", import("effect/http/HttpBody").HttpBodyError> | import("@knpkv/jira-clockify/services/SavedEntries.js").SavedEntries | import("@knpkv/jira-clockify/services/SessionAttributor.js").SessionAttributor>;
@@ -1,63 +1,29 @@
1
1
  /** The same authenticated routes, bootstrap exchange and static client in production and tests. */
2
2
  import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
3
- import { Effect, FileSystem, Layer, Path } from "effect";
4
- import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
3
+ import { Effect, Layer, Path } from "effect";
4
+ import { HttpStaticServer } from "effect/http";
5
5
  import { HttpApiBuilder } from "effect/http-api";
6
6
  import { JcfWebApi } from "./Api.js";
7
7
  import { ConfigLive, EntriesLive, RowsLive, WeekLive } from "./Handlers.js";
8
8
  import { ownerSessionAuthLayer } from "./OwnerSession.js";
9
9
  import { layer as weekPlansLayer } from "./WeekPlans.js";
10
- const mimeTypes = new Map([
11
- [".css", "text/css"],
12
- [".html", "text/html"],
13
- [".ico", "image/x-icon"],
14
- [".js", "application/javascript"],
15
- [".json", "application/json"],
16
- [".map", "application/json"],
17
- [".svg", "image/svg+xml"],
18
- [".woff2", "font/woff2"]
19
- ]);
20
- /** True only when `candidate` is `root` itself or one of its path-segment descendants. */
21
- export const isWithinDirectory = (path, root, candidate) => {
22
- const relative = path.relative(root, candidate);
23
- return relative === "" || (!path.isAbsolute(relative) && relative !== ".." &&
24
- !relative.startsWith(`..${path.sep}`));
25
- };
26
- /** The built client, served from the same origin so the session cookie applies to both. */
27
- const serveStatic = Effect.gen(function* () {
28
- const request = yield* HttpServerRequest.HttpServerRequest;
29
- const fileSystem = yield* FileSystem.FileSystem;
10
+ /**
11
+ * Serves the built client at `root`, from the same origin as the API so the session cookie applies to
12
+ * both. Extensionless HTML navigations fall back to `index.html`; `no-cache` makes the browser
13
+ * revalidate, so a rebuilt `index.html` never points at hashed assets that no longer exist.
14
+ */
15
+ export const staticClient = (root) => HttpStaticServer.layer({ cacheControl: "no-cache", root, spa: true });
16
+ /**
17
+ * The client `vite build` writes next to the compiled server. It reads host files, so give it the
18
+ * host's FileSystem even where the engine runs on another one.
19
+ */
20
+ export const StaticRouter = Layer.unwrap(Effect.gen(function* () {
30
21
  const path = yield* Path.Path;
31
- const requested = decodeURIComponent(new URL(request.url, "http://localhost").pathname).replace(/^\/+/u, "");
32
- const here = yield* path.fromFileUrl(new URL(".", import.meta.url));
33
- const staticDirectory = path.resolve(here, "../../dist/client");
34
- const resolved = path.resolve(staticDirectory, requested === "" ? "index.html" : requested);
35
- // A resolved path that escaped the directory is a traversal attempt, not a missing file.
36
- if (!isWithinDirectory(path, staticDirectory, resolved)) {
37
- return HttpServerResponse.text("Forbidden", { status: 403 });
38
- }
39
- if (yield* fileSystem.exists(resolved)) {
40
- const info = yield* fileSystem.stat(resolved);
41
- if (info.type === "File") {
42
- return HttpServerResponse.uint8Array(yield* fileSystem.readFile(resolved), {
43
- headers: { "content-type": mimeTypes.get(path.extname(resolved)) ?? "application/octet-stream" },
44
- status: 200
45
- });
46
- }
47
- }
48
- const index = path.join(staticDirectory, "index.html");
49
- if (yield* fileSystem.exists(index)) {
50
- return HttpServerResponse.uint8Array(yield* fileSystem.readFile(index), {
51
- headers: { "content-type": "text/html" },
52
- status: 200
53
- });
54
- }
55
- return HttpServerResponse.text("The client has not been built. Run: pnpm --filter @knpkv/jcf-web build", {
56
- status: 404
57
- });
58
- });
59
- const StaticRouter = HttpRouter.use((router) => router.add("GET", "/*", serveStatic));
22
+ return staticClient(yield* path.fromFileUrl(new URL("../../dist/client", import.meta.url)));
23
+ }));
60
24
  const ApiRoutes = HttpApiBuilder.layer(JcfWebApi).pipe(Layer.provide(Layer.mergeAll(WeekLive, RowsLive, ConfigLive, EntriesLive)), Layer.provide(ownerSessionAuthLayer), Layer.provide(weekPlansLayer));
25
+ /** The authenticated API and the bootstrap exchange: everything that runs on the engine's services. */
26
+ export const apiApplication = Layer.mergeAll(ApiRoutes, OwnerSession.BootstrapRouter).pipe(Layer.orDie);
61
27
  /** Supply engine services and owner secrets; listener and platform belong to the executable. */
62
- export const application = Layer.mergeAll(ApiRoutes, OwnerSession.BootstrapRouter, StaticRouter).pipe(Layer.orDie);
28
+ export const application = Layer.mergeAll(apiApplication, StaticRouter).pipe(Layer.orDie);
63
29
  //# sourceMappingURL=HttpApplication.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HttpApplication.js","sourceRoot":"","sources":["../../src/server/HttpApplication.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HttpApplication.js","sourceRoot":"","sources":["../../src/server/HttpApplication.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAA;AAEnH;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,mBAAmB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAC7F,CAAC,CAAC,CAAC,CAAA;AAEH,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CACpD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,EAC1E,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,EACpC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAC9B,CAAA;AAED,uGAAuG;AACvG,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAEvG,gGAAgG;AAChG,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA"}