@catheadowl/ukp 0.1.1 → 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.
package/src/server.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { dirname, join } from "node:path";
2
- import { readFileSync, writeFileSync } from "node:fs";
2
+ import { readFileSync, writeFileSync, fstatSync, constants as fsConstants } from "node:fs";
3
+ import { createServer as netCreateServer, connect as netConnect, type Server as NetServer } from "node:net";
3
4
  import { ENDPOINT_NAME, loadManifest, type LoadedManifest } from "./config/manifest.ts";
4
5
  import { localPathOf, readRegistry } from "./registry.ts";
5
6
  import { FILE_NATIVE_CAPABILITIES, resolveFileNativeCapability } from "./config/file-native.ts";
@@ -83,11 +84,24 @@ export interface ServeConfig {
83
84
  * identity under the Service folder (`.ukp/tls/`) — in door mode under the
84
85
  * registry's directory, since a door has no single Service folder;
85
86
  * `certificates` serves operator-provided PEM files (Let's Encrypt IP
86
- * certs, mkcert, private CA). `opensslCommand` is a test injection point
87
- * for the self-signing. */
87
+ * certs, mkcert, private CA). `sanEntries` (the `--tls-san` flag, already
88
+ * normalized `IP:x`/`DNS:y`) merges extra coverage into the self-signed
89
+ * SAN — a NAT/EIP public address no NIC carries; a persisted certificate
90
+ * lacking a requested entry is re-signed over the same key (pin unchanged,
91
+ * clients re-anchor). `opensslCommand` is a test injection point for the
92
+ * self-signing. */
88
93
  tls?:
89
- | { mode: "self-signed"; opensslCommand?: readonly string[] }
94
+ | { mode: "self-signed"; opensslCommand?: readonly string[]; sanEntries?: readonly string[] }
90
95
  | { mode: "certificates"; certPath: string; keyPath: string };
96
+ /** W9 / ADR-REM-006: self-reap after this many seconds without requests.
97
+ * The orphan backstop for on-demand-woken doors — a no-TTY remote command
98
+ * can escape SIGHUP on an abrupt disconnect, so the door bounds its own
99
+ * lifetime instead of trusting the session to reap it. */
100
+ maxIdleSeconds?: number;
101
+ /** W10 / ADR-REM-006: serve on the systemd socket-activation listener
102
+ * (LISTEN_FDS fd 3) instead of binding a port — the port belongs to the
103
+ * socket unit; the process only exists while in use. Linux-only. */
104
+ systemdSocket?: boolean;
91
105
  }
92
106
 
93
107
  export interface DiscoveryDocument {
@@ -136,11 +150,57 @@ export interface ServeInfo {
136
150
  port: number;
137
151
  authRequired: boolean;
138
152
  /** TLS identity summary (W5' / D-079) when serving over HTTPS; the pin is
139
- * what remote clients TOFU-pin at registration. */
153
+ * what remote clients TOFU-pin at registration. `source` "re-signed" marks
154
+ * the one start that grew SAN coverage over the existing key (--tls-san
155
+ * added an entry the persisted certificate lacked) — pin unchanged. */
140
156
  tls?: {
141
157
  pin: string;
142
158
  san: string;
143
- source: "generated" | "persisted" | "operator";
159
+ source: "generated" | "re-signed" | "persisted" | "operator";
160
+ };
161
+ /** Present when --max-idle armed the self-reap timer (W9). */
162
+ maxIdleSeconds?: number;
163
+ }
164
+
165
+ /** W9 / ADR-REM-006 (--max-idle): wrap a serve handler so the listener stops
166
+ * itself after `maxIdleSeconds` without requests. Requests re-arm the timer
167
+ * on entry and again once the response settles; the exit mirrors the SIGINT
168
+ * path (stderr line + server.stop(true)), after which the process exits with
169
+ * its already-set code. The timer is unref'd so a SIGINT/SIGTERM stop is
170
+ * immediate — the listener, not the timer, is what holds the serve process
171
+ * open. `stopped` fences the settle-rearm of a request that outlived the
172
+ * window (no second fire after the first stop). The bind indirection exists
173
+ * because Bun.serve hands back the server only after `fetch` is fixed. */
174
+ function armIdleExit(
175
+ handler: (request: Request) => Response | Promise<Response>,
176
+ maxIdleSeconds: number,
177
+ ): { fetch: (request: Request) => Response | Promise<Response>; bind: (stop: () => void) => void } {
178
+ const control = { stop: (): void => {} };
179
+ let timer: ReturnType<typeof setTimeout> | undefined;
180
+ let stopped = false;
181
+ const rearm = () => {
182
+ if (stopped) return;
183
+ if (timer !== undefined) clearTimeout(timer);
184
+ timer = setTimeout(() => {
185
+ stopped = true;
186
+ console.error(`ukp serve: idle exit (--max-idle ${maxIdleSeconds}s without requests)`);
187
+ control.stop();
188
+ }, maxIdleSeconds * 1000);
189
+ // Best-effort unref: present in Bun and Node; a missing no-op keeps this
190
+ // portable without changing the firing behavior while serving.
191
+ (timer as { unref?: () => void }).unref?.();
192
+ };
193
+ rearm();
194
+ return {
195
+ fetch: (request: Request) => {
196
+ rearm();
197
+ const response = handler(request);
198
+ Promise.resolve(response).then(rearm, () => {});
199
+ return response;
200
+ },
201
+ bind(stop: () => void) {
202
+ control.stop = stop;
203
+ },
144
204
  };
145
205
  }
146
206
 
@@ -449,45 +509,202 @@ function parseRgParams(
449
509
  export interface StartedServe {
450
510
  server: ReturnType<typeof Bun.serve>;
451
511
  info: ServeInfo;
512
+ /** Composite stop: in socket-activation mode this also closes the fd3
513
+ * acceptor (Bun.serve's own stop alone would leave the process alive
514
+ * holding the systemd-passed listener). Safe to call from the SIGINT/
515
+ * SIGTERM/SIGHUP handlers and the idle-exit path alike. */
516
+ stopAll: () => void;
517
+ }
518
+
519
+ /** W10 / ADR-REM-006: parse the systemd LISTEN_FDS convention — exactly one
520
+ * listening fd expected (fd 3), PID-guarded, and the variables are CONSUMED
521
+ * (unset) so any child process does not double-count inherited activation
522
+ * fds (the podman fd-counting pitfall, knowledge §5.2). */
523
+ const S_ISSOCK = fsConstants.S_IFSOCK;
524
+
525
+ export function parseListenFds(env: NodeJS.ProcessEnv): { fd: number } | { error: string } {
526
+ if ((env.LISTEN_FDS ?? "") === "" || (env.LISTEN_PID ?? "") === "") {
527
+ return {
528
+ error:
529
+ "--systemd-socket requires systemd socket activation: LISTEN_FDS/LISTEN_PID are not set (run under a .socket unit or systemd-socket-activate)",
530
+ };
531
+ }
532
+ if (Number(env.LISTEN_PID) !== process.pid) {
533
+ return { error: `LISTEN_PID ${env.LISTEN_PID} does not match this process (${process.pid}) — inherited activation variables belong to another process` };
534
+ }
535
+ const count = Number(env.LISTEN_FDS);
536
+ if (!Number.isSafeInteger(count) || count < 1) {
537
+ return { error: `LISTEN_FDS=${String(env.LISTEN_FDS)} is not a positive integer` };
538
+ }
539
+ if (count !== 1) {
540
+ return { error: `expected exactly one listening fd (LISTEN_FDS=1), got ${count} — run one socket unit per serve instance` };
541
+ }
542
+ delete env.LISTEN_FDS;
543
+ delete env.LISTEN_PID;
544
+ return { fd: 3 };
545
+ }
546
+
547
+ /** W10 / ADR-REM-006: the in-process bridge. Bun.serve cannot adopt a raw
548
+ * listening fd (no public API; ali probe 2026-09-19), but node:net can
549
+ * (`createServer().listen({fd: 3})` verified under bun). Each connection the
550
+ * systemd-held listener accepts is piped to the real Bun.serve listener on
551
+ * an ephemeral loopback port — the fake-ssh proxy pattern, in-process. */
552
+ export function createSocketBridge(acceptor: NetServer, targetPort: number): { destroyAll: () => void } {
553
+ // Live connections are tracked so stopAll can force-close the pairs — a
554
+ // peer that receives FIN but never closes (half-open keep-alive shape)
555
+ // would otherwise hold the process open after the idle exit forever
556
+ // (review P2: probe-confirmed liveness hang), letting stale serve
557
+ // processes accumulate beside systemd's fresh spawns.
558
+ const live = new Set<{ socket: import("node:net").Socket; upstream: import("node:net").Socket }>();
559
+ acceptor.on("connection", (socket) => {
560
+ const upstream = netConnect({ host: "127.0.0.1", port: targetPort });
561
+ const pair = { socket, upstream };
562
+ live.add(pair);
563
+ socket.pipe(upstream);
564
+ upstream.pipe(socket);
565
+ const drop = () => {
566
+ live.delete(pair);
567
+ socket.destroy();
568
+ upstream.destroy();
569
+ };
570
+ socket.on("error", drop);
571
+ upstream.on("error", drop);
572
+ socket.on("close", () => {
573
+ live.delete(pair);
574
+ upstream.destroy();
575
+ });
576
+ upstream.on("close", () => {
577
+ live.delete(pair);
578
+ socket.destroy();
579
+ });
580
+ });
581
+ return {
582
+ destroyAll() {
583
+ for (const pair of live) {
584
+ pair.socket.destroy();
585
+ pair.upstream.destroy();
586
+ }
587
+ live.clear();
588
+ },
589
+ };
590
+ }
591
+
592
+ /** One serve listener, both modes (W10): plain binding by default; under
593
+ * `systemdSocket` the systemd-passed fd 3 is adopted via node:net and
594
+ * bridged to the real Bun.serve listener on an ephemeral loopback port. */
595
+ function startServeListener(
596
+ handler: (request: Request) => Response | Promise<Response>,
597
+ options: {
598
+ host: string;
599
+ port?: number;
600
+ tlsMaterial?: { cert: string; key: string };
601
+ maxIdleSeconds?: number;
602
+ systemdSocket?: boolean;
603
+ },
604
+ ): { server: ReturnType<typeof Bun.serve>; stopAll: () => void; publicUrl: string; port: number } {
605
+ const idle = options.maxIdleSeconds !== undefined ? armIdleExit(handler, options.maxIdleSeconds) : undefined;
606
+ const fetch = idle?.fetch ?? handler;
607
+ const tls = options.tlsMaterial !== undefined ? { tls: { cert: options.tlsMaterial.cert, key: options.tlsMaterial.key } } : {};
608
+ const scheme = options.tlsMaterial !== undefined ? "https" : "http";
609
+
610
+ if (options.systemdSocket !== true) {
611
+ const server = Bun.serve({ hostname: options.host, port: options.port ?? 8570, ...tls, fetch });
612
+ idle?.bind(() => server.stop(true));
613
+ const port = server.port ?? (options.port ?? 8570);
614
+ return { server, stopAll: () => server.stop(true), publicUrl: `${scheme}://${options.host}:${port}`, port };
615
+ }
616
+
617
+ if (process.platform !== "linux") {
618
+ throw new ServeSetupError("--systemd-socket is Linux-only (systemd socket activation)");
619
+ }
620
+ const parsed = parseListenFds(process.env);
621
+ if ("error" in parsed) throw new ServeSetupError(parsed.error);
622
+ // Validate the fd synchronously: systemd passes a bound LISTENING socket;
623
+ // anything else (EBADF, wrong type) must fail setup, not crash later.
624
+ let isSocket = false;
625
+ try {
626
+ const stats = fstatSync(parsed.fd);
627
+ isSocket = (stats.mode & S_ISSOCK) === S_ISSOCK;
628
+ } catch {
629
+ isSocket = false;
630
+ }
631
+ if (!isSocket) {
632
+ throw new ServeSetupError(`fd ${parsed.fd} is not an open socket — socket activation must pass a bound listening socket`);
633
+ }
634
+ const server = Bun.serve({ hostname: "127.0.0.1", port: 0, ...tls, fetch });
635
+ const acceptor = netCreateServer();
636
+ const bridge = createSocketBridge(acceptor, server.port ?? 0);
637
+ acceptor.on("error", (error) => {
638
+ // Async setup failure channel (e.g. fd 3 is a CONNECTED socket — passes
639
+ // the S_IFSOCK pre-check but cannot listen): no hard exit from library
640
+ // code — report, stop both listeners, and let the loop drain with a
641
+ // failing exit code.
642
+ console.error(`ukp serve: fd${parsed.fd} acceptor failed: ${error instanceof Error ? error.message : String(error)}`);
643
+ process.exitCode = 1;
644
+ acceptor.close();
645
+ bridge.destroyAll();
646
+ server.stop(true);
647
+ });
648
+ acceptor.listen({ fd: parsed.fd });
649
+ const stopAll = () => {
650
+ acceptor.close();
651
+ bridge.destroyAll();
652
+ server.stop(true);
653
+ };
654
+ idle?.bind(stopAll);
655
+ return {
656
+ server,
657
+ stopAll,
658
+ publicUrl: `systemd:fd${parsed.fd} (bridge ${scheme}://127.0.0.1:${server.port})`,
659
+ port: server.port ?? 0,
660
+ };
452
661
  }
453
662
 
454
663
  /** TLS material resolution (W5' / D-079), shared by both serve modes: a
455
- * self-signed identity is generated/persisted under `identityDir` (the
456
- * Service folder's `.ukp/tls/` in endpoint mode, the registry directory in
457
- * door mode — a door has no single Service folder), explicit certificates
664
+ * self-signed identity is generated/re-signed/persisted under `identityDir`
665
+ * (the Service folder's `.ukp/tls/` in endpoint mode, the registry directory
666
+ * in door mode — a door has no single Service folder), explicit certificates
458
667
  * are read from the operator's paths. Bun.serve takes PEM contents
459
- * (path-string handling is platform-dependent). */
668
+ * (path-string handling is platform-dependent). Every failure — unreadable
669
+ * operator files, an unusable persisted identity, openssl unable to sign —
670
+ * surfaces as one ServeSetupError naming the certificate path. */
460
671
  function resolveTlsMaterial(
461
672
  config: ServeConfig,
462
673
  identityDir: string,
463
- ): { cert: string; key: string; pin: string; san: string; source: "generated" | "persisted" | "operator" } | undefined {
674
+ ): { cert: string; key: string; pin: string; san: string; source: "generated" | "re-signed" | "persisted" | "operator" } | undefined {
464
675
  if (config.tls === undefined) return undefined;
465
676
  const tls = config.tls;
466
- const paths = tls.mode === "self-signed"
467
- ? (() => {
468
- const identity = ensureSelfSignedTlsFiles(
469
- identityDir,
470
- tls.mode === "self-signed" && tls.opensslCommand !== undefined ? { opensslCommand: tls.opensslCommand } : {},
471
- );
472
- return {
473
- certPath: identity.certPath,
474
- keyPath: identity.keyPath,
475
- source: identity.created ? ("generated" as const) : ("persisted" as const),
476
- };
477
- })()
478
- : { certPath: tls.certPath, keyPath: tls.keyPath, source: "operator" as const };
677
+ let certPath: string | undefined;
479
678
  try {
480
- const cert = readFileSync(paths.certPath, "utf8");
679
+ let keyPath: string;
680
+ let source: "generated" | "re-signed" | "persisted" | "operator";
681
+ if (tls.mode === "self-signed") {
682
+ const identity = ensureSelfSignedTlsFiles(
683
+ identityDir,
684
+ {
685
+ ...(tls.opensslCommand !== undefined ? { opensslCommand: tls.opensslCommand } : {}),
686
+ ...(tls.sanEntries !== undefined ? { extraSanEntries: tls.sanEntries } : {}),
687
+ },
688
+ );
689
+ certPath = identity.certPath;
690
+ keyPath = identity.keyPath;
691
+ source = identity.source;
692
+ } else {
693
+ certPath = tls.certPath;
694
+ keyPath = tls.keyPath;
695
+ source = "operator";
696
+ }
697
+ const cert = readFileSync(certPath, "utf8");
481
698
  return {
482
699
  cert,
483
- key: readFileSync(paths.keyPath, "utf8"),
700
+ key: readFileSync(keyPath, "utf8"),
484
701
  pin: spkiPinOf(cert),
485
702
  san: certSanOf(cert),
486
- source: paths.source,
703
+ source,
487
704
  };
488
705
  } catch (error) {
489
706
  const reason = error instanceof Error ? error.message : String(error);
490
- throw new ServeSetupError(`TLS material is unusable (${paths.certPath}): ${reason}`);
707
+ throw new ServeSetupError(`TLS material is unusable (${certPath ?? join(identityDir, "cert.pem")}): ${reason}`);
491
708
  }
492
709
  }
493
710
 
@@ -650,12 +867,14 @@ function startDoorServer(config: ServeConfig, host: string, tlsMaterial: ReturnT
650
867
  return doorNotFound(undefined, config.registryPath);
651
868
  };
652
869
 
653
- const server = Bun.serve({
654
- hostname: host,
655
- port: config.port ?? 8570,
656
- ...(tlsMaterial !== undefined ? { tls: { cert: tlsMaterial.cert, key: tlsMaterial.key } } : {}),
657
- fetch: handler,
870
+ const listener = startServeListener(handler, {
871
+ host,
872
+ ...(config.port !== undefined ? { port: config.port } : {}),
873
+ ...(tlsMaterial !== undefined ? { tlsMaterial } : {}),
874
+ ...(config.maxIdleSeconds !== undefined ? { maxIdleSeconds: config.maxIdleSeconds } : {}),
875
+ ...(config.systemdSocket === true ? { systemdSocket: true } : {}),
658
876
  });
877
+ const { server } = listener;
659
878
 
660
879
  const info: ServeInfo = {
661
880
  mode: "door",
@@ -663,15 +882,16 @@ function startDoorServer(config: ServeConfig, host: string, tlsMaterial: ReturnT
663
882
  endpoints: listDoorEndpoints(config.registryPath).map((endpoint) => endpoint.name),
664
883
  write: listWriteEndpoints(config.registryPath),
665
884
  },
666
- url: `${tlsMaterial !== undefined ? "https" : "http"}://${host}:${server.port ?? (config.port ?? 8570)}`,
885
+ url: listener.publicUrl,
667
886
  host,
668
- port: server.port ?? (config.port ?? 8570),
887
+ port: listener.port,
669
888
  authRequired: (config.tokens?.length ?? 0) > 0,
670
889
  ...(tlsMaterial !== undefined
671
890
  ? { tls: { pin: tlsMaterial.pin, san: tlsMaterial.san, source: tlsMaterial.source } }
672
891
  : {}),
892
+ ...(config.maxIdleSeconds !== undefined ? { maxIdleSeconds: config.maxIdleSeconds } : {}),
673
893
  };
674
- return { server, info };
894
+ return { server, info, stopAll: listener.stopAll };
675
895
  }
676
896
 
677
897
  export function startUkpServer(config: ServeConfig): StartedServe {
@@ -781,27 +1001,30 @@ export function startUkpServer(config: ServeConfig): StartedServe {
781
1001
  );
782
1002
  };
783
1003
 
784
- const server = Bun.serve({
785
- hostname: host,
786
- port: config.port ?? 8570,
787
- ...(tlsMaterial !== undefined ? { tls: { cert: tlsMaterial.cert, key: tlsMaterial.key } } : {}),
788
- fetch: handler,
1004
+ const listener = startServeListener(handler, {
1005
+ host,
1006
+ ...(config.port !== undefined ? { port: config.port } : {}),
1007
+ ...(tlsMaterial !== undefined ? { tlsMaterial } : {}),
1008
+ ...(config.maxIdleSeconds !== undefined ? { maxIdleSeconds: config.maxIdleSeconds } : {}),
1009
+ ...(config.systemdSocket === true ? { systemdSocket: true } : {}),
789
1010
  });
1011
+ const { server } = listener;
790
1012
 
791
1013
  const info: ServeInfo = {
792
1014
  mode: "endpoint",
793
1015
  endpoint: binding.name,
794
1016
  folder: serviceFolder,
795
1017
  instanceUid: readInstanceUid(serviceFolder),
796
- url: `${tlsMaterial !== undefined ? "https" : "http"}://${host}:${server.port ?? (config.port ?? 8570)}`,
1018
+ url: listener.publicUrl,
797
1019
  host,
798
- port: server.port ?? (config.port ?? 8570),
1020
+ port: listener.port,
799
1021
  authRequired: (config.tokens?.length ?? 0) > 0,
800
1022
  ...(tlsMaterial !== undefined
801
1023
  ? { tls: { pin: tlsMaterial.pin, san: tlsMaterial.san, source: tlsMaterial.source } }
802
1024
  : {}),
1025
+ ...(config.maxIdleSeconds !== undefined ? { maxIdleSeconds: config.maxIdleSeconds } : {}),
803
1026
  };
804
- return { server, info };
1027
+ return { server, info, stopAll: listener.stopAll };
805
1028
  }
806
1029
 
807
1030
  async function handleSearch(
package/bun.lock DELETED
@@ -1,35 +0,0 @@
1
- {
2
- "lockfileVersion": 1,
3
- "configVersion": 1,
4
- "workspaces": {
5
- "": {
6
- "name": "@catheadowl/ukp",
7
- "dependencies": {
8
- "commander": "15.0.0",
9
- "smol-toml": "1.7.1",
10
- "zod": "4.4.3",
11
- },
12
- "devDependencies": {
13
- "@types/bun": "1.3.5",
14
- "typescript": "5.9.3",
15
- },
16
- },
17
- },
18
- "packages": {
19
- "@types/bun": ["@types/bun@1.3.5", "https://registry.npmmirror.com/@types/bun/-/bun-1.3.5.tgz", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
20
-
21
- "@types/node": ["@types/node@26.1.1", "https://registry.npmmirror.com/@types/node/-/node-26.1.1.tgz", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="],
22
-
23
- "bun-types": ["bun-types@1.3.5", "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.5.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
24
-
25
- "commander": ["commander@15.0.0", "https://registry.npmmirror.com/commander/-/commander-15.0.0.tgz", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="],
26
-
27
- "smol-toml": ["smol-toml@1.7.1", "https://registry.npmmirror.com/smol-toml/-/smol-toml-1.7.1.tgz", {}, "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ=="],
28
-
29
- "typescript": ["typescript@5.9.3", "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
30
-
31
- "undici-types": ["undici-types@8.3.0", "https://registry.npmmirror.com/undici-types/-/undici-types-8.3.0.tgz", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
32
-
33
- "zod": ["zod@4.4.3", "https://registry.npmmirror.com/zod/-/zod-4.4.3.tgz", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
34
- }
35
- }
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "strict": true,
7
- "noEmit": true,
8
- "allowImportingTsExtensions": true,
9
- "types": ["bun"]
10
- },
11
- "include": ["src", "test"]
12
- }