codeblog-app 2.3.1 → 2.3.2

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 (83) hide show
  1. package/package.json +8 -73
  2. package/drizzle/0000_init.sql +0 -34
  3. package/drizzle/meta/_journal.json +0 -13
  4. package/drizzle.config.ts +0 -10
  5. package/src/ai/__tests__/chat.test.ts +0 -188
  6. package/src/ai/__tests__/compat.test.ts +0 -46
  7. package/src/ai/__tests__/home.ai-stream.integration.test.ts +0 -77
  8. package/src/ai/__tests__/provider-registry.test.ts +0 -61
  9. package/src/ai/__tests__/provider.test.ts +0 -238
  10. package/src/ai/__tests__/stream-events.test.ts +0 -152
  11. package/src/ai/__tests__/tools.test.ts +0 -93
  12. package/src/ai/chat.ts +0 -336
  13. package/src/ai/configure.ts +0 -143
  14. package/src/ai/models.ts +0 -26
  15. package/src/ai/provider-registry.ts +0 -150
  16. package/src/ai/provider.ts +0 -264
  17. package/src/ai/stream-events.ts +0 -64
  18. package/src/ai/tools.ts +0 -118
  19. package/src/ai/types.ts +0 -105
  20. package/src/auth/index.ts +0 -49
  21. package/src/auth/oauth.ts +0 -123
  22. package/src/cli/__tests__/commands.test.ts +0 -229
  23. package/src/cli/cmd/agent.ts +0 -97
  24. package/src/cli/cmd/ai.ts +0 -10
  25. package/src/cli/cmd/chat.ts +0 -190
  26. package/src/cli/cmd/comment.ts +0 -67
  27. package/src/cli/cmd/config.ts +0 -153
  28. package/src/cli/cmd/feed.ts +0 -53
  29. package/src/cli/cmd/forum.ts +0 -106
  30. package/src/cli/cmd/login.ts +0 -45
  31. package/src/cli/cmd/logout.ts +0 -12
  32. package/src/cli/cmd/me.ts +0 -188
  33. package/src/cli/cmd/post.ts +0 -25
  34. package/src/cli/cmd/publish.ts +0 -64
  35. package/src/cli/cmd/scan.ts +0 -78
  36. package/src/cli/cmd/search.ts +0 -35
  37. package/src/cli/cmd/setup.ts +0 -622
  38. package/src/cli/cmd/tui.ts +0 -20
  39. package/src/cli/cmd/uninstall.ts +0 -281
  40. package/src/cli/cmd/update.ts +0 -123
  41. package/src/cli/cmd/vote.ts +0 -50
  42. package/src/cli/cmd/whoami.ts +0 -18
  43. package/src/cli/mcp-print.ts +0 -6
  44. package/src/cli/ui.ts +0 -357
  45. package/src/config/index.ts +0 -92
  46. package/src/flag/index.ts +0 -23
  47. package/src/global/index.ts +0 -38
  48. package/src/id/index.ts +0 -20
  49. package/src/index.ts +0 -203
  50. package/src/mcp/__tests__/client.test.ts +0 -149
  51. package/src/mcp/__tests__/e2e.ts +0 -331
  52. package/src/mcp/__tests__/integration.ts +0 -148
  53. package/src/mcp/client.ts +0 -118
  54. package/src/server/index.ts +0 -48
  55. package/src/storage/chat.ts +0 -73
  56. package/src/storage/db.ts +0 -85
  57. package/src/storage/schema.sql.ts +0 -39
  58. package/src/storage/schema.ts +0 -1
  59. package/src/tui/__tests__/input-intent.test.ts +0 -27
  60. package/src/tui/__tests__/stream-assembler.test.ts +0 -33
  61. package/src/tui/ai-stream.ts +0 -28
  62. package/src/tui/app.tsx +0 -210
  63. package/src/tui/commands.ts +0 -220
  64. package/src/tui/context/exit.tsx +0 -15
  65. package/src/tui/context/helper.tsx +0 -25
  66. package/src/tui/context/route.tsx +0 -24
  67. package/src/tui/context/theme.tsx +0 -471
  68. package/src/tui/input-intent.ts +0 -26
  69. package/src/tui/routes/home.tsx +0 -1060
  70. package/src/tui/routes/model.tsx +0 -210
  71. package/src/tui/routes/notifications.tsx +0 -87
  72. package/src/tui/routes/post.tsx +0 -102
  73. package/src/tui/routes/search.tsx +0 -105
  74. package/src/tui/routes/setup.tsx +0 -267
  75. package/src/tui/routes/trending.tsx +0 -107
  76. package/src/tui/stream-assembler.ts +0 -49
  77. package/src/util/__tests__/context.test.ts +0 -31
  78. package/src/util/__tests__/lazy.test.ts +0 -37
  79. package/src/util/context.ts +0 -23
  80. package/src/util/error.ts +0 -46
  81. package/src/util/lazy.ts +0 -18
  82. package/src/util/log.ts +0 -144
  83. package/tsconfig.json +0 -11
@@ -1,267 +0,0 @@
1
- import { createSignal } from "solid-js"
2
- import { useKeyboard } from "@opentui/solid"
3
- import { useTheme, THEME_NAMES, THEMES } from "../context/theme"
4
-
5
- // High-contrast colors that are visible on ANY terminal background
6
- const HC = {
7
- title: "#ff6600",
8
- text: "#888888",
9
- selected: "#00cc00",
10
- dim: "#999999",
11
- }
12
-
13
- function resolveThemeDef(name: string) {
14
- const fallback = THEMES.codeblog ?? Object.values(THEMES).find(Boolean)
15
- if (!fallback) {
16
- throw new Error("No themes available")
17
- }
18
- return THEMES[name] ?? fallback
19
- }
20
-
21
- export function ThemeSetup() {
22
- const theme = useTheme()
23
- const modes = ["dark", "light"] as const
24
- const [step, setStep] = createSignal<"mode" | "theme">("mode")
25
- const [modeIdx, setModeIdx] = createSignal(0)
26
- const [themeIdx, setThemeIdx] = createSignal(0)
27
- const getThemeName = (index: number) => THEME_NAMES[index] ?? "codeblog"
28
- const getThemeColors = (name: string) => resolveThemeDef(name)[theme.mode]
29
-
30
- useKeyboard((evt) => {
31
- if (step() === "mode") {
32
- if (evt.name === "up" || evt.name === "k") {
33
- setModeIdx((i) => (i - 1 + modes.length) % modes.length)
34
- evt.preventDefault()
35
- return
36
- }
37
- if (evt.name === "down" || evt.name === "j") {
38
- setModeIdx((i) => (i + 1) % modes.length)
39
- evt.preventDefault()
40
- return
41
- }
42
- if (evt.name === "return") {
43
- theme.setMode(modes[modeIdx()] ?? "dark")
44
- setStep("theme")
45
- evt.preventDefault()
46
- return
47
- }
48
- }
49
-
50
- if (step() === "theme") {
51
- if (evt.name === "up" || evt.name === "k") {
52
- const next = (themeIdx() - 1 + THEME_NAMES.length) % THEME_NAMES.length
53
- setThemeIdx(next)
54
- theme.set(getThemeName(next))
55
- evt.preventDefault()
56
- return
57
- }
58
- if (evt.name === "down" || evt.name === "j") {
59
- const next = (themeIdx() + 1) % THEME_NAMES.length
60
- setThemeIdx(next)
61
- theme.set(getThemeName(next))
62
- evt.preventDefault()
63
- return
64
- }
65
- if (evt.name === "return") {
66
- theme.finishSetup()
67
- evt.preventDefault()
68
- return
69
- }
70
- if (evt.name === "escape") {
71
- setStep("mode")
72
- evt.preventDefault()
73
- return
74
- }
75
- }
76
- })
77
-
78
- return (
79
- <box flexDirection="column" flexGrow={1} alignItems="center" paddingLeft={2} paddingRight={2}>
80
- <box flexGrow={1} minHeight={0} />
81
-
82
- <box flexShrink={0} flexDirection="column" alignItems="center">
83
- <text fg={HC.title}>
84
- <span style={{ bold: true }}>{"★ Welcome to CodeBlog ★"}</span>
85
- </text>
86
- <box height={1} />
87
- </box>
88
-
89
- {step() === "mode" ? (
90
- <box flexShrink={0} flexDirection="column" width="100%" maxWidth={50}>
91
- <text fg={HC.title}>
92
- <span style={{ bold: true }}>{"What is your terminal background color?"}</span>
93
- </text>
94
- <box height={1} />
95
- {modes.map((m, i) => (
96
- <box flexDirection="row" paddingLeft={2}>
97
- <text fg={modeIdx() === i ? HC.selected : HC.dim}>
98
- {modeIdx() === i ? "❯ " : " "}
99
- </text>
100
- <text fg={modeIdx() === i ? HC.selected : HC.dim}>
101
- <span style={{ bold: modeIdx() === i }}>
102
- {m === "dark" ? "Dark background (black/dark terminal)" : "Light background (white/light terminal)"}
103
- </span>
104
- </text>
105
- </box>
106
- ))}
107
- <box height={1} />
108
- <text fg={HC.text}>{"↑↓ select · Enter confirm"}</text>
109
- </box>
110
- ) : (
111
- <box flexShrink={0} flexDirection="column" width="100%" maxWidth={60}>
112
- <text fg={theme.colors.text}>
113
- <span style={{ bold: true }}>{"Choose a color theme:"}</span>
114
- </text>
115
- <box height={1} />
116
- {THEME_NAMES.map((name, i) => {
117
- const c = getThemeColors(name)
118
- return (
119
- <box flexDirection="row" paddingLeft={2}>
120
- <text fg={themeIdx() === i ? c.primary : theme.colors.textMuted}>
121
- {themeIdx() === i ? "❯ " : " "}
122
- </text>
123
- <text fg={themeIdx() === i ? c.text : theme.colors.textMuted}>
124
- <span style={{ bold: themeIdx() === i }}>
125
- {name.padEnd(14)}
126
- </span>
127
- </text>
128
- <text fg={c.logo1}>{" ●"}</text>
129
- <text fg={c.logo2}>{"●"}</text>
130
- <text fg={c.primary}>{"●"}</text>
131
- <text fg={c.accent}>{"●"}</text>
132
- <text fg={c.success}>{"●"}</text>
133
- <text fg={c.error}>{"●"}</text>
134
- </box>
135
- )
136
- })}
137
- <box height={1} />
138
- <text fg={theme.colors.textMuted}>{"↑↓ select · Enter confirm · Esc back"}</text>
139
- </box>
140
- )}
141
-
142
- <box flexGrow={1} minHeight={0} />
143
- </box>
144
- )
145
- }
146
-
147
- export function ThemePicker(props: { onDone: () => void }) {
148
- const theme = useTheme()
149
- const [idx, setIdx] = createSignal(Math.max(0, THEME_NAMES.indexOf(theme.name)))
150
- const [tab, setTab] = createSignal<"theme" | "mode">("theme")
151
- const getThemeName = (index: number) => THEME_NAMES[index] ?? "codeblog"
152
- const getThemeColors = (name: string) => resolveThemeDef(name)[theme.mode]
153
-
154
- useKeyboard((evt) => {
155
- if (tab() === "theme") {
156
- if (evt.name === "up" || evt.name === "k") {
157
- const next = (idx() - 1 + THEME_NAMES.length) % THEME_NAMES.length
158
- setIdx(next)
159
- theme.set(getThemeName(next))
160
- evt.preventDefault()
161
- return
162
- }
163
- if (evt.name === "down" || evt.name === "j") {
164
- const next = (idx() + 1) % THEME_NAMES.length
165
- setIdx(next)
166
- theme.set(getThemeName(next))
167
- evt.preventDefault()
168
- return
169
- }
170
- if (evt.name === "tab") {
171
- setTab("mode")
172
- evt.preventDefault()
173
- return
174
- }
175
- if (evt.name === "return" || evt.name === "escape") {
176
- props.onDone()
177
- evt.preventDefault()
178
- return
179
- }
180
- }
181
-
182
- if (tab() === "mode") {
183
- if (evt.name === "up" || evt.name === "down" || evt.name === "k" || evt.name === "j") {
184
- theme.setMode(theme.mode === "dark" ? "light" : "dark")
185
- evt.preventDefault()
186
- return
187
- }
188
- if (evt.name === "tab") {
189
- setTab("theme")
190
- evt.preventDefault()
191
- return
192
- }
193
- if (evt.name === "return" || evt.name === "escape") {
194
- props.onDone()
195
- evt.preventDefault()
196
- return
197
- }
198
- }
199
- })
200
-
201
- return (
202
- <box flexDirection="column" flexGrow={1} paddingLeft={2} paddingRight={2} paddingTop={1}>
203
- <box flexDirection="row" gap={4} flexShrink={0}>
204
- <text fg={theme.colors.primary}>
205
- <span style={{ bold: true }}>Theme Settings</span>
206
- </text>
207
- <box flexGrow={1} />
208
- <text fg={theme.colors.textMuted}>{"Tab: switch section · Enter/Esc: done"}</text>
209
- </box>
210
-
211
- <box flexDirection="row" flexGrow={1} paddingTop={1} gap={4}>
212
- {/* Theme list */}
213
- <box flexDirection="column" width={40}>
214
- <text fg={tab() === "theme" ? theme.colors.text : theme.colors.textMuted}>
215
- <span style={{ bold: true }}>{"Color Theme"}</span>
216
- </text>
217
- <box height={1} />
218
- {THEME_NAMES.map((name, i) => {
219
- const c = getThemeColors(name)
220
- return (
221
- <box flexDirection="row">
222
- <text fg={idx() === i ? c.primary : theme.colors.textMuted}>
223
- {idx() === i && tab() === "theme" ? "❯ " : " "}
224
- </text>
225
- <text fg={idx() === i ? c.text : theme.colors.textMuted}>
226
- <span style={{ bold: idx() === i }}>
227
- {name.padEnd(14)}
228
- </span>
229
- </text>
230
- <text fg={c.logo1}>{" ●"}</text>
231
- <text fg={c.logo2}>{"●"}</text>
232
- <text fg={c.primary}>{"●"}</text>
233
- <text fg={c.accent}>{"●"}</text>
234
- <text fg={c.success}>{"●"}</text>
235
- <text fg={c.error}>{"●"}</text>
236
- </box>
237
- )
238
- })}
239
- </box>
240
-
241
- {/* Mode toggle */}
242
- <box flexDirection="column" width={30}>
243
- <text fg={tab() === "mode" ? theme.colors.text : theme.colors.textMuted}>
244
- <span style={{ bold: true }}>{"Background Mode"}</span>
245
- </text>
246
- <box height={1} />
247
- <box flexDirection="row">
248
- <text fg={theme.mode === "dark" && tab() === "mode" ? theme.colors.primary : theme.colors.textMuted}>
249
- {theme.mode === "dark" && tab() === "mode" ? "❯ " : " "}
250
- </text>
251
- <text fg={theme.mode === "dark" ? theme.colors.text : theme.colors.textMuted}>
252
- <span style={{ bold: theme.mode === "dark" }}>{"Dark"}</span>
253
- </text>
254
- </box>
255
- <box flexDirection="row">
256
- <text fg={theme.mode === "light" && tab() === "mode" ? theme.colors.primary : theme.colors.textMuted}>
257
- {theme.mode === "light" && tab() === "mode" ? "❯ " : " "}
258
- </text>
259
- <text fg={theme.mode === "light" ? theme.colors.text : theme.colors.textMuted}>
260
- <span style={{ bold: theme.mode === "light" }}>{"Light"}</span>
261
- </text>
262
- </box>
263
- </box>
264
- </box>
265
- </box>
266
- )
267
- }
@@ -1,107 +0,0 @@
1
- import { createSignal, onMount, For, Show } from "solid-js"
2
- import { useKeyboard } from "@opentui/solid"
3
- import { useTheme } from "../context/theme"
4
- import { McpBridge } from "../../mcp/client"
5
-
6
- export function Trending() {
7
- const theme = useTheme()
8
- const [data, setData] = createSignal<any>(null)
9
- const [loading, setLoading] = createSignal(true)
10
- const [tab, setTab] = createSignal<"posts" | "tags" | "agents">("posts")
11
-
12
- onMount(async () => {
13
- try {
14
- const raw = await McpBridge.callToolJSON<any>("trending_topics", {})
15
- setData(raw.trending || raw)
16
- } catch {
17
- setData(null)
18
- }
19
- setLoading(false)
20
- })
21
-
22
- useKeyboard((evt) => {
23
- if (evt.name === "1") { setTab("posts"); evt.preventDefault() }
24
- if (evt.name === "2") { setTab("tags"); evt.preventDefault() }
25
- if (evt.name === "3") { setTab("agents"); evt.preventDefault() }
26
- })
27
-
28
- return (
29
- <box flexDirection="column" flexGrow={1}>
30
- <box paddingLeft={2} paddingRight={2} paddingTop={1} flexShrink={0} flexDirection="row" gap={1}>
31
- <text fg={theme.colors.accent}>
32
- <span style={{ bold: true }}>Trending</span>
33
- </text>
34
- <box flexGrow={1} />
35
- <text fg={theme.colors.textMuted}>esc:back</text>
36
- </box>
37
-
38
- <box paddingLeft={2} paddingTop={1} flexShrink={0} flexDirection="row" gap={2}>
39
- <text fg={tab() === "posts" ? theme.colors.primary : theme.colors.textMuted}>
40
- <span style={{ bold: tab() === "posts" }}>[1] Posts</span>
41
- </text>
42
- <text fg={tab() === "tags" ? theme.colors.primary : theme.colors.textMuted}>
43
- <span style={{ bold: tab() === "tags" }}>[2] Tags</span>
44
- </text>
45
- <text fg={tab() === "agents" ? theme.colors.primary : theme.colors.textMuted}>
46
- <span style={{ bold: tab() === "agents" }}>[3] Agents</span>
47
- </text>
48
- </box>
49
-
50
- <Show when={loading()}>
51
- <box paddingLeft={4} paddingTop={1}>
52
- <text fg={theme.colors.textMuted}>Loading trending...</text>
53
- </box>
54
- </Show>
55
-
56
- <Show when={!loading() && data()}>
57
- <Show when={tab() === "posts"}>
58
- <box flexDirection="column" paddingTop={1}>
59
- <For each={data()?.top_upvoted || data()?.posts || []}>
60
- {(post: any) => (
61
- <box flexDirection="row" paddingLeft={2} paddingRight={2}>
62
- <box width={6} justifyContent="flex-end" marginRight={1}>
63
- <text fg={theme.colors.success}>{`▲${post.score ?? post.upvotes ?? 0}`}</text>
64
- </box>
65
- <box flexDirection="column" flexGrow={1}>
66
- <text fg={theme.colors.text}>
67
- <span style={{ bold: true }}>{post.title}</span>
68
- </text>
69
- <text fg={theme.colors.textMuted}>{`👁${post.views ?? 0} 💬${post.comments ?? post.comment_count ?? 0} by ${post.agent ?? "anon"}`}</text>
70
- </box>
71
- </box>
72
- )}
73
- </For>
74
- </box>
75
- </Show>
76
-
77
- <Show when={tab() === "tags"}>
78
- <box flexDirection="column" paddingTop={1} paddingLeft={2}>
79
- <For each={data()?.trending_tags || data()?.tags || []}>
80
- {(tag: any) => (
81
- <box flexDirection="row" gap={2}>
82
- <text fg={theme.colors.primary}>{`#${tag.tag || tag.name || tag}`}</text>
83
- <text fg={theme.colors.textMuted}>{`${tag.count ?? ""} posts`}</text>
84
- </box>
85
- )}
86
- </For>
87
- </box>
88
- </Show>
89
-
90
- <Show when={tab() === "agents"}>
91
- <box flexDirection="column" paddingTop={1} paddingLeft={2}>
92
- <For each={data()?.top_agents || data()?.agents || []}>
93
- {(agent: any) => (
94
- <box flexDirection="row" gap={2}>
95
- <text fg={theme.colors.primary}>
96
- <span style={{ bold: true }}>{agent.name || agent.username || agent}</span>
97
- </text>
98
- <text fg={theme.colors.textMuted}>{`${agent.posts ?? agent.post_count ?? ""} posts`}</text>
99
- </box>
100
- )}
101
- </For>
102
- </box>
103
- </Show>
104
- </Show>
105
- </box>
106
- )
107
- }
@@ -1,49 +0,0 @@
1
- export class TuiStreamAssembler {
2
- private text = ""
3
- private finished = false
4
- private lastSeq = 0
5
-
6
- reset() {
7
- this.text = ""
8
- this.finished = false
9
- this.lastSeq = 0
10
- }
11
-
12
- getText() {
13
- return this.text
14
- }
15
-
16
- pushDelta(delta: string, seq?: number) {
17
- if (!delta || this.finished) return this.text
18
- if (seq !== undefined && seq <= this.lastSeq) return this.text
19
- if (seq !== undefined) this.lastSeq = seq
20
-
21
- this.text += delta
22
- return this.text
23
- }
24
-
25
- pushFinal(finalText: string) {
26
- if (!finalText) {
27
- this.finished = true
28
- return this.text
29
- }
30
-
31
- if (!this.text) {
32
- this.text = finalText
33
- this.finished = true
34
- return this.text
35
- }
36
-
37
- if (finalText.length >= this.text.length && finalText.includes(this.text)) {
38
- this.text = finalText
39
- this.finished = true
40
- return this.text
41
- }
42
-
43
- if (finalText.length > this.text.length) {
44
- this.text = finalText
45
- }
46
- this.finished = true
47
- return this.text
48
- }
49
- }
@@ -1,31 +0,0 @@
1
- import { describe, test, expect } from "bun:test"
2
- import { Context } from "../context"
3
-
4
- describe("Context", () => {
5
- test("create and provide context", () => {
6
- const ctx = Context.create<string>("test")
7
-
8
- ctx.provide("hello", () => {
9
- expect(ctx.use()).toBe("hello")
10
- })
11
- })
12
-
13
- test("throws outside provider", () => {
14
- const ctx = Context.create<number>("num")
15
- expect(() => ctx.use()).toThrow(Context.NotFound)
16
- })
17
-
18
- test("nested contexts work correctly", () => {
19
- const ctx = Context.create<string>("nested")
20
-
21
- ctx.provide("outer", () => {
22
- expect(ctx.use()).toBe("outer")
23
-
24
- ctx.provide("inner", () => {
25
- expect(ctx.use()).toBe("inner")
26
- })
27
-
28
- expect(ctx.use()).toBe("outer")
29
- })
30
- })
31
- })
@@ -1,37 +0,0 @@
1
- import { describe, test, expect } from "bun:test"
2
- import { lazy } from "../lazy"
3
-
4
- describe("lazy", () => {
5
- test("initializes value on first call", () => {
6
- let count = 0
7
- const val = lazy(() => {
8
- count++
9
- return 42
10
- })
11
- expect(val()).toBe(42)
12
- expect(count).toBe(1)
13
- })
14
-
15
- test("caches value on subsequent calls", () => {
16
- let count = 0
17
- const val = lazy(() => {
18
- count++
19
- return "hello"
20
- })
21
- val()
22
- val()
23
- val()
24
- expect(count).toBe(1)
25
- })
26
-
27
- test("reset clears cached value", () => {
28
- let count = 0
29
- const val = lazy(() => {
30
- count++
31
- return count
32
- })
33
- expect(val()).toBe(1)
34
- val.reset()
35
- expect(val()).toBe(2)
36
- })
37
- })
@@ -1,23 +0,0 @@
1
- import { AsyncLocalStorage } from "async_hooks"
2
-
3
- export namespace Context {
4
- export class NotFound extends Error {
5
- constructor(public override readonly name: string) {
6
- super(`No context found for ${name}`)
7
- }
8
- }
9
-
10
- export function create<T>(name: string) {
11
- const storage = new AsyncLocalStorage<T>()
12
- return {
13
- use() {
14
- const result = storage.getStore()
15
- if (!result) throw new NotFound(name)
16
- return result
17
- },
18
- provide<R>(value: T, fn: () => R) {
19
- return storage.run(value, fn)
20
- },
21
- }
22
- }
23
- }
package/src/util/error.ts DELETED
@@ -1,46 +0,0 @@
1
- import z from "zod"
2
-
3
- export abstract class NamedError extends Error {
4
- abstract schema(): z.core.$ZodType
5
- abstract toObject(): { name: string; data: any }
6
-
7
- static create<Name extends string, Data extends z.core.$ZodType>(name: Name, data: Data) {
8
- const schema = z
9
- .object({
10
- name: z.literal(name),
11
- data,
12
- })
13
- .meta({ ref: name })
14
- const result = class extends NamedError {
15
- public static readonly Schema = schema
16
- public override readonly name = name as Name
17
-
18
- constructor(
19
- public readonly data: z.input<Data>,
20
- options?: ErrorOptions,
21
- ) {
22
- super(name, options)
23
- this.name = name
24
- }
25
-
26
- static isInstance(input: any): input is InstanceType<typeof result> {
27
- return typeof input === "object" && "name" in input && input.name === name
28
- }
29
-
30
- schema() {
31
- return schema
32
- }
33
-
34
- toObject() {
35
- return { name, data: this.data }
36
- }
37
- }
38
- Object.defineProperty(result, "name", { value: name })
39
- return result
40
- }
41
-
42
- public static readonly Unknown = NamedError.create(
43
- "UnknownError",
44
- z.object({ message: z.string() }),
45
- )
46
- }
package/src/util/lazy.ts DELETED
@@ -1,18 +0,0 @@
1
- export function lazy<T>(fn: () => T) {
2
- let value: T | undefined
3
- let loaded = false
4
-
5
- const result = (): T => {
6
- if (loaded) return value as T
7
- value = fn()
8
- loaded = true
9
- return value as T
10
- }
11
-
12
- result.reset = () => {
13
- loaded = false
14
- value = undefined
15
- }
16
-
17
- return result
18
- }